
Prueba de concepto para ataques man-in-the-middle en ZRTP
CVE-2016-6271 afecta a libbzrtp, una librería ZRTP desarrollada por Belledonne Communications.
Esta librería está integrada en aplicaciones de usuario final, por ejemplo linphone, que está disponible como aplicación para Android en Play store. La versión actual 3.2.7 incluye una versión de libbzrtp que no debería ser vulnerable 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 es una solución para asegurar llamadas de voz sobre IP.
Lo siguiente es un extracto del IETF rfc6189:
ZRTP is a key agreement protocol that performs a Diffie-Hellman key
exchange during call setup in the media path and is transported over
the same port as the Real-time Transport Protocol (RTP) [RFC3550]
media stream which has been established using a signaling protocol
such as Session Initiation Protocol (SIP) [RFC3261]. This generates
a shared secret, which is then used to generate keys and salt for a
Secure RTP (SRTP) [RFC3711] session. ZRTP borrows ideas from
[PGPfone]. A reference implementation of ZRTP is available in
[Zfone].
The ZRTP protocol has some nice cryptographic features lacking in
many other approaches to media session encryption. Although it uses
a public key algorithm, it does not rely on a public key
infrastructure (PKI). In fact, it does not use persistent public
keys at all. It uses ephemeral Diffie-Hellman (DH) with hash
commitment and allows the detection of man-in-the-middle (MiTM)
attacks by displaying a short authentication string (SAS) for the
users to read and verbally compare over the phone.
Para resumir:
Un agente ZRTP vulnerable se compila a partir de un único archivo 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);
}
Para disfrutarlo, construye la imagen Docker con cd vulnerable-bzrtp && docker build -t vulnerable-bzrtp .. Ten en cuenta que el Dockerfile define start.sh como entrypoint, que establece el enrutamiento a través de Mallory; más sobre esto más adelante.
Divulgada el 30 de marzo de 2016 a Belledone Communications, esta vulnerabilidad fue corregida rápidamente con este commit.
ZRTP realiza Diffie-Hellman en dos mensajes llamados DHPart1 y DHPart2. Bob compromete el mensaje DHPart2 que enviará después de recibir el DHPart1 de Alice.
| Commit (Bob's ZID, options, hash value) F5 |
|<--------------------------------------------------|
| F6 DHPart1 (pvr, shared secret hashes) |
|-------------------------------------------------->|
| DHPart2 (pvi, shared secret hashes) F7 |
|<--------------------------------------------------|
La vulnerabilidad descubierta en bzrtp es la ausencia de verificación del hash commitment, dejando espacio para que un atacante forje pvi con propiedades interesantes.
Esta prueba de concepto implementa la debilidad descrita en rfc6189:
The use of hash commitment in the DH exchange constrains the attacker
to only one guess to generate the correct Short Authentication String
(SAS) (Section 7) in his attack, which means the SAS can be quite
short. A 16-bit SAS, for example, provides the attacker only one
chance out of 65536 of not being detected. Without this hash
commitment feature, a MiTM attacker would acquire both the pvi and
pvr public values from the two parties before having to choose his
own two DH public values for his MiTM attack. He could then use that
information to quickly perform a bunch of trial DH calculations for
both sides until he finds two with a matching SAS. To raise the cost
of this birthday attack, the SAS would have to be much longer. The
Short Authentication String would have to become a Long
Authentication String, which would be unacceptable to the user. A
hash commitment precludes this attack by forcing the MiTM to choose
his own two DH public values before learning the public values of
either of the two parties.
Con Mallory como atacante activo, el intercambio DH anterior se convierte en:
Bob Mallory Alice
| | Commit (Alice's ZID...) |
| |<------------------------|
| Commit (Alice's ZID...) | |
|<------------------------| |
| | |
| DHPart1 (pvr...) | |
|------------------------>| |
| | DHPart1 (pvr'...) |
| |------------------------>|
| | DHPart2 (pvi...) |
| |<------------------------|
| * SAS(Mallory, Alice) is known at this time * |
| * now find a pvi' such as SAS(Bob, Mallory) * |
| * equals SAS(Mallory, Alice) * |
| DHPart2(pvi'...) | |
|<------------------------| |
| * SAS(Mallory, Bob) = SAS(Alice, Mallory) * |
| * Both parties will confirm they share the same * |
| * SAS value * |
| * Note that SRTP material (Alice, Mallory) will * |
| * not match SRTP material (Mallory, Bob) * |
| * However, Mallory will be able to handle SRTP * |
| * flows from both Bob and Alice, giving * |
| * interception and tampering capabilities. * |
Está construido con Python3 y asyncio. Los paquetes IP crudos se capturan usando un socket PF_PACKET, escuchando solo tramas IPv4. Scapy ayuda a diseccionar las tramas crudas.
Analiza los paquetes ZRTP y los reescribe; en particular, recalcula las etiquetas de autenticación HMAC.
El bruteforcer de SAS está basado en C y toma como entrada:
Hello of responder || Commit || DHPart1DHPart1Construye la imagen Docker con cd mitm-bzrtp && docker build -t mitm-bzrtp . && cd ...
El hombre en el medio se configurará con la ayuda de:
Alice y Bob se comunicarán a través de Mallory, quien realiza oportunistamente un ataque de hombre en el medio.
Se reduce a probar varios svi y verificar si el SAS derivado coincide con el SAS ya obtenido. Todos los detalles escabrosos se pueden encontrar en el código fuente del bruteforce.
Los mensajes ZRTP se autentican usando una cadena invertida de hashes iterados como claves. Si DHPart2 es modificado sobre la marcha por Mallory, Alice detectará que el DHPart2 manipulado de Mallory no es auténtico, porque el HMAC calculado no coincidirá con el HMAC recibido. Para superar esta verificación, Mallory tiene que usar una cadena completa de hashes iterados, y necesita firmar todos los mensajes enviados usando esta cadena.