
Proof of concept for ZRTP man-in-the-middle
CVE-2016-6271 afeta o libbzrtp, que é uma biblioteca ZRTP desenvolvida pela Belledonne Communications.
Esta biblioteca está embutida em aplicações de utilizador final, por exemplo o linphone, que está disponível como uma aplicação Android na Play store. A versão atual 3.2.7 inclui uma versão do libbzrtp que não deve ser vulnerável ao 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 é uma solução para proteger chamadas de voz sobre IP.
O seguinte é um excerto do 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:
Um agente ZRTP vulnerável é compilado a partir de um único ficheiro 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 aproveitar, construa a imagem docker usando cd vulnerable-bzrtp && docker build -t vulnerable-bzrtp .. Esteja ciente de que o Dockerfile define start.sh como entrypoint, que define o roteamento via Mallory - mais sobre isso depois.
Divulgado em 30 de março de 2016 para a Belledone Communications, esta vulnerabilidade foi rapidamente corrigida com este commit.
ZRTP realiza Diffie-Hellman em duas mensagens chamadas DHPart1 e DHPart2. Bob compromete a mensagem DHPart2 que enviará após receber a DHPart1 de Alice.
| Commit (Bob's ZID, options, hash value) F5 |
|<--------------------------------------------------|
| F6 DHPart1 (pvr, shared secret hashes) |
|-------------------------------------------------->|
| DHPart2 (pvi, shared secret hashes) F7 |
|<--------------------------------------------------|
A vulnerabilidade descoberta no bzrtp é a ausência de verificação do compromisso de hash, deixando espaço para um atacante forjar pvi com propriedades interessantes.
Esta prova de conceito implementa a fraqueza descrita no 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.
Com Mallory como atacante ativo, a troca DH acima torna-se:
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. * |
Ele é construído em Python3 e asyncio. Pacotes IP brutos são capturados usando um socket PF_PACKET, escutando apenas quadros IPv4. Scapy ajuda a dissecar quadros brutos. Ele analisa pacotes ZRTP e os reescreve, em particular, recalcula tags de autenticação HMAC.
O bruteforcer SAS é baseado em C e recebe como entrada:
Hello of responder || Commit || DHPart1public value sent by responder in DHPart1initiator ZIDresponder ZIDtarget SAS valueConstrua a imagem docker usando cd mitm-bzrtp && docker build -t mitm-bzrtp . && cd ...
O homem-no-meio será configurado com a ajuda de:
Alice e Bob trocarão através de Mallory, que realiza oportunisticamente um ataque de homem-no-meio.
Resume-se a testar vários svi e verificar se o SAS derivado corresponde ao SAS já obtido. Todos os detalhes sangrentos podem ser encontrados no código fonte do bruteforce.
As mensagens ZRTP são autenticadas usando uma cadeia reversa de hashes iterados como chaves. Se o DHPart2 for modificado em tempo real por Mallory, Alice detectará o DHPart2 adulterado de Mallory como não autêntico, porque o HMAC calculado não corresponderá ao HMAC recebido. Para passar nesta verificação, Mallory tem que usar uma cadeia completa de hashes iterados, e ela precisa assinar todas as mensagens enviadas usando esta cadeia.