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
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()"
}
Go to the exploit server and add this
iframe
to the body field:
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.