DOM XSS using web messages and JSON.parse

Description

This lab uses web messaging and parses the message as JSON.

Reproduction and proof of concept

  1. Analysis:

<script>
    window.addEventListener('message', function(e) {
        var iframe = document.createElement('iframe'), ACMEplayer = {element: iframe}, d;
        document.body.appendChild(iframe);
        try {
            d = JSON.parse(e.data);
        } catch(e) {
            return;
        }
        switch(d.type) {
            case "page-load":
                ACMEplayer.element.scrollIntoView();
                break;
            case "load-channel":
                ACMEplayer.element.src = d.url;
                break;
            case "player-height-changed":
                ACMEplayer.element.style.width = d.width + "px";
                ACMEplayer.element.style.height = d.height + "px";
                break;
        }
    }, false);
</script>

The home page contains an event listener that listens for a web message. This event listener expects a string that is parsed using JSON.parse(). In the JavaScript, the event listener expects a type property and that the load-channel case of the switch statement changes the iframe src attribute.

IOW, when a message is received, the script creates an iframe and appends it to the current page. The message is then parsed as JSON and, depending on the message content, an action may be performed. A possible actions is loading an url contained in the message within the iframe.

See the mozilla documentation for iframe: In the lab, no checks are done on the content of the message, so it is possible to inject a JavaScript url:

{
    "type": "load-channel", 
    "url": "javascript:print()"
}
  1. Go to the exploit server and add this iframe to the body field:

DOM-based

  1. Store the exploit and deliver it to the victim.

When the iframe we constructed loads, the postMessage() method sends a web message to the home page with the type load-channel. The event listener receives the message and parses it using JSON.parse() before sending it to the switch.

The switch triggers the load-channel case, which assigns the url property of the message to the src attribute of the ACMEplayer.element iframe. However, in this case, the url property of the message actually contains our JavaScript payload.

As the second argument specifies that any targetOrigin is allowed for the web message, and the event handler does not contain any form of origin check, the payload is set as the src of the ACMEplayer.element iframe. The print() function is called when the victim loads the page in their browser.

Exploitability

An attacker needs to construct an HTML page on the exploit server that exploits this vulnerability and calls the print() function.