
Prova di concetto per ZRTP man-in-the-middle
CVE-2016-6271 impatta libbzrtp, una libreria ZRTP sviluppata da Belledonne Communications.
Questa libreria è inclusa in applicazioni per utenti finali, ad esempio linphone, disponibile come app Android su Play Store. La versione attuale 3.2.7 include una versione di libbzrtp che non dovrebbe essere vulnerabile a CVE-2016-6271.
cd vulnerable-bzrtp && docker build -t vulnerable-bzrtp .
cd mitm-bzrtp && docker build -t mitm-bzrtp .
docker-compose -f cve-2016-6271.yaml up
ZRTP è una soluzione per proteggere le chiamate vocali su IP.
Quanto segue è un estratto dall'IETF rfc6189:
ZRTP è un protocollo di scambio chiavi che esegue un
scambio Diffie-Hellman durante l'impostazione della chiamata
nel percorso dei media e viene trasportato sulla stessa porta
del flusso di media Real-time Transport Protocol (RTP) [RFC3550]
che è stato stabilito utilizzando un protocollo di segnalazione
come Session Initiation Protocol (SIP) [RFC3261]. Questo genera
un segreto condiviso, che viene poi utilizzato per generare chiavi e salt
per una sessione Secure RTP (SRTP) [RFC3711]. ZRTP prende in prestito idee
da [PGPfone]. Un'implementazione di riferimento di ZRTP è disponibile in
[Zfone].
Il protocollo ZRTP ha alcune belle caratteristiche crittografiche che mancano
in molti altri approcci alla crittografia delle sessioni multimediali. Sebbene utilizzi
un algoritmo a chiave pubblica, non si basa su un'infrastruttura a
chiave pubblica (PKI). Infatti, non utilizza affatto chiavi pubbliche
persistenti. Utilizza Diffie-Hellman effimero (DH) con hash
commitment e consente il rilevamento di attacchi man-in-the-middle (MiTM)
mostrando una breve stringa di autenticazione (SAS) che gli utenti
possono leggere e confrontare verbalmente al telefono.
Per riassumere:
Un agente ZRTP vulnerabile viene compilato da un singolo file C:
ctx = bzrtp_createBzrtpContext(self_ssrc);
assert(ctx != NULL);
ret = bzrtp_setCallbacks(ctx, &bzrtp_callbacks);
assert(ret == 0);
bzrtp_initBzrtpContext(ctx);
bzrtp_setClientData(ctx, self_ssrc, ctx);
ret = bzrtp_startChannelEngine(ctx, self_ssrc);
assert(ret == 0);
for (now = 0; now += 50;) {
usleep(500000);
ret = recv(sd, buffer, sizeof(buffer), MSG_DONTWAIT);
if (ret > 0) {
received = ret;
bzrtp_processMessage(ctx, self_ssrc, buffer, received);
}
bzrtp_iterate(ctx, self_ssrc, now);
}
Per divertirti, costruisci l'immagine Docker usando cd vulnerable-bzrtp && docker build -t vulnerable-bzrtp .. Attenzione che il Dockerfile definisce start.sh come entrypoint, che imposta il routing tramite Mallory - ne parleremo più avanti.
Divulgata il 30 marzo 2016 a Belledone Communications, questa vulnerabilità è stata rapidamente corretta con questo commit.
ZRTP esegue Diffie-Hellman in due messaggi chiamati DHPart1 e DHPart2. Bob si impegna al messaggio DHPart2 che invierà dopo aver ricevuto il DHPart1 di Alice.
| Commit (ZID di Bob, opzioni, valore hash) F5 |
|<--------------------------------------------------|
| F6 DHPart1 (pvr, hash del segreto condiviso) |
|-------------------------------------------------->|
| DHPart2 (pvi, hash del segreto condiviso) F7 |
|<--------------------------------------------------|
La vulnerabilità scoperta in bzrtp è l'assenza di verifica dell'hash-commitment, lasciando spazio a un attaccante di falsificare pvi con proprietà interessanti.
Questa prova di concetto implementa la debolezza descritta in rfc6189:
L'uso dell'hash-commitment nello scambio DH limita l'attaccante
a un solo tentativo per generare la corretta Stringa di Autenticazione
Breve (SAS) (Sezione 7) nel suo attacco, il che significa che la SAS
può essere piuttosto breve. Una SAS a 16 bit, ad esempio, fornisce
all'attaccante solo una possibilità su 65536 di non essere rilevato.
Senza questa caratteristica di hash-commitment, un attaccante MiTM
acquisirebbe entrambi i valori pubblici pvi e pvr dalle due parti
prima di dover scegliere i propri due valori pubblici DH per il suo
attacco MiTM. Potrebbe quindi utilizzare queste informazioni per
eseguire rapidamente una serie di calcoli DH di prova per entrambi
i lati finché non ne trova due con una SAS corrispondente. Per
aumentare il costo di questo attacco del compleanno, la SAS dovrebbe
essere molto più lunga. La Stringa di Autenticazione Breve dovrebbe
diventare una Stringa di Autenticazione Lunga, il che sarebbe
inaccettabile per l'utente. Un hash-commitment impedisce questo
attacco costringendo il MiTM a scegliere i propri due valori
pubblici DH prima di apprendere i valori pubblici di una delle
due parti.
Con Mallory come attaccante attivo, lo scambio DH sopra diventa:
Bob Mallory Alice
| | Commit (ZID di Alice...) |
| |<------------------------|
| Commit (ZID di Alice...) | |
|<------------------------| |
| | |
| DHPart1 (pvr...) | |
|------------------------>| |
| | DHPart1 (pvr'...) |
| |------------------------>|
| | DHPart2 (pvi...) |
| |<------------------------|
| * SAS(Mallory, Alice) è nota in questo momento * |
| * ora trova un pvi' tale che SAS(Bob, Mallory) * |
| * sia uguale a SAS(Mallory, Alice) * |
| DHPart2(pvi'...) | |
|<------------------------| |
| * SAS(Mallory, Bob) = SAS(Alice, Mallory) * |
| * Entrambe le parti confermeranno di condividere * |
| * lo stesso valore SAS * |
| * Nota che il materiale SRTP (Alice, Mallory) * |
| * non corrisponderà al materiale SRTP (Mallory, * |
| * Bob) * |
| * Tuttavia, Mallory sarà in grado di gestire * |
| * i flussi SRTP sia di Bob che di Alice, * |
| * offrendo capacità di intercettazione e * |
| * manomissione. * |
È costruito su Python3 e asyncio. I pacchetti IP grezzi vengono catturati usando un socket PF_PACKET, ascoltando solo frame IPv4. Scapy aiuta a dissociare i frame grezzi.
Analizza i pacchetti ZRTP e li riscrive, in particolare ricalcola i tag di autenticazione HMAC.
Il bruteforcer SAS è basato su C e prende come input:
Hello del responder || Commit || DHPart1DHPart1Costruisci l'immagine Docker usando cd mitm-bzrtp && docker build -t mitm-bzrtp . && cd ...
Il man-in-the-middle sarà impostato con l'aiuto di:
Alice e Bob scambieranno tramite Mallory, che esegue opportunisticamente un attacco man-in-the-middle.
In pratica si tratta di testare vari svi e verificare se la SAS derivata corrisponde alla SAS già ottenuta. Tutti i dettagli cruenti si trovano nel sorgente del bruteforce.
I messaggi ZRTP sono autenticati utilizzando una catena inversa di hash iterati come chiavi. Se DHPart2 viene modificato al volo da Mallory, Alice rileverà il DHPart2 manomesso da Mallory come non autentico, perché l'HMAC calcolato non corrisponderà all'HMAC ricevuto. Per superare questo controllo, Mallory deve utilizzare un'intera catena di hash iterati e deve firmare tutti i messaggi inviati usando questa catena.