В код клиентов Telegram добавили возможность маскировки под HTTPS (TLS + HTTP/2.0).
![](https://habrastorage.org/r/w1560/webt/b3/hg/t0/b3hgt0upzhdrmslkix08w_hjd80.png)
Для использования этой возможности добавили новый префикс секрета — «ee». Кроме того, добавили возможность кодировать секрет в адресе прокси сервера как base64, в дополнение к hex.
Перед тем, как углубиться в детали, попробуем разобраться как развивалась поддержка прокси-серверов в Telegram.
В протоколе между клиентом Telegram и прокси-сервером добавили ещё один слой инкапсуляции поверх TCP. Вместо посылки данных по TCP, данные оборачиваются в записи TLS следующего вида:
![](https://habrastorage.org/r/w1560/webt/re/ye/gr/reyegr-9iptayjtr6hyruhcm56y.png)
В начале работы добавился этап эмуляции TLS-handshake. Пакет от клиента к прокси-серверу имеет такую структуру:
![](https://habrastorage.org/r/w1560/webt/c7/cw/2-/c7cw2-da2powgfq1_zrx8iellbs.png)
Почти все поля не имеют для клиентов Telegram смысла и нужны лишь для того чтобы прикидываться TLS. Самая важную функцию несёт поле Random, куда помещается результат HMAC от общего секрета и данных в пакете, что позволяет доказать клиенту что он знает секрет. Также, клиент ксорит последние 4 байта поля Random со своим временем в формате unixtime, что позволяет прокси-серверу определять когда был сгенерирован пакет. Это полезно для защиты от replay-атак. Если пакет сгенерирован давно или в будущем, то прокси-сервер может его сразу отбросить.
При подключении клиента, прокси сервер проверяет переданный HMAC. Если он совпадает с вычисленным, прокси отвечает пакетом со следующей структурой:
![](https://habrastorage.org/r/w1560/webt/to/d_/z0/tod_z0oiokru5bd6lsctypp5m_a.png)
Поле Random в нём также не является случайным, а является результатом HMAC от общего секрета и данных в пакете, причём при вычислении HMAC, случайное значение, отправленное клиентом приписывается перед данными самого пакета. При передаче самих данных, первая посылка игнорируется клиентом, что позволяет послать ему данные случайной длины, для дополнительного усложнения обнаружения.
Для демонстрации был доработан и поднят прокси-сервер на языке Python, к которому можно подключаться десктопным клиентом Telegram последних версий и смотреть передающийся трафик с помощью Wireshark:
Также, поддержка маскировки под TLS была добавлена в прокси-сервер на языке Erlang. Скорее всего, в ближайшее время, данную функциональность добавят и в другие реализации прокси-серверов.
![](https://habrastorage.org/webt/b3/hg/t0/b3hgt0upzhdrmslkix08w_hjd80.png)
Для использования этой возможности добавили новый префикс секрета — «ee». Кроме того, добавили возможность кодировать секрет в адресе прокси сервера как base64, в дополнение к hex.
Перед тем, как углубиться в детали, попробуем разобраться как развивалась поддержка прокси-серверов в Telegram.
Предыстория
- Сначала Телеграм поддерживал SOCKS-прокси. Это помогало обойти блокировку серверов по IP, но протокол был заметен в трафике, а пароль передавался в открытом виде
- Примерно год назад выпустили официальный прокси, работающий по новому протоколу MTProto. В отличие от SOCKS, пароль в MTProto не передавался в открытом виде. В протоколе избавились от каких-либо служебных заголовков, по которым можно было бы понять что это действительно он. Ещё добавили возможность показа рекламы пользователям прокси-сервера
- Оказалось что прокси-серверы, работающие по протоколу MTProto, можно обнаружить по длине пакетов. При установке соединения клиент и прокси сервер обмениваются пакетами определённой длины, а при работе — пакетами одной и той же длины по модулю 4. Эта особенность начала использоваться крупными провайдерами для блокировки мессенжера. Разработчики Telegram отреагировали на это модификацией протокола, добавив в каждый пакет некоторое количество случайных байт. Так как изменение ломало совместимость, пришлось дополнить формат секрета специальным префиксом «dd», означающим использование модифицированного протокола:
tg://proxy?server=178.62.232.110&port=3256&secret=dd00000000000000000000000000000000
- При изучении особенностей блокировок прокси серверов в Китае и Иране выяснилось, что надзорные органы используют для детекта replay-атаки. В альтернативных реализациях прокси-серверов на Python, Erlang и Go появилась частичная защита от такого вида атак. Для этого прокси серверы запоминают данные, передающиеся на начальном этапе установки соединения и не дают повторно соединиться с такими же данными. У подхода есть проблема с крупными прокси-серверами, т.к. для запоминания требуется большое количество оперативной памяти
- В Китае и Иране применяют следующую тактику: если протокол неизвестен, то на всякий случай скорость его работы сильно режут. На практике это означает возможность использования Telegram только для передачи текстовых сообщений, без картинок и видео. Причём в Китае так умели делать давно, а в Иране научились относительно недавно. В России пока не научились, но закон уже приняли. Попытка разработчиков мессенжера замаскировать трафик под какой-нибудь популярный протокол на этом фоне выглядит закономерно.
Что изменилось?
В протоколе между клиентом Telegram и прокси-сервером добавили ещё один слой инкапсуляции поверх TCP. Вместо посылки данных по TCP, данные оборачиваются в записи TLS следующего вида:
![](https://habrastorage.org/webt/re/ye/gr/reyegr-9iptayjtr6hyruhcm56y.png)
В начале работы добавился этап эмуляции TLS-handshake. Пакет от клиента к прокси-серверу имеет такую структуру:
![](https://habrastorage.org/webt/c7/cw/2-/c7cw2-da2powgfq1_zrx8iellbs.png)
Почти все поля не имеют для клиентов Telegram смысла и нужны лишь для того чтобы прикидываться TLS. Самая важную функцию несёт поле Random, куда помещается результат HMAC от общего секрета и данных в пакете, что позволяет доказать клиенту что он знает секрет. Также, клиент ксорит последние 4 байта поля Random со своим временем в формате unixtime, что позволяет прокси-серверу определять когда был сгенерирован пакет. Это полезно для защиты от replay-атак. Если пакет сгенерирован давно или в будущем, то прокси-сервер может его сразу отбросить.
При подключении клиента, прокси сервер проверяет переданный HMAC. Если он совпадает с вычисленным, прокси отвечает пакетом со следующей структурой:
![](https://habrastorage.org/webt/to/d_/z0/tod_z0oiokru5bd6lsctypp5m_a.png)
Поле Random в нём также не является случайным, а является результатом HMAC от общего секрета и данных в пакете, причём при вычислении HMAC, случайное значение, отправленное клиентом приписывается перед данными самого пакета. При передаче самих данных, первая посылка игнорируется клиентом, что позволяет послать ему данные случайной длины, для дополнительного усложнения обнаружения.
Где попробовать?
Для демонстрации был доработан и поднят прокси-сервер на языке Python, к которому можно подключаться десктопным клиентом Telegram последних версий и смотреть передающийся трафик с помощью Wireshark:
tg://proxy?server=178.62.232.110&port=3256&secret=7gAAAAAAAAAAAAAAAAAAAABnb29nbGUuY29t
Также, поддержка маскировки под TLS была добавлена в прокси-сервер на языке Erlang. Скорее всего, в ближайшее время, данную функциональность добавят и в другие реализации прокси-серверов.