
Доказательство концепции атаки «человек посередине» на ZRTP
CVE-2016-6271 затрагивает libbzrtp, библиотеку ZRTP, разработанную Belledonne Communications.
Эта библиотека встраивается в конечные приложения, например, linphone, доступный как приложение для Android в Play Store. Текущая версия 3.2.7 включает версию libbzrtp, которая не должна быть уязвима к 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 — это решение для защиты голосовых вызовов по IP.
Ниже приведён отрывок из 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.
Краткое содержание:
Уязвимый ZRTP агент компилируется из одного 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);
}
Чтобы его опробовать, соберите Docker-образ: cd vulnerable-bzrtp && docker build -t vulnerable-bzrtp .. Обратите внимание, что Dockerfile определяет start.sh как точку входа, который настраивает маршрутизацию через Mallory — об этом далее.
Уведомление об уязвимости было отправлено 30 марта 2016 года в Belledonne Communications; уязвимость была быстро устранена этим коммитом.
ZRTP выполняет обмен Диффи-Хеллмана в двух сообщениях: DHPart1 и DHPart2. Боб фиксирует сообщение DHPart2, которое отправит после получения DHPart1 от Алисы.
| Commit (Bob's ZID, options, hash value) F5 |
|<--------------------------------------------------|
| F6 DHPart1 (pvr, shared secret hashes) |
|-------------------------------------------------->|
| DHPart2 (pvi, shared secret hashes) F7 |
|<--------------------------------------------------|
Обнаруженная уязвимость в bzrtp заключается в отсутствии проверки хэш-фиксации, что оставляет возможность злоумышленнику подделать pvi с интересными свойствами.
Данная proof-of-concept реализует слабость, описанную в 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.
При активном злоумышленнике Mallory обмен DH превращается в:
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. * |
Реализация выполнена на Python3 и asyncio. Сырые IP-пакеты перехватываются с помощью сокета PF_PACKET, прослушивающего только IPv4-кадры. Scapy помогает разбирать сырые фреймы.
Он анализирует ZRTP-пакеты и перезаписывает их, в частности пересчитывая HMAC-теги аутентификации.
Подбор SAS реализован на C и принимает на вход:
Hello of responder || Commit || DHPart1DHPart1Соберите Docker-образ: cd mitm-bzrtp && docker build -t mitm-bzrtp . && cd ...
Атака «человек посередине» настраивается с помощью:
Алиса и Боб обмениваются данными через Mallory, которая оппортунистически выполняет атаку «человек посередине».
Всё сводится к тестированию различных svi и проверке, совпадает ли производный SAS с уже полученным. Все подробности можно найти в исходнике подбора.
Сообщения ZRTP аутентифицируются с помощью обратной цепочки итерированных хэшей в качестве ключей. Если DHPart2 изменяется на лету Mallory, Алиса обнаружит, что изменённый DHPart2 не является подлинным, поскольку вычисленный HMAC не совпадёт с полученным. Чтобы пройти эту проверку, Mallory должна использовать полную цепочку итерированных хэшей и подписать все отправленные сообщения с помощью этой цепочки.