In questo post devo necessariamente fare una carrellata delle principali vulnerabilita’ del mondo web. Si tratta di temi che tutti avrete sentito decine di volte, cerchero’ do trattarli restando sul pratico e mescolando con un po’ di attivita’ di bug bounty.
Esistono due grandi famiglie di XSS: stored e reflected. Con stored ci si riferisce al fatto che il payload dell’attacco viene posizionato su una delle componenti dell’applicazione target come un Database o un sistema di cache. In questo caso gli effetti del payload possono essere visibili a tutti gli utenti che dovessero accedere alla pagina vulnerabile. Con reflected ci si riferisce al fatto che il payload e’ incluso in una richiesta o in un link. L’applicazione vulnerabile utilizza il payload all’interno della pagina con relativa modifica del contenuto. In questo caso gli effetti del payload sono visibili sono all’utente che ha accesso al link della pagina target dell’attacco.
In entrambi i casi cio’ che accade e’ che viene manipolato l’output che viene presentato all’utente vittima e questo e’ possibile essenzialmente grazie all’iniezione di porzioni di codice HTML o, piu’ frequentemente, JavaScript.
Di documentazione su JavaScript ne esiste un’infinita’, quindi in questo post mi limito a ricordare che si tratta di un linguaggio di scripting che consente di gestire cio’ che viene visualizzato dal browser dell’utente e che i browser piu’ usati presentano degli strumenti molto comodi per vederlo all’opera. In particolare e’ molto utile la console:
E’ ovviamente possibile scrivere degli script da portare direttamente sul browser, ma lavorare sulla console anche per eseguire piccole porzioni di script fa risparmiare parecchio tempo. L’analisi del codice lato client e’ uno degli elementi fondamentali in quanto e’ proprio questa parte dell’applicazione che puo’ darci utili spunti per comprendere il funzionamento generale del sistemi, semplicemente intercettando gli elementi di interazione. In tal senso la console ci puo’ aiutare a verificare e provare alcune interazioni direttamente dal browser.
Di base il funzionamento e’ molto semplice: in corrispondenza di un input field puo’ essere verificato che vengano accettati, senza essere “sanitizzati” i caratteri utilizzati in HTML per definire un elemento ( <elemento> ) ed i caratteri usati in JS per dichiarare una funzione ( { … } ), una stringa ( ‘ e “ ) e chiudere un’istruzione ( ; ). Se per qualche ragione l’applicazione accetta questi caratteri in input il browser potrebbe poi trovarsi ad interpretare una porzione di codice inserita dall’utente come se fosse parte effettiva dell’applicazione.
Un grande classico, in assenza di qualsiasi controllo, e’ inviare una porzione di codice JS che ci consenta di avere un riscontro immediato dell’azione:
<script>alert(1)</script>
Per sperimentare gli effetti ci sono diversi lab online per esercitare la tecnica, oltre ai piu’ noti vi segnalo quelli di root-me.org e quelli dell’accademy di PortSwigger e proprio su quest’ultima riporto qui l’esempio piu’ semplice che viene solitamente preso in esame.
Al link https://portswigger.net/web-security/cross-site-scripting vengono descritte le diverse tipologie di XSS e la prima e’ la gia’ citata Reflected XSS con relativo lab si esempio.
Come discusso il contesto presenta un campo di ricerca in cui possiamo inserire del testo da cercare tra i contenuti del blog. Il primo step e’ analizzare il comportamento dell’applicazione eseguendo una richiesta ed analizzando, con uno strumento come BurpSuite, lo scanbio di informazioni e la risposta dell’applicazione. Se cerchiamo, ad esempio, la stringa “test” la richiesta che invieremo al server sara’ la seguente:
Il linea 1 e’ ben visibile la GET per il parametro search con valore test. Alla richiesta l’applicazione risponde con i risultati e con un messaggio di testo:
… di cui possiamo analizzare il codice per appurare che, in qualche modo, il testo inserito nel campo di ricerca viene utilizzato per comporre parte del messaggio. L’errore classico, come accennato ad inizio post’, e’ quello di non preoccuparsi del controllo di quanto portato in input con il rischio di accettare anche frammenti di codice JS. Per verificare se questo e’ il caso possiamo inserire un semplice comando nel campo di ricerca – come alert(1) – e vedere cosa succede.
A conferma della presenza della vulnerabilita’ verra’ eseguito il comando alert() e nel codice della pagina trovaremo il comportamento atteso:
Per individuare questo tipo di vuonerabilita’ e’ quindi necessario eseguire dei test sull’applicazione utilizzando e manipolando gli input che il sistema e’ in grado di gestire a caccia di qualcosa che accetti tag o comandi senza una corretta procedura di sanitization.
E’ da considerare, nel contesto dei siti web come per altri contesti come i web services o le API, che i campi <input> non sono l’unico modo per inviare dati ad un’applicazione: il codice potrebbe considerare altre informazioni in arrivo del cliente come l’IP, il Referer, lo User Agent. Questi dati posso essere utilizzati dall’applicazione per registrare informazioni a livello di log e tracciamento o per modificare il comportamento della stessa. Non e’ raro che lo User Agent condizioni cio’ che l’applicazione rispondera’ per adattare l’output al tipo di dispositivo che l’utente sta utilizzando. Anche queste informazioni sono in parte manipolabili lato client, potrebbero quindi concretizzarsi le consizioni che portano ad una vuln. XSS reflected o stored, dipendera’ dal comportamento dell’applicazione.
Per comprendere meglio in tema il materiali ufficiale dei famosi corsi di certificazione propone esempi su frammenti di codice o su emblematiche vulnerabilita’ delle plugin di WordPress. Per l’occasione ho scritto una porzione di codice con cui giocare.
Per utilizzare lo script e’ sufficiente una qualsiasi macchina con un web server come Apache e una versione di PHP. Nel mio caso ho predisposto il tutto sulla mia Debian di laboratorio ed ho posizionato il file nella Document Root a cui accedo con il browser di BurpSuite. Il risultato:
Dal codice si vede chiaramente che la variabile $user_agent non viene minimamente elaborata a livello di script, come arriva dal client cosi’ viene stampata. Possiamo quindi teorizzare che lo script accettara’ e stampera’ qualsiasi contenuto decideremo di inviargli tramite la variabile $_SERVER[‘HTTP_USER_AGENT’].
Questi post sono per me un’occasione di approfondimento, analizziamo quindi a fondo questa variabile di PHP. Partiamo dalla documentazione di PHP sulle variabili riservate tra cui c’e’, come chi legge avra’ intuito, la variabile $_SERVER (link di riferimento per la specifica variabile riservata: https://www.php.net/manual/en/reserved.variables.server). La documentazione e’ abbastanza chiara: “$_SERVER is an array containing information such as headers, paths, and script locations“.
Se chiediamo allo script di stampare l’intero contenuto dell’array $_SERVER otterremo un elenco di dati tra cui il gia’ utilizzato HTTP_USER_AGENT e HTTP_ACCEPT che si riferisce ai tipi di media (come HTML, XML, JSON, immagini, ecc.) che il client accetta come risposta dal server.
Come si nota dalla richiesta HTTP intercettata da BurpSuite anche questa porzione dell’header e’ manipolabile all’origine. Si tratta di elementi interessanti in quanto non e’ raro che le applicazioni utilizzino queste informazioni per il tracciamento degli utenti o delle configurazioni dei client: spesso a livello di log vengono salvate sul DB le informazioni relative al browser dell’utente lasciando quindi spazio a stored XSS, ovviamente sempre nel caso non vengano correttamente gestiti gli input.
Per utilizzare in qualche misura le falla va quindi creata una porzione di codice che, se sostituita alla variabile che possiamo governare, abbia un senso nel contesto in cui e’ inserita ed esegua un comando a nostra discrezione. Uno degli esempi che ho riportato sulla repo GitHub presenta una pozione di codice HTML modificato da una delle variabili in input dello script:
<a href="$page">
La variabile $page viene valorizzata tramite una GET e non vengono eseguiti controlli in merito al contenuto. Possiamo quindi manipolare il codice HTML della pagine impostando a piacimento il contenuto di $page:
<a href="" onmouseover="alert(1)" target="_blank">
La porzione di codice evidenziata viene quindi passata come parametro allo script che la andra’ a sostituire a $page generando un nuovo comportamento nella pagine: l’apertura di un alert pilotata dall’utente.
Una comoda risorsa per diverse tipologie di payloads e’ la seguente: https://github.com/payloadbox/xss-payload-list
Il formato SVG vieme spesso utilizzato per specifiche tipologie di immagini vettoriali ed e’ di fatto un file XML che i browser sono in grado di interpretare visualizzando l’immagine descritta nel codice. Molte applicazioni web accettano il formato SVG come un formato immagine ma, essendo in realta’ composto da codice che puo’ essere arbitrariamente manipolato dall’utente che ha la possibilita’ di eseguire l’upload, sono da prevedere dei controlli specifici sul file cosi’ da evitare eventuali tentativi di compromissione. In particolare quando si parla di ferifica dei formati in upload si fa riferimento all’estensioni ed al mime-type, nel caso specifico non è sufficiente accertarsi che il mime-type sia corretto, va anche verificato e “pulito” il contenuto che, banalmente, potrebbe contenere una porzione di codice javascript.