Полный технический анализ MITM в клиенте Telega
Утром 18 марта создатели приложения Telega активировали скрытую функциональность, позволяющую им перехватывать все данные между их приложением и сервером Telegram, пропуская их через свои сервера.
К сожалению, информации об этом мало, и поэтому была написана эта статья с подробным, повторяемым анализом данного механизма.
Те, кому не очень интересны технические детали, но хочется узнать о ситуации больше — читайте "TL;DR" в конце каждого параграфа.
Background о шифровании и клиент-серверном протоколе Telegram
Telegram имеет несколько серверов в разных регионах мира — у каждого по одному или несколько IP-адресов и внутри Telegram они называются DC (дата-центры). Например, у DC2 в Амстердаме, к которому относятся все пользователи из России, имеет IP-адрес 149.154.167.51. (источник)
Когда клиент Telegram устанавливает подключение к DC, он генерирует случайные параметры шифрования и передает их в зашифрованном виде с помощью алгоритма RSA — в клиент вшит публичный ключ, которым клиент шифрует данные, а затем сервер, с помощью соответствующего приватного ключа их расшифровывает.
Другими словами, клиент Telegram использует публичный, заранее известный ключ RSA для установления первичного шифрованного соединения с сервером, чтобы сгенерировать и договориться об общем, новом ключе шифрования (для AES). (источник)
TL;DR: в клиент Telegram вшиты IP-адреса серверов мессенджера и ключи шифрования для взаимодействия.
Как Telega вмешивается в это взаимодействие — подмена IP-адресов
Разберем последнюю на данный момент версию клиента Telega для Android (sha256 ca47b6..6d34f0) и распакуем через jadx.
Пишем в поиске по файлам dc-proxy и находим следующие куски кода:
ru/dahl/messenger/dc/DCRestService.java:
public interface DCRestService {
@GET("dc-proxy")
Object getDcConfig(Continuation<? super DcConfig> continuation);
}ru/dahl/messenger/data/rest/RestClient.java:
public static final String API_URL = "https://api.telega.info/v1/";Можно сделать вывод, что приложение совершает HTTP GET запрос по ссылке https://api.telega.info/v1/dc-proxy, которая возвращает JSON-объект с параметром { "dc_version": 2 }, а так же массив dcs следующего формата:
{ "dcs": [{ "id": 2, "addresses": [{ "host": "130.49.152.41", "port": 443}] }] }Где id имеет значение от 1 до 5 (соответствуя номерам DC Telegram), а все IP-адреса находятся в диапазоне 130.49.152.0/24 и принадлежат AS203502 JOINT STOCK COMPANY "TELEGA", которая была зарегистрирована 24 ноября 2025 года.
Интересный факт — единственный апстрим данной AS - AS47764 LLC VK (Mail.ru), и им же принадлежит соседняя подсеть 130.49.224.0/19. Это косвенно указывает на то, что Telega — дочерний проект VK. Непонятно, откуда у маленького стартапа из Казани средства на организацию своего AS и покупку целого /24 блока IP-адресов.
То есть, приложение получает IP-адреса контроллируемых Telega серверов, которые должны заменить адреса настоящих серверов Telegram.
Затем, вызывается метод applyDcVersion() из класса DCAuthHelper.java:
public final Object applyDcVersion(
final int r17, // dcVersion (2)
final java.util.List<ru.dahl.messenger.dc.entity.Datacenter> r18, // адреса серверов Telega
final ru.dahl.messenger.dc.entity.DcOptions r19,
final boolean r20,
kotlin.coroutines.Continuation<? super kotlin.Unit> r21
)Внутри этого метода, вероятно, идет вызов публичного метода ConnectionsManager.setDcVersion с новыми адресами:
// ConnectionsManager.java:1934-1935
public void setDcVersion(int version, int[] dcIds, String[] addresses,
int[] ports, boolean[] flags, boolean usePfs,
String[] transports) {
native_setDcVersion(this.currentAccount, version, dcIds, addresses,
ports, flags, dcIds.length, usePfs, transports);
}На это косвенно указывает полу-декомпилированный код в DCAuthHelper.java:379, который проверяет, что значение dcVersion изменилось на нужное:
ConnectionsManager r7 = ConnectionsManager.getInstance(r7);
int r7 = r7.getDcVersion();
int r8 = this.$dcVersion;
if (r7 != r8) { /* not yet */ }Итого мы получаем примерно такую цепочку вызовов:
Обратите внимание: вызов AuthTokensHelper.clearLogInTokens() при смене режима не удаляет auth_key (ключ шифрования сессии), этим занимается другая функция, мы описали её ниже.
TL;DR: Клиент Telega подменяет IP-адреса серверов Telegram на свои собственные по "команде" с их сервера.
Подмена ключа шифрования RSA
Но без подмены ключа шифрования атака MITM невозможна. Чтобы найти RSA ключ Telega, нужно декомпилировать динамическую библиотеку libtmessages.49.so, которая хранится в этом же APK-файле. Именно эта библиотека реализует методы native_*, используемые в классе ConnectionsManager.
Открываем IDA Pro и скармливаем ей libtmessages.49.so, в данном случае вариант для arm64. Нажимаем Shift+F12, откроется список найденных текстовых строк. Ищем все ключи по заголовку "BEGIN RSA PUBLIC KEY" (Ctrl+F):
Ключ №1 по адресу 0x1576FFC:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAyr+18Rex2ohtVy8sroGPBwXD3DOoKCSpjDqYoXgCqB7ioln4eDCF
fOBUlfXUEvM/fnKCpF46VkAftlb4VuPDeQSS/ZxZYEGqHaywlroVnXHIjgqoxiAd
192xRGreuXIaUKmkwlM9JID9WS2jUsTpzQ91L8MEPLJ/4zrBwZua8W5fECwCCh2c
9G5IzzBm+otMS/YKwmR1olzRCyEkyAEjXWqBI9Ftv5eG8m0VkBzOG655WIYdyV0H
fDK/NWcvGqa0w/nriMD6mDjKOryamw0OP9QuYgMN0C9xMW9y8SmP4h92OAWodTYg
Y1hZCxdv6cs5UnW9+PWvS+WIbkh+GaWYxwIDAQAB
-----END RSA PUBLIC KEY-----Ключ №2 по адресу 0x15788E1:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAum9pZNEIWVt6jQUm/qcP4na0RgWHfSls/TJwxYQTsruNyuVgdrBu
y7gbNcObgnxmjxohwRjkNCOASwfYOD5yZ0UUqlg+iK84cmS8HdSublM/Bvf4huqN
7RZ0GXQ8nGCZQFQ67ZqXS5R/4XNUmoj5kmhHOl7OU4ow3DXdjM3JEmvaVtacGoMW
BT2s1JtTt3bXVJmarBxt3g8yn+lmAs7aCZkVj0cdocHT7jOyPaCtvSC+pGThr7qA
aDEWl2q8Z4fH1hYF3xrm4vxraJq4fFIbuBLceMKfHsI7ahL4KLF/tYNNZzbfaE5s
4Z2HPiEI+78hAdxCWAnQd9Efj2Dbc6OM2wIDAQAB
-----END RSA PUBLIC KEY-----Ключ №3 по адресу 0x1578A8B:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAyMEdY1aR+sCR3ZSJrtztKTKqigvO/vBfqACJLZtS7QMgCGXJ6XIR
yy7mx66W0/sOFa7/1mAZtEoIokDP3ShoqF4fVNb6XeqgQfaUHd8wJpDWHcR2OFwv
plUUI1PLTktZ9uW2WE23b+ixNwJjJGwBDJPQEQFBE+vfmH0JP503wr5INS1poWg/
j25sIWeYPHYeOrFp/eXaqhISP6G+q2IeTaWTXpwZj4LzXq5YOpk4bYEQ6mvRq7D1
aHWfYmlEGepfaYR8Q0YqvvhYtMte3ITnuSJs171+GDqpdKcSwHnd6FudwGO4pcCO
j4WcDuXc2CTHgH8gFTNhp/Y8/SpDOhvn9QIDAQAB
-----END RSA PUBLIC KEY-----Ключ №4 по адресу 0x1578C35:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA6LszBcC1LGzyr992NzE0ieY+BSaOW622Aa9Bd4ZHLl+TuFQ4lo4g
5nKaMBwK/BIb9xUfg0Q29/2mgIR6Zr9krM7HjuIcCzFvDtr+L0GQjae9H0pRB2OO
62cECs5HKhT5DZ98K33vmWiLowc621dQuwKWSQKjWf50XYFw42h21P2KXUGyp2y/
+aEyZ+uVgLLQbRA1dEjSDZ2iGRy12Mk5gpYc397aYp438fsJoHIgJ2lgMv5h7WY9
t6N/byY9Nw9p21Og3AoXSL2q/2IJ1WRUhebgAdGVMlV1fkuOQoEzR7EdpqtQD9Cs
5+bfo3Nhmcyvk5ftB0WkJ9z6bNZ7yxrP8wIDAQAB
-----END RSA PUBLIC KEY-----Теперь скачаем последний релиз Telegram для Android с официального сайта (https://telegram.org/dl/android/apk), распакуем APK обычным архиватором, и скормим IDA Pro такую же библиотеку (sha256 5ebcea..0176c9) и сравним ключи.
Оказывается, в оригинальной библиотеке от Telegram есть только 3 из 4 ключей, которые зашиты в клиент Telega:
| Адрес ключа в Telega | Адрес ключа в Telegram | SHA-256 |
|---|---|---|
0x1576FFC |
0x15704DC |
76f57758..4583493e |
0x15788E1 |
- | 7f7d5bd9..104f3fe1 |
0x1578A8B |
0x1571CAE |
2652db36..10beb77f |
0x1578C35 |
0x1571E58 |
abaec5de..041348db |
Хэш ключа генерировался следующей командой:
openssl rsa -in pub.pem -pubin -outform DER 2>/dev/null | openssl dgst -sha256
В итоге выходит, что в Telega был добавлен ключ №2 по адресу 0x15788E1. Чтобы долго не копаться в сурсах библиотеки, устроим простой тест — попробуем установить соединение с сервером DC 2 от Telega и сервером DC 2 от Telegram, используя ключ, добавленный создателями Telega.
Попросим Claude Opus написать скрипт, который попытается инициировать первичное рукопожатие с сервером MTProto, непосредственно скрипт: mtproto_handshake_test.py. Можно попросить любой ИИ объяснить, как он работает.
Запустим этот скрипт сначала с адресом официального DC2 Telegram:
$ python3 mtproto_handshake_test.py 149.154.167.50
Key fingerprint: 0x2c945714333b5ebd (2048-bit)
Server: 149.154.167.50:443 (DC 2)
Server fingerprints: ['0xd09d1d85de64fd85', '0x0bc35f3509f7b7a5', '0xc3b42b026ce86b21']
Our fingerprint in list: False
RESULT: transport error -404
The server could not decrypt our payload.
⇒ Server does NOT hold the private key for this RSA key.Скрипт сообщает, что сервер Telegram предлагает свои ключи, к которым ключ Telega не относится. При попытке установить соединение сервер отправляет ошибку транспорта -404, так как не может дешифровать наш запрос, зашифрованный неизвестный ему ключом.
Теперь то же самое, только с сервером DC2 Telega:
$ python3 mtproto_handshake_test.py 130.49.152.41
Key fingerprint: 0x2c945714333b5ebd (2048-bit)
Server: 130.49.152.41:443 (DC 2)
Server fingerprints: ['0x2c945714333b5ebd']
Our fingerprint in list: True
RESULT: server_DH_params_ok
The server successfully decrypted our payload.
⇒ Server HOLDS the private key for this RSA key.Сервер Telega предложил нам этот же ключ и успешно завершил рукопожатие.
TL;DR: В Telega добавлен дополнительный, четвертый публичный ключ RSA, который не принимает сервер Telegram, но принимает сервер Telega.
Что дает подмена сервера и ключа шифрования?
Как было описано в начале статьи, шифрование RSA между клиентом и сервером Telegram используется при рукопожатии (handshake) для генерации нового ключа шифрования, с помощью которого затем шифруется весь трафик.
Из-за того, что рукопожатие проводится один раз при первичном установлении соединения клиента и сервера (например, если пользователь только что установил себе клиент Telegram и еще даже не вошел в аккаунт), в Telega был встроен еще один механизм — по команде с сервера Telega клиент может инициировать выход из аккаунта, стирая все данные о связи с сервером, включая общий ключ шифрования.
Метод DCEventHandler.performSoftLogoutForAllAccounts:
public final void performSoftLogoutForAllAccounts() {
try {
int maxAccountCount = getBridge().getMaxAccountCount();
for (int i = 0; i < maxAccountCount; i++) {
if (getBridge().isClientActivated(i)) {
getBridge().logout(i);
}
}
Napier.d$default(Napier.INSTANCE, "DC Event: soft logout completed for all accounts", null, null, 6, null);
} catch (Exception e) {
Napier.e$default(Napier.INSTANCE, "DC Event: error during soft logout", e, null, 4, null);
}
}Метод TelegramBridge.logout:
public void logout(int i) {
clearDismissedPromos();
UserConfig.getInstance(i).clearConfig(); // удаляет сессию и ключ шифрования (auth_key)
MessagesController.getInstance(i).performLogout(0); // отправляет серверу сигнал о выходе из аккаунта
ConnectionsManager.getInstance(i).cleanup(true); // убивает все соединения
}Данный механизм вызывается несколькими способами:
- Скрытым push-уведомлением от сервера Telega c полями
type=dc_update_versionиforce_relogin=true(См. классTelegaPushHandler) - Скрытым push-уведомлением от сервера Telega c полями
type=dc_force_switchиforce_reconnect=true(См. классTelegaPushHandler) - При переходе по ссылке
tg://dc_event?force_relogin=trueилиtg://dc_event?force_reconnect=true(См. классDCDeepLinkHandler) - Промо-баннер с предложением о "миграции"
(См. классы
PromoRestClient,DahlBannerCell,DCMigrationHelper)
Последний механизм показывает пользователю промо-баннер со следующим текстом:
Перезайдите в приложение, чтобы ускорить соединение
Мы переходим на выделенные серверы для максимальной скорости и стабильности звонков, чатов и загрузки медиа. Вам нужно только перезайти в свой аккаунт.
Мы не знаем, показывался ли данный баннер при активации MITM 18 марта, но по данному сообщению видно, что Telega ведёт себя как обычное вредоносное ПО — предлагает пользователю скорость и стабильность, а под капотом ворует данные от их аккаунтов Telegram и устанавливает постоянную "прослушку".
Так как Telega со своим dc-proxy контроллирует хэндшейк, это означает, что они могут провести классическую атаку MITM (Man-in-the-middle, "человек посередине") — договориться с клиентом об одном ключе шифрования, а с настоящим сервером Telegram о другом ключе шифрования, и, будучи посередине между клиентом и сервером, просматривать, сохранять и модифицировать весь трафик.
TL;DR: Telega может, без вашего ведома
- Читать все входящие и исходящие сообщения в любом чате;
- Просматривать всю историю сообщений в любом чате;
- Подменять контент сообщений — например, блокировать неугодные каналы по причине "нарушения правил Telegram" (не Telega!);
- Хранить все ваши данные и действия в Telegram и передавать их третьим лицам — особенно правоохранительным органам;
- Выполнять абсолютно любые действия с вашим аккаунтом без вашего участия.
Отключение Perfect Forward Secrecy
PFS (Perfect Forward Secrecy — «совершенная прямая секретность») — это механизм защиты, который гарантирует: даже если кто-то однажды получит ваш ключ шифрования, он не сможет прочитать старые переписки.
В MTProto это реализовано так: вместо использования "долгосрочного", не меняющегося ключа auth_key, клиент и сервер генерируют временный ключ шифрования temp_auth_key примерно раз в 1-2 суток и шифруют весь трафик с помощью него.
В официальных клиентах Telegram флаг об использовании этой опции захардкожен в значение true во время сборки и не может быть изменён.
На контрасте, в приложении Telega этот флаг по умолчанию выключен, а его состояние контроллируется сервером Telega через тот же самый эндпоинт /dc-proxy.
Тот же метод https://api.telega.info/v1/dc-proxy имеет объект options, который может контролировать это (сейчас он пустой), но по умолчанию, если параметр use_pfs отсутствует, PFS выключен.
На это указывает флаг DcConfig.options.use_pfs (Boolean?, JSON: "use_pfs"), далее он используется в методе DCRepository.handleDcConfig():
DcOptions options = dcConfig.getOptions();
boolean usePfs = false; // ← выключено по умолчанию
if (options != null && options.getUsePfs() != null) {
usePfs = options.getUsePfs().booleanValue();
}Затем в методе DCAuthHelper.applyDcVersion():
boolean usePfs = dcOptions != null ? dcOptions.getUsePfs() : false;
ConnectionsManager.setDcVersion(
dcVersion=2,
dcIds, hosts, ports, ipv6,
usePfs, // ← переключатель PFS
transports
);Метод ConnectionsManager.setDcVersion затем передает этот флаг в нативный метод native_setDcVersion.
TL;DR: В Telega по умолчанию отключена дополнительная защита Telegram от перехвата сообщений и ключей шифрования.
Отключение секретных чатов
Секретные чаты в Telegram представляют из себя чаты с End-to-End шифрованием, что позволяет даже серверу не знать содержимого сообщений. Ключи для секретных чатов никогда не покидают устройство.
Telega получает Remote Config через Firebase каждый час и обрабатывает в классе FeatureManager. Текущий Remote Config с сервера сейчас выглядит так — секретные чаты выключены флагом enable_sc = false :
{
"entries": {
"ads_control": "true",
"autosubscribe_channel": "true",
"chat_invite_friend_modal": "false",
"chat_invite_sticky_banner": "false",
"connection_no_vpn_mode": "true",
"connection_settings": "true",
"connection_stable_calls": "true",
"contact_list_invite_friend": "true",
"dialogs_invite_friend_button": "false",
"enable_sc": "false",
"group_video_calls": "false",
"invite_friend": "false",
"moderation_enabled": "false",
"p2p_video_calls": "true",
"parental_control_core": "true",
"parental_control_menu_item": "false",
"profile_invite_friend_item": "true",
"settings_invite_friend_item": "true",
"sidemenu_invite_friend": "true",
"telega_calls": "true",
"telega_group_calls_attach": "false",
"telega_group_calls_chat": "false",
"telega_p2p_calls": "true",
"telega_wall": "true",
"telegram_call_fallback": "false",
"waitlist": "none",
"waitlist_enabled": "true"
},
"state": "UPDATE",
"templateVersion": "472"
}Флаг enable_sc обрабатывается классом FeatureManager и он используется в логике обработки секретных чатов в следующих местах:
Метод SecretChatHelper.acceptSecretChat:
public void acceptSecretChat(final TLRPC.EncryptedChat encryptedChat) {
if (this.acceptingChats.get(encryptedChat.id) == null && FeatureManager.currentInstance().isSCEnabled()) {
// логика "принятия" запроса на начало секретного чата
}
}Так как FeatureManager.currentInstance().isSCEnabled() возвращает false, входящие секретные чаты тихо игнорируются клиентом Telega и пользователь об этом не узнает.
Помимо этого, Telega скрывает кнопку "Начать секретный чат" и игнорирует deep link ссылки на секретные чаты.
TL;DR: Telega может отключать секретные (end-to-end encrypted) чаты по команде со своего сервера — при этом по умолчанию они уже отключены. Обычный пользователь даже не узнает, если кто-то попытается написать ему в секретном чате.
Cистема "модерации"
Telega может влиять на контент внутри приложения даже без использования MITM-атаки, описанной выше. Внутри приложения есть функциональность "чёрных списков", которые работают отдельно от похожих механизмов в Telegram, и позволяет запретить пользователям Telega открывать определённые каналы, переписки с чат-ботами и даже личные сообщения с определёнными пользователями.
Раннее создатели Telega объяснили эту функциональность "родительским контролем" (якобы, родитель может запретить ребёнку открывать то, что запрещено), но просмотр исходного кода показывает, что "чёрные списки" существуют отдельно и применяются глобально для всех пользователей.
Как это работает:
- Настройка для каждого пользователя
blacklist_filter_enabled(ключ JSON вTelegaUserConfig, полученный из собственного API настроек Telega, по умолчанию:false) - При включении каждое открытие чата/профиля/истории вызывает:
POST https://api.telega.info/v1/api/blacklist/filter Body: { "targets": [{ "type": "user|channel|chat|bot", "id": 123456 }] } Response: { "blacklisted": [{ "type": "user", "id": 123456 }] } - Если цель в черном списке → отображается
BlacklistedOverlay, контент полностью скрыт - Результаты кэшируются локально в
moderation_listSharedPreferences
При этом в BlacklistedOverlay отображается следующий текст:
Материалы недоступны Этот [чат/канал/бот] недоступен в связи с нарушениями правил платформы
создавая у пользователя впечатление, что контент был заблокирован администрацией Telegram (платформы), а не жуликами-админами Telega (всего лишь безобидный клиент от маленького стартапа из Казани).
Где конкретно применяется эта логика:
| Местоположение | Эффект |
|---|---|
ChatActivity.checkIsBlacklisted |
блокирует открытие чата |
ProfileActivity.checkIsBlacklisted |
блокирует просмотр профиля |
PeerStoriesView.checkIsBlacklisted |
блокирует просмотр историй |
Ключевые классы:
ru.dahl.messenger.data.rest.ModerationService→ Делает запрос HTTPPOST api/blacklist/filterru.dahl.messenger.data.repository.ModerationRepository→ Локальный кэш + логика удаленной проверкиru.dahl.messenger.data.entity.BlacklistRequestObject→ Формирует объект запроса{ targets: [{ type, id }] }ru.dahl.messenger.data.entity.BlacklistResponseObject→ Обрабатывает объект ответа{ blacklisted: [{ type, id }] }ru.dahl.messenger.data.entity.TargetType→ Перечисление:USER,CHANNEL,CHAT,BOTru.dahl.messenger.data.entity.TelegaUserConfig→ Конфигурация для каждого пользователя сblacklistFilterEnabledru.dahl.messenger.ui.components.BlacklistedOverlay→ Наложение UI экрана блокировки
TL;DR: Telega может удаленно скрывать любого пользователя, канал, чат или бота от всех пользователей Telega и создавать впечатление, что он заблокирован для всех администрацией Telegram.
Не пользуйтесь клиентом Telega — это Мах на минималках
Надеемся, эта статья послужит исчерпывающим доказательством того, что Telega — на самом деле не безобидный маленький стартап из Казани, а самое настоящее вредоносное ПО, в худшем случае имеющее связи и поддерживаемое государством.
Если кто-то из ваших близких и знакомых пользуется этим клиентом — настоятельно убедите их удалить его и завершить сессию в настройках аккаунта. Один пользователь Telega лишает приватности не только себя, но и всех своих собеседников без их согласия — они даже не знают об этом.
Использование Telega — это примерно то же самое, как если бы вы взяли телефон незнакомого человека, зашли на нем в свой аккаунт Telegram и отдали навсегда этот телефон обратно. А у этого человека есть родственник, который работает в полиции.
Для восстановления работоспособности Telegram пользуйтесь исключительно официальным клиентом и встроенной функцией прокси-серверов. Использование любого другого клиента Telegram точно также ставит ваши персональные данные, аккаунт и устройство под угрозу.
В каком-то смысле Telega даже хуже чем Мах — в государственном мессенджере у вас нет нескольких лет истории переписок со всеми вашими знакомыми, которые можно было бы прочитать, проанализировать, подписаны ли вы на "неугодные" каналы, а самое главное, при использовании Telega вы думаете, что все ваши данные надежно защищены — это же Telegram, а не Мах!