company_banner

Разбираем WeChat — второй по популярности мессенджер в мире



    • Небольшой экскурс в WeChat;
    • О платформе, версии приложения, используемых утилитах и расшифровке исполняемого файла;
    • О двух протоколах (старом и новом);
    • О сериализации объектов;
    • Используемая криптография и обмен ключами;
    • О заголовках и хэш-функциях;
    • О найденных уязвимостях.


    Мессенджер WeChat от китайской компании Tencent


    WeChat — второй по популярности мессенджер в мире. Официальные данные о количестве пользователей найти очень затруднительно, но сделать приблизительную оценку можно.
    Речь идет примерно о 800 миллионах пользователей по всему миру, 90% которых приходится на Китай.

    В Китае практически каждый владелец смартфона использует WeChat (китайское самоназвание -Weixin), так как это не только мессенджер в классическом понимании, а целая система, включающая мобильный кошелек, встроенный браузер, интернет-магазин и пр. В нем представлены все государственные учреждения Китая. Оплатить квитанцию или записаться на прием к врачу можно через мессенджер.

    Актуальной задачей являлась интеграция CRM систем заказчиков, активно работающих в Китае, с WeChat. Этому способствовала широкая распространенность WeChat в Китае, а также отсутствие официального API. СМС-информирование в Китае стоит дорого и, что самое главное, работает нестабильно, к тому же здесь отсутствует статус «прочитано». Заказчик, используя API, сможет через свою через CRM систему уведомлять пользователей WeChat (подписанных на получение информации от номера заказчика, об этом расскажем ниже) о доставке товаров, новых заказах и прочей сервисной информации.

    Исследование протокола


    Было принято решение изучить мессенджер «изнутри», разобраться в коде 32-битной версии мессенджера для iOS. Имеется у нас старенький, видавший виды, iPhone 4S с версией iOS 7.2.1.

    В качестве MITM использовуем Burp Suite Free Edition.
    Скачиваем приложение и при помощи замечательной утилиты dumpdecrypted расшифровываем исполняемый файл.
    На момент начала реверс-инжиниринга WeChat была актуальна версия 6.3.13.

    Теперь осталось скопировать файл с устройства, дисассемблировать в IDA, и можно начинать.

    Рассмотрим алгоритм обмена ключами на примере регистрации.
    Запускаем приложение и видим предложение ввести номер телефона для регистрации.

    Вводим номер телефона и видим в MITM HTTP-запрос на адрес hkshort.weixin.qq.com/bindopmobileforreg.



    Тело запроса состоит из:

    • Заголовка (желтый цвет);
    • Шифрованных данных (синий цвет).

    После весьма продолжительного статического анализа кода удалось выяснить, что клиент общается с сервером посредством сериализованных объектов. Объекты сериализуются при помощи библиотеки Protocol Buffers.

    Первым сообщением передаются следующие данные:

    • Номер телефона ;
    • Язык системы телефона ;
    • ID устройства ;
    • Версия клиента ;
    • Ключ для расшифровывания ответа (случайные 16 байт) ;
    • Прочие данные (не очень интересующие нас).

    Сообщение сериализуется и шифруется публичным ключом сервера при помощи алгоритма RSA. Ответ сервера расшифровывается AES-ключом, переданным в запросе. В ответе сообщается, что либо все в порядке, либо указывается ошибка.

    Получаем СМС и вводим код. Формируется тот же запрос, только уже с кодом из СМС, а в ответе получаем так называемый ticket. Теперь, имея ticket, можно отправлять запрос на регистрацию. Вводим имя и нажимаем Ок.

    Обмен ключами происходит по алгоритму Диффи-Хеллмана с использованием эллиптической кривой над конечным полем «secp224r1». Генерируются закрытый и открытый ключ, и отправляется запрос на адрес hkshort.weixin.qq.com/newreg. Сервер генерирует свои ключи, а также выдает нам так называемые CryptUin и ServerID, о которых расскажем позже. В ответ сервер присылает нам свой публичный ключ и сессионный ключ.

    Теперь у нас есть публичный ключ сервера, и мы вычисляем общий ключ, с помощью которого расшифровываем сессионный ключ. С этого момента общение клиент-сервер осуществляется с помощью симметричного алгоритма AES с длиной ключа 128 бит.

    Вообще, в алгоритме установки соединения и обмена ключами нет ничего сверхъестественного. Правильно зашифровать данные и обменяться ключам — это половина задачи, необходимо также для каждого сообщения правильно составить заголовок. Даже если правильно сериализовать данные и зашифровать, то при неправильном заголовке сервер пришлет ответ с ошибкой. Заголовок выглядит так:

    bfa65f16050520252cb6b770021001754cc8fd5e57e085457800fb0242420001aeb890f40b010a0080



    Теперь расскажем подробнее о каждом поле:

    1. Идентификатор протокола. Каждый пакет начинается с этого байта.

    2. Флаги. Хранит информацию о длине SrvID, длине самого заголовка и сжатии исходного сообщения.



    Флаг сжатия выставляется равным 0b10, если сообщение не сжималось, в противном случае ставится равным 0b01.

    3. Версия приложения. Без комментариев.

    4. CryptUin. После прохождения регистрации каждому аккаунту присваивается уникальный идентификатор из четырех байт.

    5. SrvID. ID текущей сессии. Меняется при каждом новом подключении.

    6. uiCgi. Код команды. У каждой команды есть свой uiCgi и url. Например для команды bindopmobileforreg, uiCgi равен 0x91, а для newreg – 0x7e. Большинство чисел упаковываются, используя следующий алгоритм:

    private static void Write7BitEncodedInt(BinaryWriter store, int value)
            {
                Debug.Assert(store != null);
                // Write out an int 7 bits at a time.  The high bit of the byte,
                // when on, tells reader to continue reading more bytes.
                uint v = (uint)value;   // support negative numbers
                while (v >= 0x80)
                {
                    store.Write((byte)(v | 0x80));
                    v >>= 7;
                }
                store.Write((byte)v);
            }


    В данном примере uiCgi равен 0x17b, а в упакованном виде – fb02.

    7. Длина исходного сообщения. Длина сериализованных данных. Число так же упаковывается, но поскольку оно меньше 0x80, осталось без изменений.

    8. Длина сжатого сообщения. Сжатия не производилось, поэтому не отличается от предыдущего.

    9. Флаг.

    10. Хеш.

    Вычисляется следующим образом:

    hash1 = md5(cryptUin.shareKey);
    hash2 = md5( strlen(data).shareKey.hash1.data)
    resultHash = adler32(hash2)


    sharedKey –это общий ключ, полученный при хендшейке.
    Хеш также упаковывается.

    11.Флаги. Значение этих флагов осталось загадкой, но они статичны, поэтому отдельно их изучать не было смысла.

    Сейчас уже используется другой протокол, о котором напишем в последующих публикациях. По сути он является оберткой над вышеописанным. Старый протокол до сих пор поддерживается — для этого в отладчике необходимо сбросить флаг MmtlsCtrlFlag.

    Защита от спама.
    Чтобы защититься от спама, пользователь может включить опцию «подтверждение дружбы». В этом случае написать ему сообщение можно только после того как он подтвердит, что вы друзья. Запрос на подтверждение дружбы может содержать приветственное сообщение.

    Отправить много приветственных сообщений не получится. После отправки пятнадцати запросов все остальные перестают отправляться и встают в очередь. Шестнадцатый запрос отправится только тогда, когда кто-то из предыдущих пятнадцати добавит вас в друзья. Но пользователь этого не знает, и интерфейс приложения тоже никак об этом не сообщает. Выяснить это удалось при помощи анализа траффика и экспериментов.

    В процессе работы была так же обнаружена интересная уязвимость. В приложении есть возможность найти пользователя по номеру телефона. Сервер либо отвечает что такого пользователя нет, либо возвращает информацию о нем(имя, пол, город, фото итд). Но если отправлять эти запросы слишком часто, то сервер отвечает сообщением «Too many attempts. Try again later ». Этим можно воспользоваться, так как, если пользователя не существует, сервер всегда сообщит об этом, а если приходит ответ «Too many attempts. Try again later „- это значит, что пользователь существует. Используя это, можно собирать базу пользователей. Кстати обнаружились очень “интересные» пользователи, которые используют WeChat, но обычным способом их не обнаружить, и даже невозможно отправить  им приглашение, скорее всего это «особенные» люди Китая. Даже если запросить регистрацию на «интересный» номер, то сервер сообщит, что данный пользователь уже зарегистрирован и предлагает восстановить аккаунт, но уже не по СМС.

    Используя 20 000 потоков возможно с одного аккаунта за сутки собрать всю базу WeChat по Китаю, блокировки аккаунта не происходит.

    Также отдельно хотелось бы сообщить, что End-to-End шифрования между пользователями не происходит. Сообщения шифруются только симметричным ключом, сервер их расшифровывает и заново зашифровывает симметричным ключом получателя и отправляет их получателю.

    Данная статья является вводной, при наличии интереса со стороны сообщества Хабра возможно появление следующих публикаций по WeChat, так как любое событие в WeChat (например, сериализация объектов) достойны отдельных статей.
    Бринго
    30.52
    Company
    Share post

    Comments 18

      0

      Интерес есть.
      Хотя остается главный вопрос: когда они с своими бизнес-аккаунтами выйдут за пределы своего региона.

        0
        Согласен, не понятно почему не выходят за пределы. По нашим данным только в России пользователей WeChat порядка 400 000 человек, хотя скорее всего большая часть это граждане Китая, которые живут в России и держат связь с родственниками.
          +1
          Как по мне, со стороны пользователя WeChat так себе в плане юзабилити, и хуже аналогов. В Китае он так популярен потому что у него нет нормальных аналогов, и как я понял у него есть поддержка властей.
            0
            нет нормальных аналогов

            точнее они запрещены и/или заблокированы? :)

      +1
      Мде, парад велосипедов какой-то с сомнительными решениями. Процесс обмена ключами, на первый взгляд, успешно можно было заменить на TLS с certificate pinning и быть спокойным, что все сделано правильно. Для хэша вообще ерунда используется без криптографической стойкости. И, как понимаю, открытая часть сообщения никак не связана с закрытой. GCM режим шифрования тут подошел бы идеально, заменив сразу и убогий хэш, и защитив от попыток подмены открытую часть.
        +2
        Согласен, велосипедов там хватает. В текущей версии, действительно реализован TLS c AES GCM, но что интересно, пакеты ходят те же самые, описанные в данной статье(с хэшем и заголовками). Т.е они одно шифрование накрутили на другое, шифруют уже шифрованные данные. Гениально…
        0
        Молодцы, хорошо расковыряли. Ждем Whatsapp )
          +6
          WhatsApp уже разобран под iOS х64 и x32. В скором времени выложим отдельную статью, нужно время, чтобы ее хорошо оформить. Это была наша первая статья, уже видим, что можно было лучше оформить.
            0
            А вайбер расковыривали? )
              0

              Вайбер даже не начинали. У Вайбера есть официальная two-way АPI для интеграции. Есть разобранный iMessage, в дальнейшем планируем опубликовать.

              0
              Да, про WhatsApp было бы круто, эти китайские приколы довольно скучные.
            0
            Спасибо большое. Обязательно жду продолжения. Очень интересная статья.
            Интересно было бы узнать, как мессенджер следит за стоп-словами (и как их обрабатывает) которые запрещены в КНР.
            И ссылки на запрещенные ресурсы.

            Так же интересно, какие данные приложение собирает в телефоне и что отправляет на сервера.

            Возможно, после стоп-слов, приложение начнет вести себя странно. Может быть попробует даже сделать фото через фронтальную камеру или произвольно задействует GPS. А может еще что-то сделает.
            Довольно интересная тема.
              0

              Весьма редкая инфа по реализации протокола мессенджера, и ньюансы шифрования.
              Все очень интересно! Больше инфы! Больше Хлеба и Зрелищ!!!)

                0
                и не забыть сказать, что все данные оттуда достояние китайского правительства. Вдруг кто-то не обратил внимание.
                  –2
                  Вот это, описывающее способ шифрования:
                  Сообщение сериализуется и шифруется публичным ключом сервера при помощи алгоритма RSA. Ответ сервера расшифровывается AES-ключом, переданным в запросе. В ответе сообщается, что либо все в порядке, либо указывается ошибка.
                  Противоречит утверждению в конце статьи:
                  Также отдельно хотелось бы сообщить, что End-to-End шифрования между пользователями не происходит.
                  Автор, похоже, сам не знает, что такое End-to-End шифрование.

                  Но в целом статья весьма нужная — я, как разработчик p2p-информационной системы, с интересом слежу за технологиями, альтернативными гадкой вебне.
                    0
                    В чем противоречие? — Черным по белому написано что сообщение шифруется публичным ключом сервера.
                    Если бы применялось сквозное шифрование, то сообщение шифровалось бы публичным ключом получателя.
                    Ты похоже сам не знаешь, «что такое End-to-End шифрование»…
                      0
                      End-to-end шифрование означает, что сервер не может читать сообщения пользователей, что требует обмена публичными ключами между пользователями, которые желают беседовать. Здесь же описывается обычная схема гибридного шифрования, которая обычно реализуется SSL/TLS, а тут решили свой велосипед сделать.

                    Only users with full accounts can post comments. Log in, please.