В продолжение направления публикаций исследований нашей компании о внутренних механизмах крупнейших в мире мессенджеров. Сегодня будем смотреть на WhatsApp в его актуальном состоянии. На самом деле внутренности не сильно изменились за последние три года, изменения там скорее косметические.
В этой статье мы подробно посмотрим как можно изучать протокол работы мессенджера, ответим на вопрос «может ли WhatsApp читать нашу переписку?» и приложу весь необходимый код на языке PHP.
В качестве формата сообщений WhatsApp использует доработанную версию протокола XMPP. Все сообщения сжимаются посредством замены часто используемых слов на 1 или 2 байтные токены (например вместо «message» — записываем байт 0x5f), таким образом получается то, что названо FunXMPP.
Некоторые пакеты могут быть дополнительно сжаты zlib. Полученный пакет шифруется AES GCM 256-бит и передаётся на сервер.
Для получения ключей шифрования клиент выполняет хэндшейк noise protocol. Вот здесь хорошо описано. От воплощения в коде все разработчики открытых реализаций почему-то отказались и повесили плашку «end of support», мы это поправим.
При первом подключении выполняется полный хэндшейк. После успешного подключения к серверу каждый раз выдаётся новый ключ для следующей сессии, при помощи которого в дальнейшем выполняется подключение без обмена ключами.
WhatsApp поддерживает сквозное шифрование с использованием libaxolotl (Signal Protocol), в коде реализованы две версии — сначала они просто сделали шифрование, а потом добавили выравнивание и назвали это v2. Тут есть только одна проблема — т.к. это централизованная система, то и передача ключей идёт через сервера WhatsApp, так что чисто технически нет никаких препятствий для разработчиков мессенджера передать подставные ключи шифрования и полностью читать переписку. Но это не имеет обратной силы, — прочитать сообщения задним числом не получится.
Кстати, при получении сообщения от не авторизованного контакта — в мессенджере отображается кнопка «пожаловаться на спам», нажав на неё мы не только заблокируем контакт, но и перешлём по защищенному каналу (уже в открытом виде!), текст сообщения. Без изучения кода приложения целиком нет возможности гарантировать, что этот функционал не используется в каких-либо других случаях.
Для проверки аутентичности ключей шифрования в приложении WhatsApp можно зайти в карточку контакта, выбрать пункт «Шифрование», после чего приложение предложит просканировать QR-код на устройстве получателя. Таким образом, кстати, можно сделать кастомное приложение на базе протокола WhatsApp, которое будет постоянно следить за статусом ключей шифрования и выдавать красивые плашки «АНБ следит за вами» или «Вы в безопасности»: учитывая многообразие мессенджеров, — заменить один из них на такое приложение будет даже полезно.
Алгоритм работы можно предположить следующий: устройство отображающее QR-код кодирует в него свой публичный ключ, устройство считывающее QR-код сверяет ключ с имеющемся в своей базе данных. Это безопасный способ верификации ключа, но только при отсутствии закладок в приложении.
При отправке мультимедиа файлы загружаются на сервера WhatsApp, мы не проводили дополнительные исследования шифруются ли они там. Скорее всего не шифруются, т.к. разработчик приложения себе доверяет, а ссылка на файл передаётся по защищенному сквозным шифрованием каналу.
Регистрация проходит в три https-запроса на домен v.whatsapp.net (их можно подсмотреть любым известным способом, например burp или mitmproxy, в приложении используется certificate pinning, который обходится при помощи ssl kill switch).
не делает ничего полезного, раньше, скорее всего, служил для проверки не зарегистрирован ли уже этот номер (скорее всего до момента, пока кто-то не стал перебирать их базу)
Собственно запрашивает код sms активации. Так же может использоваться аналогичный запрос для получения звонка. Параметры сквозного шифрования не обязательны, — их можно настроить при дальнейшем подключении. Токен получается так:
Первая строка — это зашифрованная строка landscape, что бы это не значило. Версию приложения (21752) можно подставить любую (самые любопытные могут попробовать зарегистрироваться ещё не вышедшей версией), в самом коде приложения хэш от версии зашит готовый, но что-то похожее на md5-хэш довольно быстро подбирается.
Этот запрос, соответственно, подтверждает регистрацию с кодом полученным по смс или звонку.
В запросах используется
Всё это известно не первый день, — есть несколько реализаций протокола WA16 (Chat-API, Yowsup), от текущего WA20 отличие заключается по-сути только в Noise Protocol. Обладая этой информацией мы можем разработать локальный MITM для просмотра расшифрованного трафика приложения. Т.к. изначально протокол приложения XMPP — то всё что там происходит будет вполне понятно сразу из расшифрованного трафика, поэтому погружаться в дебри дизассемблера особой нужды нет.
Для запуска MITM — мы представимся настоящим сервером WhatsApp, выполним полный хэндшейк, после чего наше приложение будет прослушивать трафик перенаправляя его на оригинальный сервер whatsapp и обратно. Для этого нам понадобится изменить оригинальное приложение:
Все операции проводятся в следующей конфигурации: iPhone 4, iOS 7.1.2, IDA 7, WhatsApp 2.17.52.
1. Мы будем выполнять полный хэндшейк при каждом подключении, это существенно упростит нам жизнь. В методе
В регистре R0 как раз хранится serverStaticPublicKey и при его отсутствии выполняется переход к FullHandshake. А мы сделаем этот переход безусловным. Для этого заменим два байта
на
Результат
В декомпиляторе видим, что одна из веток условия стала недостижимой и не отображается.
2. Мы отключим проверку подписи сервера, т.к. у нас нет закрытых ключей оригинального сервера. Без этого изменения прослушать трафик будет невозможно. Для этого внесём изменения в метод (bool)-
Нам необходимо чтобы функция всегда возвращала единицу. Сейчас результат проверки сертификата из регистра R6 кладется в R0.
Сделаем так чтобы в R0 записывалась единица
В результате получаем
Приложение собрано со всей отладочной информацией, поэтому дополнительно выполнять переименования, описывать структуры и вообще выполнять процесс реверсинга — при создании патча фактически не требуется. Изображения приведены сразу после запуска декомпилятора, без дополнительной обработки.
iOS 7.1.2 не проверяет аутентичность бинарных файлов приложений, поэтому все изменения можно сделать прямо в файле приложения. В более поздних версиях iOS можно сделать эти же изменения в памяти запущенного приложения.
Далее при помощи IDA и долгих кропотливых усилий подготавливаем код успешно выполняющий NoiseHandshake на серверах WhatsApp. Затем реализуем фейковый сервер — делаем все те же операции шифрования, но в обратном порядке (звучит просто, но если не занимаешься этим каждый день — та ещё головная боль). Готовый код лежит здесь.
Скачиваем на компьютер приложение MITM полученное по ссылке выше. Устанавливаем PHP 5.6 (подойдёт и другая версия, в моей конфигурации использовалась именно эта версия). Так же понадобится ещё две библиотеки:
— первая
— вторая
Остаётся только заставить приложение подключаться к нашему серверу, вместо настоящего. Приложение выполняет подключение к одному из серверов e%d.whatsapp.net (где %d — число от 1 до 16, например e5.whatsapp.net), каждый из которых резолвится на несколько IP-адресов, каждый раз — разных, в сумме больше 300 серверов. Проще всего будет подменить DNS-ответ сервера и направить его на наш компьютер с MITM.
Для этого помещаем телефон и компьютер в одну сеть (буквально любой маршрутизатор wifi подойдёт), на телефоне выставляем DNS на наш компьютер на котором устанавливаем bind9 со следующим хостом:
Далее отредактируем mitm.php — нужно подставить свой номер телефона в поле username и содержимое файла cck.dat (находится в директории с приложением) в поле password.
Запускаем php mitm.php. Запускаем приложение WhatsApp и видим следующую картинку:
На скриншоте видно два пакета от сервера — сообщение об успешной авторизации и какие-то настройки. Таким образом выглядит абсолютно весь трафик приложения, — всё читабельно и дополнительно разбирать приложение, в большинстве случаев, не требуется.
Алгоритм превращения FunXMPP в читаемый текст есть во всех библиотеках работы с WhatsApp. Внутри простейшая машина состояний, дополнительно расписывать его здесь не буду.
Приложение сделано качественно, использует современные протоколы шифрования, однако глубоко внутри лежит XMPP оставшийся от изначально используемого ejabberd. Использовать protobuf, судя по текущему вектору развития приложения, — было бы логичнее, однако историческое наследие слишком дорого.
Сервис долгое время сильно страдал от массовых рассылок, — код открытых проектов широко этому способствовал. На данный момент уже давно не приходило никаких рассылок, — разработчики ввели ограничение на количество отправляемых сообщений не авторизованным контактам. Именно это дополнение (технически реализуемое полностью на стороне сервера), на мой взгляд, решительно поставило точку в противостоянии любителей рассылок и работников отдела по борьбе с рассылками (если у них такой есть).
Чисто технически нет ничего сложного в таком алгоритме, разработчикам других мессенджеров (привет, Viber, — получаю спам не реже раза в месяц), — стоит взять этот приём на заметку.
Спасибо за прочтение, надеюсь читать про это было так же весело и интересно, как и проводить исследование.
В этой статье мы подробно посмотрим как можно изучать протокол работы мессенджера, ответим на вопрос «может ли WhatsApp читать нашу переписку?» и приложу весь необходимый код на языке PHP.
Общая информация
В качестве формата сообщений WhatsApp использует доработанную версию протокола XMPP. Все сообщения сжимаются посредством замены часто используемых слов на 1 или 2 байтные токены (например вместо «message» — записываем байт 0x5f), таким образом получается то, что названо FunXMPP.
Некоторые пакеты могут быть дополнительно сжаты zlib. Полученный пакет шифруется AES GCM 256-бит и передаётся на сервер.
Для получения ключей шифрования клиент выполняет хэндшейк noise protocol. Вот здесь хорошо описано. От воплощения в коде все разработчики открытых реализаций почему-то отказались и повесили плашку «end of support», мы это поправим.
При первом подключении выполняется полный хэндшейк. После успешного подключения к серверу каждый раз выдаётся новый ключ для следующей сессии, при помощи которого в дальнейшем выполняется подключение без обмена ключами.
WhatsApp поддерживает сквозное шифрование с использованием libaxolotl (Signal Protocol), в коде реализованы две версии — сначала они просто сделали шифрование, а потом добавили выравнивание и назвали это v2. Тут есть только одна проблема — т.к. это централизованная система, то и передача ключей идёт через сервера WhatsApp, так что чисто технически нет никаких препятствий для разработчиков мессенджера передать подставные ключи шифрования и полностью читать переписку. Но это не имеет обратной силы, — прочитать сообщения задним числом не получится.
Кстати, при получении сообщения от не авторизованного контакта — в мессенджере отображается кнопка «пожаловаться на спам», нажав на неё мы не только заблокируем контакт, но и перешлём по защищенному каналу (уже в открытом виде!), текст сообщения. Без изучения кода приложения целиком нет возможности гарантировать, что этот функционал не используется в каких-либо других случаях.
Для проверки аутентичности ключей шифрования в приложении WhatsApp можно зайти в карточку контакта, выбрать пункт «Шифрование», после чего приложение предложит просканировать QR-код на устройстве получателя. Таким образом, кстати, можно сделать кастомное приложение на базе протокола WhatsApp, которое будет постоянно следить за статусом ключей шифрования и выдавать красивые плашки «АНБ следит за вами» или «Вы в безопасности»: учитывая многообразие мессенджеров, — заменить один из них на такое приложение будет даже полезно.
Алгоритм работы можно предположить следующий: устройство отображающее QR-код кодирует в него свой публичный ключ, устройство считывающее QR-код сверяет ключ с имеющемся в своей базе данных. Это безопасный способ верификации ключа, но только при отсутствии закладок в приложении.
При отправке мультимедиа файлы загружаются на сервера WhatsApp, мы не проводили дополнительные исследования шифруются ли они там. Скорее всего не шифруются, т.к. разработчик приложения себе доверяет, а ссылка на файл передаётся по защищенному сквозным шифрованием каналу.
Регистрация учетной записи
Регистрация проходит в три https-запроса на домен v.whatsapp.net (их можно подсмотреть любым известным способом, например burp или mitmproxy, в приложении используется certificate pinning, который обходится при помощи ssl kill switch).
v.whatsapp.net/v2/exists?cc=код_страны&in=телефон&id=id_устройства&lg=en&lc=zz
не делает ничего полезного, раньше, скорее всего, служил для проверки не зарегистрирован ли уже этот номер (скорее всего до момента, пока кто-то не стал перебирать их базу)
v.whatsapp.net/v2/code?method=sms&cc=код_страны&in=телефон&token=токен&sim_mcc=mcc&sim_mnc=mnc&id=id_устройства&lg=en&lc=zz&параметры_сквозного_шифрования
Собственно запрашивает код sms активации. Так же может использоваться аналогичный запрос для получения звонка. Параметры сквозного шифрования не обязательны, — их можно настроить при дальнейшем подключении. Токен получается так:
md5("0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM" . md5("21752") . "телефон")
Первая строка — это зашифрованная строка landscape, что бы это не значило. Версию приложения (21752) можно подставить любую (самые любопытные могут попробовать зарегистрироваться ещё не вышедшей версией), в самом коде приложения хэш от версии зашит готовый, но что-то похожее на md5-хэш довольно быстро подбирается.
v.whatsapp.net/v2/register?cc=код_страны&in=телефон&code=код_из_смс&id=id_устройства&lg=en&lc=zz
Этот запрос, соответственно, подтверждает регистрацию с кодом полученным по смс или звонку.
В запросах используется
User-Agent: WhatsApp/2.17.52 iPhone_OS/7.1.2 Device/iPhone_4
. Правильный агент скорее всего обязателен, для корректной верификации токена.MITM
Всё это известно не первый день, — есть несколько реализаций протокола WA16 (Chat-API, Yowsup), от текущего WA20 отличие заключается по-сути только в Noise Protocol. Обладая этой информацией мы можем разработать локальный MITM для просмотра расшифрованного трафика приложения. Т.к. изначально протокол приложения XMPP — то всё что там происходит будет вполне понятно сразу из расшифрованного трафика, поэтому погружаться в дебри дизассемблера особой нужды нет.
Для запуска MITM — мы представимся настоящим сервером WhatsApp, выполним полный хэндшейк, после чего наше приложение будет прослушивать трафик перенаправляя его на оригинальный сервер whatsapp и обратно. Для этого нам понадобится изменить оригинальное приложение:
Все операции проводятся в следующей конфигурации: iPhone 4, iOS 7.1.2, IDA 7, WhatsApp 2.17.52.
Патчим приложение
1. Мы будем выполнять полный хэндшейк при каждом подключении, это существенно упростит нам жизнь. В методе
-[NoiseHandshakeManager initWithLoginPayload:clientStaticKeyPair:serverStaticPublicKey:]
при наличии serverStaticPublicKey
выполняется ResumeHandshake
, а при отсутствии FullHandshake
.В регистре R0 как раз хранится serverStaticPublicKey и при его отсутствии выполняется переход к FullHandshake. А мы сделаем этот переход безусловным. Для этого заменим два байта
на
Результат
В декомпиляторе видим, что одна из веток условия стала недостижимой и не отображается.
2. Мы отключим проверку подписи сервера, т.к. у нас нет закрытых ключей оригинального сервера. Без этого изменения прослушать трафик будет невозможно. Для этого внесём изменения в метод (bool)-
[NoiseHandshakeManager validateNoiseCertificate:serverHandshakeStaticPublicKey:]
.Нам необходимо чтобы функция всегда возвращала единицу. Сейчас результат проверки сертификата из регистра R6 кладется в R0.
Сделаем так чтобы в R0 записывалась единица
В результате получаем
Приложение собрано со всей отладочной информацией, поэтому дополнительно выполнять переименования, описывать структуры и вообще выполнять процесс реверсинга — при создании патча фактически не требуется. Изображения приведены сразу после запуска декомпилятора, без дополнительной обработки.
iOS 7.1.2 не проверяет аутентичность бинарных файлов приложений, поэтому все изменения можно сделать прямо в файле приложения. В более поздних версиях iOS можно сделать эти же изменения в памяти запущенного приложения.
Пишем код...
Далее при помощи IDA и долгих кропотливых усилий подготавливаем код успешно выполняющий NoiseHandshake на серверах WhatsApp. Затем реализуем фейковый сервер — делаем все те же операции шифрования, но в обратном порядке (звучит просто, но если не занимаешься этим каждый день — та ещё головная боль). Готовый код лежит здесь.
Скачиваем на компьютер приложение MITM полученное по ссылке выше. Устанавливаем PHP 5.6 (подойдёт и другая версия, в моей конфигурации использовалась именно эта версия). Так же понадобится ещё две библиотеки:
— первая
— вторая
Перехватываем соединение
Остаётся только заставить приложение подключаться к нашему серверу, вместо настоящего. Приложение выполняет подключение к одному из серверов e%d.whatsapp.net (где %d — число от 1 до 16, например e5.whatsapp.net), каждый из которых резолвится на несколько IP-адресов, каждый раз — разных, в сумме больше 300 серверов. Проще всего будет подменить DNS-ответ сервера и направить его на наш компьютер с MITM.
Для этого помещаем телефон и компьютер в одну сеть (буквально любой маршрутизатор wifi подойдёт), на телефоне выставляем DNS на наш компьютер на котором устанавливаем bind9 со следующим хостом:
$TTL 86400 @ IN SOA ns.whatsapp.net. admin.whatsapp.net. ( 2017100500 28800 7200 1209600 86400) @ IN NS @ @ IN A ВАШ_АЙПИ v IN A 184.173.136.86 v IN A 174.37.243.85 * IN CNAME @
Смотрим трафик
Далее отредактируем mitm.php — нужно подставить свой номер телефона в поле username и содержимое файла cck.dat (находится в директории с приложением) в поле password.
Запускаем php mitm.php. Запускаем приложение WhatsApp и видим следующую картинку:
На скриншоте видно два пакета от сервера — сообщение об успешной авторизации и какие-то настройки. Таким образом выглядит абсолютно весь трафик приложения, — всё читабельно и дополнительно разбирать приложение, в большинстве случаев, не требуется.
Алгоритм превращения FunXMPP в читаемый текст есть во всех библиотеках работы с WhatsApp. Внутри простейшая машина состояний, дополнительно расписывать его здесь не буду.
Выводы
Приложение сделано качественно, использует современные протоколы шифрования, однако глубоко внутри лежит XMPP оставшийся от изначально используемого ejabberd. Использовать protobuf, судя по текущему вектору развития приложения, — было бы логичнее, однако историческое наследие слишком дорого.
Сервис долгое время сильно страдал от массовых рассылок, — код открытых проектов широко этому способствовал. На данный момент уже давно не приходило никаких рассылок, — разработчики ввели ограничение на количество отправляемых сообщений не авторизованным контактам. Именно это дополнение (технически реализуемое полностью на стороне сервера), на мой взгляд, решительно поставило точку в противостоянии любителей рассылок и работников отдела по борьбе с рассылками (если у них такой есть).
Чисто технически нет ничего сложного в таком алгоритме, разработчикам других мессенджеров (привет, Viber, — получаю спам не реже раза в месяц), — стоит взять этот приём на заметку.
Спасибо за прочтение, надеюсь читать про это было так же весело и интересно, как и проводить исследование.