Why postMessage?Using postMessage has several benefits. The most obvious, and intended benefit, is the ability to communicate between documents of different origins. This communication does not have to touch the network. This allows us to write a minimal payload which can retrieve a more complex payload from the other document - the server will never see it. Another benefit is the ability to exfiltrate entire pages, which lets us parse data offsite or use the data how ever we wish. In the typical exploitation approach, we would have to include all the code to retrieve and parse data in our payload. In short, we can use postMessage to construct a very small and discreet payload which allows us to do much more.
The ScenariosBecause postMessage relies on communication between multiple documents, we have a few scenarios we can work with. In the first scenario, we write an iframe to the victim and start our communication from there. In the second scenario, this is reversed - we iframe the victim from a page we control. Although these scenarios are similar, there are a few nuances which, depending on the situation, can make one better than the other. There is one final scenario, which involves opening a new window to the victim page. It is worth noting that the location of the iframe does not matter, as a full two-way communication will always be possible. For these examples, I will be using a basic reflected XSS vulnerability.
Scenario One: Place an iframe to your page on the victim
A payload for this might look like this:
This payload behaves similarly to a remote shell in that we can get the victim to execute code passed to it over a hidden channel. To receive data, we would have to set up an event listener on attacker.com, and listen for messages from window.top, which will be the victim page.
Here is another payload, but this time without the ability to execute commands dynamically: What this does is send the contents of the current page to the attacker frame. An id needs to be specified, so we know where to send the data. We can retrieve other pages using XHR as well - I used this as an example in the next scenario.
Bypasses any limitations specified by X-Frame-Options.
Attacker url becomes visible to victim.
Scenario Two: Iframe the victim on your website
A payload for this might look like this: (with this url in an iframe, of course)
What this would do is send the entire current page up to whoever is iframing this page. This is a small payload which allows us to grab as much data as possible. We don't even have to be limited to the current page - we can be more creative and use XMLHttpRequest to retrieve even more pages for us, and send the contents of those as well. Here is a simple payload which achieves this:
Now we can retrieve as many pages as we want and read all the data.
- We can be a bit stealthy with our payload.
- Setting the targetOrigin as '*' gives no information as to where postMessage will be sending the data, or what is being done with it.
- Normally, the referer would leak to the iframe, and our url will be found out. We can get around this - the HTTP spec states that the referer must be removed from any request to an HTTP url from an HTTPS url. By using HTTPS on the framing page, and HTTP on the victim page (or the other way around), we will have removed the referer.
X-Frame-Options completely kills our ability to iframe the victim and any attempts to communicate with it.
Scenario Three: Open a new window to the victim page
With postMessage, we are not limited to just iframes. Opening new windows is a completely legitimate method for communication using postMessage. An example is the following:
Attacker's Page: Here, we open a page to the vulnerable page, have them eval() code that sends us the current page.
- No iframes. (matter of personal preference)
- Can be made stealthy in the same ways as scenario two.
- Bypasses X-Frame-Options limitations.
Popups gather a lot of attention, and are easily foiled by popup blockers.
More Payload ExamplesSome of these examples are assuming you are using an iframe. If you aren't, simply substitute "window.top" with your other document.
postMessage a relative path, and this payload will reply with the contents of that page: Basic example, sends you the victim's cookie:
ThoughtsOver all, postMessage makes exfiltration an easy task, and can be used to hide our actions and intentions in a way that typical payloads do not allow for. There is also a lot of room to be creative when it comes to using postMessage; the payloads I have used as examples are by no means an exhaustive list. Most of the code I used here can be compressed and simplified a bit, since this was not my main goal.
Although out of the scope of this post, postMessage can be used similarly to do much more than exfiltrate data (bypassing CSRF protections comes to mind).