Security

SQLi exploiting with overflow handling

Blind Boolean Based SQLi

Riccardo Krauter Ottobre, 2020
SQLi exploiting with overflow handling
In questo articolo vi raccontiamo come sia possibile sfruttare una vulnerabilità di SQL injection anche in situazioni molto complicate, dove magari l'unione di firewall e controlli sull'applicazione web non permettono di avere nemmeno una blind boolean based per poter fare inferenza.
 

Scenario

Per questo caso di studio abbiamo ricreato una situazione dove l'applicazione web, per costruzione, non consente di avere un output diretto dei risultati della query al database, eliminando quindi la possibilità di sfruttare una SQL injection di tipo union based. Inoltre, abbiamo un firewall che blocca i payload che potrebbero aiutare un attaccante a utilizzare tecniche basate sul tempo (time based).
Diamo un'occhiata da vicino ai sorgenti che compongono la nostra applicazione vulnerabile d'esempio. L'applicazione è molto semplice, abbiamo un primo file db.php che serve solo per creare l'oggetto (PDO) che utilizzeremo per interfacciarci con il database. Di seguito la porzione di codice sorgente di nostro interesse:
 
File "db.php"

Poi abbiamo una pagina index.php che invece è vulnerabile a SQL injection. Di seguito il codice sorgente che ci interessa:
 
File "index.php"

Come è possibile notare dal file db.php, stiamo usando il database MySQL.
Abbiamo poi una pagina logs.php che si occupa di popolare il database ma che per lo scopo di questo articolo possimo tranquillamente ignorare.
Infine, diamo uno sguardo alla struttura del database in uso che consiste in una sola tabella di nome logs.
 
Tabella "logs"
 

Analisi dei sorgenti

Alla riga 7 della pagina index.php possiamo notare che viene settata la variabile $user_agent con il valore dell'header "User-agent" presente nella richiesta HTTP.
In seguito, alle righe 11, 12 e 13, viene creata ed eseguita la query al database.
La query viene creata concatenando la stringa SELECT * FROM logs WHERE ua_signature=' con la variabile $user_agent. Dal momento che la variabile $user_agent è controllabile dall'utente e che non viene effettuato nessun tipo di sanificazione dell'input, l'applicazione risulta vulnerabile a SQL injection.
Come è possibile notare alla riga 8 della pagina index.php, è presente un controllo che, prima di eseguire la query al database, interrompe l'esecuzione dello script nel caso in cui ci sia un match con un'espressione regolare o regex.
La regex risulta vera quando viene letta una parola che contiene almeno una delle stringhe sleep, file, into e bench che sono funzioni tipicamente utilizzate in una SQL injection. Inoltre, la regex è case insensitive, questo significa che non c'è distinzione tra la parola "SLEEP" in maiuscolo e la parola "sleep" in minuscolo (o una combinazione di maiuscole e minuscole).
 

Analisi dinamica

Vediamo come si presenta la pagina nel caso in cui si faccia una normale richiesta GET.
 
Normale richiesta GET con curl

Sappiamo che l'applicazione dovrebbe essere vulnerabile a SQL injection mediante il carattere ' (apice), quindi proviamo a fare un'altra richiesta che lo contiene.
 
Payload base per SQL injection

In effetti la risposta contiene un messaggio di errore di sintassi SQL e questo è sufficiente per confermare la vulnerabilità!
 
Se proviamo a passare uno user-agent che contiene ad esempio la parola "SLeeP", lo script si interrompe e di conseguenza non è più possibile sfruttare la SQL injection.
 
SQL injection sanification

Proviamo ora ad utilizzare una tecnica blind based per fare inferenza. I classici payload che possiamo usare sono i seguenti:
  • condizione sempre vera: ' OR '1'='1
  • condizione sempre falsa: ' AND '1'='2
Di seguito un esempio di condizione sempre vera:
 
union based SQL injection always true

Com'è possibile notare, la risposta non è cambiata nemmeno di una virgola quindi non riusciamo a dedurre nulla dal suo comportamento. Inoltre, i controlli implementati sono abbastanza fastidiosi e non ci permettono di estrapolare informazioni usando tecniche basate sul tempo per le quali c'è bisogno di usare funzioni come la sleep().
 
