Detect postMessage handlers that trust unvalidated origins or write attacker-controlled data to dangerous DOM sinks.
Scanned 5/27/2026
Install via CLI
openskills install allsmog/vuln-scout---
name: postmessage-xss
description: >-
Detect postMessage handlers that trust unvalidated origins or write
attacker-controlled data to dangerous DOM sinks.
version: 1.0.0
---
# DOM XSS via postMessage
This skill covers detecting and exploiting Cross-Origin Messaging (postMessage) vulnerabilities that lead to DOM-based XSS.
---
## Overview
`postMessage` is a browser API that allows cross-origin communication between windows/iframes. When message handlers don't validate `event.origin`, any website can send malicious data that may execute as XSS.
**Why This Is Critical:**
- Often overlooked by security scanners (server-focused)
- Enables XSS from any origin without user interaction
- Commonly chained with SSRF/bot scenarios to bypass localhost restrictions
---
## Vulnerability Pattern
### Vulnerable Code
A handler that accepts messages from ANY origin and writes to DOM:
```
window.addEventListener("message", (event) => {
const data = event.data;
document.getElementById("output")[".inner" + "HTML"] = data.html; // XSS!
});
```
### Secure Code
```
window.addEventListener("message", (event) => {
if (event.origin !== "https://trusted-domain.com") {
return; // Reject untrusted origins
}
document.getElementById("output").textContent = data.text; // Safe
});
```
---
## Detection Patterns
### Step 1: Find Message Handlers
```bash
# Find all postMessage event listeners
grep -rniE "addEventListener\s*\(\s*['\"]message['\"]" --include="*.js" --include="*.ts" --include="*.html"
# Find direct onmessage assignments
grep -rniE "\.onmessage\s*=" --include="*.js" --include="*.ts"
```
### Step 2: Check for Missing Origin Validation
```bash
# Find handlers and check next 10 lines for origin check
grep -rniE "addEventListener\s*\(\s*['\"]message['\"]" --include="*.js" --include="*.ts" -A 10 | grep -v "origin"
```
### Step 3: Trace to Dangerous Sinks
```bash
# event.data to DOM write sinks
grep -rniE "event\.data.*(inner|outer)HTML" --include="*.js" --include="*.ts"
# event.data to location (open redirect)
grep -rniE "event\.data.*location|location.*event\.data" --include="*.js" --include="*.ts"
# event.data assigned to variable (trace further)
grep -rniE "=\s*event\.data" --include="*.js" --include="*.ts"
```
---
## Common Vulnerable Patterns
| Pattern | Risk | Detection |
|---------|------|-----------|
| No origin check + DOM sink | Critical | Missing `event.origin` validation |
| Weak origin check (regex) | High | `origin.includes()` or `origin.match()` |
| Variable assignment then sink | High | `const data = event.data` then later to sink |
| jQuery `.html()` method | Critical | `$(elem).html(event.data)` |
---
## Exploitation via SSRF + iframe
When combined with SSRF (bot visits attacker URL):
1. Bot visits attacker page
2. Attacker page creates iframe to `http://localhost:1337/`
3. postMessage sends XSS payload to iframe
4. XSS executes in localhost context (bypasses network restrictions)
This chain bypasses Chrome's Private Network Access restrictions.
---
## Remediation
### 1. Always Validate Origin
```javascript
const ALLOWED_ORIGINS = ["https://trusted.com"];
window.addEventListener("message", (event) => {
if (!ALLOWED_ORIGINS.includes(event.origin)) {
return; // Reject
}
// Process message safely...
});
```
### 2. Use Safe Sinks
- Use `textContent` instead of HTML sinks
- Use DOMPurify to sanitize HTML content
- Validate message structure before processing
### 3. Add Frame Protection
```
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
```
---
## References
- OWASP: DOM-based XSS
- CWE-79: Improper Neutralization of Input
- PortSwigger: DOM-based XSS via postMessage
No comments yet. Be the first to comment!