Le web application utilizzano massivamente i database per immagazzinare i dati necessari al loro funzionamento. Da molti anni i database relazionali sono la tecnologia preferita per la gestione dei dati, ma ultimamente hanno cominciato a farsi avanti anche soluzioni organizzate in strutture XML. Se i DB relazionali venivano interrogati utilizzando il linguaggio SQL, i database XML utilizzano XPath per effettuare query.
Così come esistono vulnerabilità nel linguaggio SQL, ve ne sono alcune anche nell'Xpath.
In particolare, l'XPath injection ci consente di iniettare sintassi Xpath all'interno della richiesta che l'applicazione deve
interpretare, permettendoci così di eseguire query ad-hoc per, ad esempio, bypassare meccanismi di autentificazione o accedere ad informazioni che non
dovrebbero essere leggibili.
La struttura di un database XML
Per capire come funziona una query XPath, immaginiamo un database che contenga dati di autenticazione (username, password e livello di accesso):
<?xml version="1.0" encoding="ISO-8859-1"?>
<auth>
<user>
<username>admin</username>
<password>LaMiaPassword123</password>
<account>admin</account>
</user>
<user>
<username>ospite1</username>
<password>Passosp1t31</password>
<account>guest</account>
</user>
<user>
<username>ospite2</username>
<password>Passosp1r32</password>
<account>guest</account>
</user>
</auth>
Specifichiamo più in dettaglio cosa rappresentano i tag utilizzati:
-
<auth>
: è l'elemento radice, e possiamo considerarlo il nome che vogliamo dare al nostro database -
<user>
: definisce la prima separazione degli elementi dello stesso insieme; in un database relazionale corrisponderebbe al nome della tabella -
<username>
,<password>
e<account>
: definiscono i vari record relativi ad ogni<user>
Possiamo inoltre supporre che l'utente può accedere al database tramite un qualche front-end, programmato in un qualsiasi linguaggio web-compatibile. Tramite questo strumento, sarà possibile inserire dati, verificarli e garantire l'accesso al database secondo gli opportuni permessi.
La nostra prima XPath injection
Al momento della verifica delle credenziali d'accesso, verrà generata una stringa simile alla seguente:
[code]
//user[username/text()='USERNAME' and password/text()='PASSWORD']/account/text()
[/code]
Qui, l’input dell’utente è rappresentato dalle stringhe USERNAME e PASSWORD. Se l'applicazione che interpreta il codice XPath non le
“filtra” entrambe in maniera opportuna (cioè, in questo caso, non rimuove gli apici), essa è esposta ad una vulnerabilità di tipo Xpath injection.
Per prima cosa dobbiamo verificare la fattibilità dell'injection, e possiamo farlo immettendo un singolo apice. Così facendo, se il server
ritornerà un errore di sintassi, potremo essere abbastanza sicure del fatto che l'applicazione è vulnerabile ad Xpath injection.
Vediamo ora come possiamo sfruttarla. Supponiamo che le stringhe precedenti (USERNAME e PASSWORD) siano valorizzate come segue:
Username | ' or 'a'='a |
---|---|
Password | ' or 'a'='a |
Con questi valori, la query che risulterà sarà la seguente:
[code]
//user[username/text()='' or 'a'='a' and password/text()='' or 'a'='a']/account/text()
[/code]
La condizione presente in questo tipo di query ritornerà sempre un risultato positivo (TRUE), garantendo di fatto l'accesso con il primo account all'interno del database, senza che
l'utente abbia inserito credenziali valide.
Se non conosciamo nulla sulla struttura interna del database XML o l'applicazione non fornisce nessun tipo di messaggio d'errore, possiamo utilizzare
un attacco di tipo Blind Xpath injection.
Blind Xpath injection
A seconda dei dati che stiamo cercando di ottenere è possibile sfruttare funzioni interne al linguaggio XPath, al fine di scoprire maggiori informazioni sulla struttura del database XML.
stringlenght()
Con questa funzione possiamo estrarre la lunghezza di una stringa qualsiasi all'interno del database, come mostra l'esempio:
[code]
stringlenght(//user[position()=1]/child::node()[position()=2])
[/code]
In questo caso otterremo la lunghezza della seconda stringa del primo utente.
Possiamo anche verificare direttamente se la lunghezza è uguale ad un determinato valore:
[code]
stringlength(//user[position()=1]/child::node()[position()=2]) = 6
[/code]
Il risultato sarà di tipo TRUE o FALSE.
substring()
Possiamo estrarre il valore contenuto all'interno di un campo del database come da esempio:
[code]
substring((//user[position()=1]/child::node()[position()=2]),1,1)
[/code]
Otteniamo così il primo carattere dell'username.
Inoltre, è possible controllare se la stringa di output è uguale ad un determinato valore:
[code]
substring(//user[position()=1]/child::node()[position()=2]),1,1) = "a"
[/code]
Anche in questo caso il risultato è un valore booleano.
count()
Questa funzione è quella che consente di ottenere il numero di nodi all'interno della struttura.
[code]
count(//user/child::node())
[/code]
Anche qui possiamo verificare ciò manualmente, specificando un valore ed ottenendo ancora TRUE o FALSE:
[code]
count(//user/child::node()) = 1
[/code]
Con queste tre semplici funzioni siamo già in grado di estrapolare i dati contenuti all'interno del database. Ovviamente il lavoro di blind
injection è molto lungo se esguito manualmente, ma strumenti come l'Intruder di BurpSuite consentono di ottenere buoni risultati molto più velocemente.
Prevenzione
Le vulnerabilità di tipo Xpath injection si possono evitare in modo analogo a come si previene una SQL injection:
- trattare tutti gli input provenienti dall'utente come non sicuri ed applicate opportuni filtri su di essi;
-
quando si applica un filtro all'input dell'utente, verificare la correttezza del tipo di dato, la lunghezza, il formato ed il contenuto. Per esempio, si può usare
un'espressione regolare per controllare la presenza di tag XML e caratteri speciali; - in un'applicazione client-server, effettuare i controlli sia sul lato client, sia sul lato server;
- eseguire sempre opportuni test sull'applicazione prima della fase di produzione.
Un'altra buona tecnica di prevenzione per correggere le vulnerabilità di injection è la parametrizzazione.
In generale, comunque, per garantire un buon livello di sicurezza è bene assicurarsi di filtrare i seguenti caratteri:
[code]
< > / ' = " * ? : ( )
[/code]
Conclusioni
Abbiamo visto come verificare, sfruttare ed infine correggere una vulnerabilità di tipo Xpath injection. La tecniche per estrapolare i dati da un database
sono moltissime, e la bravura dell'attaccante sta nel poter sfruttare il maggior numero di funzioni durante l'attacco. Anche la conoscenza della
struttura del database aiuta, e spesso il nome dei nodi è associato alla funzione che essi hanno nel database.
Ovviamente, per acquisire maggiore dimestichezza con questo genere di attacco, l'unico modo per migliorare è "sporcarsi le mani"; un modo legale per farlo è usare WebGoat.