Ma su MySQL esistono anche tecniche di Out-Of-Band (OOB) che usano, ad esempio su Microsoft Windows, la funzione load_file(). Anche in questo caso però c'è la fastidiosa regex a fermarci (nello scenario che abbiamo ipotizzato, l'utente ha privilegi limitati sul database).

Tirando le somme a questo punto abbiamo solo due tipi di risposte dal server: una in cui c'è un errore di sintassi SQL e un'altra in cui invece la query viene eseguita correttamente.
 

Getting out from the darkness

L'idea alla base della soluzione proposta è riuscire a produrre ed eseguire una query con una sintassi valida ma che implichi comunque un errore MySQL controllabile. A tal proposito, se si spulcia la documentazione di MySQL, possiamo trovare interessante la parte di Out-of-Range and Overflow Handling di cui riportiamo uno stralcio nel seguito.

When MySQL stores a value in a numeric column that is outside the permissible range of the column data type, the result depends on the SQL mode in effect at the time:

If strict SQL mode is enabled, MySQL rejects the out-of-range value with an error, and the insert fails, in accordance with the SQL standard.
...

In sostanza, in modalità "strict" sarebbe possibile far andare in errore una query se si esce dai range consentiti (a seconda del tipo di dato).
Di seguito abbiamo una lista dei range per alcuni tipi di dato:
 
MySQL data types

Inoltre, è da notare che la modalità "strict" è quella utilizzata da MySQL by default!
Se riusciamo a sfruttarlo in modo controllato, questo comportamento sembra promettente.
Eseguiamo allora un test direttamente da terminale:
 
Out-Of-Range overflow handling in MySQL

Da un primo test sembra che il comportamento del database sia quello atteso, come da la documentazione. Infatti, la query SELECT pow(9999,9999); ha come risultato un numero che esce dai range per il tipo DOUBLE e scatena quindi un errore!
Proviamo ora a testare il payload in remoto, sulla web application.
 
Out-Of-Range request con curl

Ottimo! Tutto sembra funzionare!

Ora non ci resta che pensare a un modo per sfruttare questo comportamento per fare inferenza e quindi enumerare il database. A tale scopo, è possibile usare un payload di questo tipo:
 
nonexsistentua' OR pow(99999999, pow(99999999, id REGEXP '^22' AND ua_signature LIKE '%'))#

Il payoad funziona sfruttando una semplice proprietà delle potenze:
 
Proprietà delle potenze

Tornando al nostro payload, la funzione pow() più interna esegue "99999999X " dove "X" è il valore restituito da id REGEXP '^22' AND ua_signature LIKE '%' che restituirà 1 in caso di match e 0 in caso contrario. In questo modo è possibile controllare l'errore di Out-Of-Range ed enumerare le tabelle del database. Infatti, se il sever restituisce un errore allora abbiamo un match positivo altrimenti no.

Schematizzando il comportamento abbiamo che:
  • pow(99999999, pow(99999999, 0)) = 99999999^1 => nessun errore
  • pow(99999999, pow(99999999, 1)) = 99999999^99999999 => errore di overflow

Testiamolo in remoto con i seguenti due payload per estrarre dati dal db:
  • nonexsistentua' OR pow(99999999, pow(99999999, id REGEXP '^22' AND ua_signature LIKE 'a%'))#
  • nonexsistentua' OR pow(99999999, pow(99999999, id REGEXP '^22' AND ua_signature LIKE 's%'))#
 
SQL injection blind boolean based

Funziona perfettamente! Infatti, per inferenza, possiamo arrivare ad enumerare il secret-user-agent presente nella tabella logs.

Abbiamo così ottenuto una SQL injection blind boolean based completamente controllabile e automatizzabile.
 

Conclusioni

Questa particolare tecnica può tornare molto utile ad esempio in presenza di firewall o quando la logica applicativa non è verbosa. Inoltre, è interessante notare come a volte può essere utile considerare ed analizzare a fondo i comportamenti di ogni componente dell'applicazione web.
 

Riferimenti

Articoli correlati

Security

Articoli in evidenza

Approfondimenti

UNISCITI A NOI. INVIA LA TUA CANDIDATURA

Certimeter Group crede nei valori, nella passione e nella professionalità delle persone.