Приветствую тебя, %username%! Ох и давно я не писал ничего на Хабр (10+ лет) — чернила высохли, перо затупилось. И все же, читая последние сводки, мой академический интерес проснулся.
Если вдруг пропустили и не понимаете о чем я, то информационный фон сейчас бурлит: тут и новости про то, как большинство популярных приложений детектируют VPN, и выход утилиты RKNHardering с методичками по борьбе с обходами, и тревожные отчеты о свободе интернета в 2026 году. Но последней статьей которая на меня повлияла стала статья про критическую уязвимость VLESS-клиентов, из-за которой «скоро все ваши VPN будут заблокированы».
Смахнув скупую мужскую слезу, вызванную этим богатым на эмоции потоком, я задумался: а насколько вообще сложно детектируется VPN на Android? Оказалось, что даже с использованием сплит-туннелирования у приложений остается вагон возможностей для детекта (хоть и не 100%, но все же) :-/ .
Что там по признакам VPN?
Для начала я собрал мысли и идеи о том, какие признаки наличия VPN может собирать приложение, и набросал небольшую тестовую тулзу. Смысла ее публиковать особо нет, все то, что она делает, уже есть и в том самом RKNHardering (поэтому и скриншот из нее).

Теория подтвердилась практикой: приложение, находящееся в исключениях VPN, все равно находит его признаки. Оно видит наличие tun интерфейса, видит роуты в системе, список установленных пакетов, а главное — видит поднятый локальный socks на устройстве и может узнать внешний IP VPN сервера. К сожалению, с большинством из этих признаков (вроде списка пакетов или роутов) ничего не поделать, так как Android — достаточно свободная в этом плане ОС. Но вот с открытым socks-портом, который и стал причиной паники в последней статье, побороться можно и нужно.
Идея: прячем концы в воду
Раз разгорелся энтузиазм, надо направить его в нужное русло. В той самой статье про уязвимость VLESS автор справедливо заметил одну пугающую вещь: на данный момент ни один популярный VPN-клиент под Android не маскирует свой локальный socks-порт и дает возможность узнать внешний IP. Все они (v2RayTun, V2BOX, v2rayNG, Hiddify и прочие) светят им наружу, как маяком.
Как обычно устроены клиенты? Поднимается ядро, которое открывает локальный socks-прокси, а трафик системы заворачивается в него.
Чтобы закрыть этот socks от любопытных глаз других приложений, нужно сделать так, чтобы прокси поднимался на случайном порту и был запаролен случайными кредами, которые генерируются на лету и известны только самому приложению-клиенту. В итоге никакая другая софтина на телефоне (даже если просканирует локалхост) в этот прокси не пробьется и не задетектит внешний IP.
Под капотом: Xray, tun2socks и обход ограничений ОС
С VLESS все оказалось просто: есть проект xray-core, билды которого существуют под множество платформ. Бери бинарники, запускай из приложения, пользуй socks. Но хочется ведь иметь на устройстве полноценный сетевой интерфейс (tun).
Сам Xray умеет в туннель, однако на практике оказался крайне капризным в этом плане. Сколько я ни пытался поднять нормальный туннель его родными средствами, всегда всплывали какие-то подводные камни. Идти в гору оказалось непросто, поэтому было принято тактическое решение поискать альтернативы.
И альтернатива нашлась — tun2socks. Официального билда для Android у него нет, но статически собранный бинарник linux-arm64 заводится на зеленом роботе без каких-либо проблем. Получается, все инструменты есть, нужна только обертка, которая всё это свяжет.
Архитектурно клиент написан на солянке из Flutter, Kotlin и чуть-чуть C++. Что-то делегировал ИИ — и регулярно бил по рукам, когда тот пускался во все тяжкие. В итоге многое все равно писал сам: иногда реально проще сделать ручками, чем добиться от куска кремния выполнения своей задумки.
Отдельно стоит упомянуть запуск самих бинарников. Начиная с Android 10, система строго бьет по рукам за запуск исполняемых файлов из data-директории приложения (W^X violation). Чтобы обойти это ограничение, я упаковал исполняемые файлы в lib/ как .so библиотеки — старый добрый трюк, который отлично работает.
Вместо простыней кода просто покажу кусок лога, где отлично видно, как происходит динамическая генерация и связка:
[2026-04-12T09:44:46.641632] [INFO] Starting VPN [2026-04-12T09:44:46.641818] [INFO] Underlying network set: 176 [2026-04-12T09:44:46.641946] [INFO] nativeSetMaxFds result (parent): 0 [2026-04-12T09:44:46.643867] [INFO] TUN established, dup fd=142 [2026-04-12T09:44:46.644004] [INFO] xray started [2026-04-12T09:44:46.644130] [INFO] Starting tun2socks (native): /data/app/~~_xfukaXUtA4X7kd1BmB4UA==/com.teapodstream.teapodstream-zTL0GGuDWxPk8XDn0VkAiw==/lib/arm64/libtun2socks.so -device fd://142 -proxy socks5://upu6CHvvV:ZJC3GMDXKOLFdPKAfxBxrHiU@127.0.0.1:45478 -mtu 1500 -loglevel error -tcp-sndbuf 524288 -tcp-rcvbuf 524288 -tcp-auto-tuning [2026-04-12T09:44:46.644247] [INFO] tun2socks started (pid=28880) [2026-04-12T09:44:46.644399] [INFO] VPN connected
Обратите внимание на строку запуска tun2socks: порт 45478 и забористые логин/пароль генерируются на лету при каждом старте соединения. Файловый дескриптор tun интерфейса (fd://142) пробрасывается напрямую в бинарник.
Итоги
И вот я таки родил работающее приложение — TeapodStream (отсылка к HTTP-коду 418 + намек на туннель/прокси/поток).

Главный вопрос: работает ли защита? Да. Проверка на тех самых приложениях-детекторах из статьи про уязвимость VLESS, а также тесты через RKNHardering показали, что они перестали детектировать открытый socks. Прокси и внешний IP спрятан.
Функционал пока тривиальный, но покрывающий базу:
Поддержка протоколов: VLESS, VMess, Trojan, Shadowsocks.
Раздельное туннелирование (сплит-туннелинг) — выбор приложений для исключения из VPN.
Подписки — автоматическое обновление конфигураций по URL.
И прочие необходимые мелочи.
Приложение с исходниками лежит на GitHub: https://github.com/Wendor/teapod-stream
По итогу я доволен: мозг размял, проблему решил, а раз вы читаете эту статью — значит, в этот раз я поработал не только на личный опыт, но и осмелился поделился результатом с сообществом)

Напоследок хочу спросить сообщество: как думаете, какие еще неочевидные маркеры наличия VPN могут начать массово использовать для детекта в ближайшем будущем?
Всем бобра!
UPD. curl --interface tun0 пофиксил в 1.1.0, лежит в пререлизах. Написал свою tun2socks реализацию, которая при открытии подключения сверят от кого пришел пакет, а если пакет прошел минуя api андроида - дропается.
Комментарии (154)

Gambit132
12.04.2026 12:13М.б. внешние поведенческие, через счетчики аналитик завязанных на идентификации, вроде Я.Метрика?

Wendor Автор
12.04.2026 12:13Вот да. Тоже кажется начнут на страницах стучать на заграничные сервера и сверять ip.

alex_1065
12.04.2026 12:13Там уже не кажется, там либо уже начали либо вот вот начнут, проблем то минимум, добавить java скипт который будет делать запрос к любому узлу из этих:
https://api.ipify.org?format=jsonлибо свой собственный аналог на зарубежном vps поднимут и сохранять данные на сервере что-то типа (по токенам грока):

В firefox на этот случай есть (в остальных браузерах понятия не имею есть ли аналоги):



Нужные странички в "личное", спайваре в "покупки" или куда пожелаете и пускай стучатся

Gambit132
12.04.2026 12:13Яндекс фингерпринты при случае делает, собирая данные вплоть до названий Wi-Fi сетей (BSSID) и гео. Прокси не спасут, если отпечаток уплыл через Яндекс.Браузер или скрипты Яндекс приложений. Идентификация идет через склейку сессий с Яндекс айди (Canvas, WebGL, шрифты и+100500 условных параметров).

alex_1065
12.04.2026 12:13тем кто пользуется яндекс браузером по моему скромному мнению внп вообще противопоказан. Кстати подозреваю что им как раз хорошо зайдет и мессенджер "телега"

Gambit132
12.04.2026 12:13Я.Бразуер это часто один рабочий метод выхода для пожилых людей на стареньких устройствах, посмотреть тот же ютуб. Даже выкиним Я.Браузер, есть Я.Гоу, например и еще кучу сервисов от Яндекса, которые тупо висят в телефоне.

alex_1065
12.04.2026 12:13я извиняюсь, но то что я писал выше не про телефон, а про компьютер, на телефоне все сложнее там чтобы использовать прокси (не впн) нужно даже не firefox ставить, а его версию firefox nightly (хорошо хоть от самих же разработчиков firefox).

max9
12.04.2026 12:13с чего бы яб будет отлично от хрома работать на старых устройствах? движок один и тотже и кушает также

Gambit132
12.04.2026 12:13Я тоже раньше не понимал в чем причина и считал домыслами, пока сам для теста не проверил на слабых устройствах и "плохом" инете. Тут дело не в движке а в каскаде оптимизаций:
1. Агрессивная выгрузка вкладок. Я.Браузер замораживают неактивные вкладки гораздо быстрее и жестче. Это предотвращает ситуацию, когда браузер занимает 90% памяти.2. Оптимизация интерфейса. Внезапно, для старых версий винды и ведроидов у Яндекса есть встроенные механизмы снижения нагрузки на GPU при отрисовке интерфейса.
3. Вшитые кодеки. Я.Браузер часто юзает аппаратное ускорение для видео, а не программное.
4.Турбо. Когда скорость падает, страницы проходят через сервера-посредники, где картинки и видео сжимаются, а лишние скрипты отсекаются.
5. Локальные DNS и CDN. В России и СНГ у Яндекса тупо больше узлов выдачи контента.
6. Встроенный Protect в Я.Браузере блокирует крупные баннеры и видеовставки на уровне ядра.
Причем, заметно это только на слабых устройствах.

edo1h
12.04.2026 12:13уже комментарии начали с помощью llm писать?

Gambit132
12.04.2026 12:13Нет, но любопытно, почему вы так посчитали? Вряд-ли бы LLM так криво бы написала=)
Закинул коммент свой в Геминьку, результаты не утешительны:В этом тексте5 явных ошибок(грамматических, пунктуационных и оформительских), не считая использования сленга и пренебрежения буквой «ё»..

edo1h
12.04.2026 12:13«Тут дело не в движке а в каскаде оптимизаций» и длинный список за ним звучит как-то по-машинному

Reternos
12.04.2026 12:13Я тоже о подобном читал, не знаю как сейчас дела обстоят, но в 2020 я как-то скачал Я.Браузер на очень очень старом компе и приговор был неутешительным, комп лагал, тормозил, а при включенном Я.Браузере и других приложениях комп невозвратно зависал.

Gambit132
12.04.2026 12:13Надо будет более детально разобраться в этом вопросе, т.к. мне "очень повезло", что через знакомых просят "посмотреть" древнюю технику их родителей. Подозреваю, что скорее всего, упрется в поддерживаемые инструкции конкретного проца... А учитывая что творит Яндекс, ну их в сторону осеннего леса.

litalen
12.04.2026 12:13CORS не даст. Запрос-то они сделают, а вот ответ им прочитать браузер не даст.

Heggi
12.04.2026 12:13Делаешь сервер за бугром с правильными политиками CORS и принимаешь соединения от сайтов...

Samogonshik
12.04.2026 12:13Ну при сплит нужно чтобы этот сервер прописали у себя в конфигах пользователи. Ну или пытаться попасть в чью-то подсеть

Heggi
12.04.2026 12:13В чью-то подсеть попасть, наверное, не так сложно (тот же cloudflare сейчас почти полностью через квн ходит)

Vindicar
12.04.2026 12:13Если сервер их, просто сольют нужную инфу в заголовках запроса. Сервер запрос примет и сделает что надо, а ответ в этом случае и не требуется, egress уже выполнен.

gotch
12.04.2026 12:13Но зачем нам ещё один vpn-клиент? Могли бы вы сделать форк, например, Nekobox без socks? А в идеале чтобы он ещё мог использовать плагин Naiive без открытого +одного порта socks?
А там может вы бы влили эту функциональность в основную ветку.

WinPooh32
12.04.2026 12:13Для Nekobox уже предложили трюк для блокировки запросов по socks5, надо добавить кастомное правило в Маршруты:
{ "inbound": ["mixed-in", "socks-in"], "outbound": "block" }
gotch
12.04.2026 12:13Да, это рабочий вариант, но там есть ещё конфигурация, закрывающая tun0.
Только, как оказалось, плагин Naiive (если используется) добавляет дополнительный открытый socks5, он уже не прикрывается.

LM-ML
12.04.2026 12:13я вообще мало понимаю в подобном и потому решил спросить тут. вот я пользовался обновленным и (на телефоне) Happ и каринг и этой прогой с этой статьи, но... хз на счет других прог но вот чтобы я не делал приложение личный кабинет мегафон всегда просит отключить ВПН. получает оно видит что ВПН включен и значит ТЕМА не работает так?

Wendor Автор
12.04.2026 12:13Основной посыл в том, что vpn имеет кучу косвенных признаков на android. Главная проблема - утечка вашего внешнего ip. Вы можете добавить приложение в исключения впн клиента, но оно все равно может задетектить внешний ip - клиент в статье борется именно с этим.

alex_1065
12.04.2026 12:13сам андроид устроен так что при включении любого стороннего сетевого интерфейса появляется значек - VPN. Насколько я в курсе это не поборимо без доступа к ядру (т.е. рута).
Альтернатива - использовать прокси т.е. 127.0.0.1:XXXX это в системе не светится, но приложения и веб странички с помощью JS могут определить его наличие и соответственно куда он ведет.
Ox2A
12.04.2026 12:13Ну, справедливости ради, по крайней мере на xiaomi включенный впн из 2го пространства в 1е не светит значком.

alex_1065
12.04.2026 12:13Если кому интересно насчет настроек браузера firefox, мультиконтейнера с раздельным прокси и перекрытия доступа js со страничек открытых в напрямую (без контейнера с настроенным прокси) к сканированию прокси сервисов, пишите расскажу.

zartarn
12.04.2026 12:13напоминает спам в телеге “есть методички по … кому интересно пишите в лс” хД лучше уж напишите статью :)

enchained
12.04.2026 12:13Было бы отлично такое иметь в формате статьи. Я пока только узнала о существовании опции “Block Outsider Intrusion into LAN” в uBlock Origin, но не нашла готового детектора, на котором можно проверить эффективность защиты.
Ну и в целом все сейчас про мобилки пишут, там конечно основная боль, но хотелось бы и про пк послушать. Там много возможностей делать сплит (браузеры с прокси-экстеншенами, WireSock, Proxifier/ProxiFyre/ProxyBridge, Clash Verge Rev, Karing итд на sing-box), но хочется найти максимально подходящий ситуации вариант.
А конкретно по браузерам еще волнуют вопросы "как защититься от вражеского пикселя на страничке ру-зоны, который хостится на зарубежном CDN и детектит IP", "что еще зловредного сейчас могут делать сайты для детекции, кроме прямого чтения IP посетителя", а также и "как лучше предотвратить случайное открытие сайта не в том браузере/контейнере".

agpecam
12.04.2026 12:13как думаете, какие еще неочевидные маркеры наличия VPN могут начать массово использовать для детекта в ближайшем будущем?
Так ведь давно уже же используют обратный пинг для этого. Приложение пингует определенный хост и засекает время ответа. Хост пингует выходной IP квн приложения и сообщает ему время ответа. На разнице этих времен весь кордебалет и палится

alex_1065
12.04.2026 12:13я как не великий специалист в этой области ничего не понял, можно подробнее?

agpecam
12.04.2026 12:13На 2ip.ru зайдите при включенном квн и проверьте наличие туннеля. Вкратце, приложение, скажем озон, пингует хост где-то за рубежом. Пинг идет через ваш квн сервер, и это долго. Потом тот хост пингует адрес, который его пинговал (ваш квн за рубежом), и это быстро (внезапно!). Вот и все, сижу на амнезии премиум и у них на выходной ноде не отключен ICMP. Такая беда...

rzhestkov
12.04.2026 12:13Допустим, все друг друга попинговали. Как склеить эти события? На основании какого ключа?

qyix7z
12.04.2026 12:13Я бы сделал так: приложение запустилось, попинговало сервер и отправило результаты на сервер. Сервер в свою очередь попинговал в сторону клиента и сравнил результаты.

Heggi
12.04.2026 12:13А если у клиента NAT провайдерский? NAT-шлюз снаружи не факт что вообще пинговаться будет

qyix7z
12.04.2026 12:13Если что, я отвечал на этот вопрос:
Как склеить эти события? На основании какого ключа?
И у меня такие же вопросы. Вот нет ответа на пинг, какой вывод? Или допустим сменил вышку/сеть - тоже разница в пинге.
Я думаю, как косвенная улика - сама по себе ничего не говорит, но вкупе с другими подтверждает.
Heggi
12.04.2026 12:13Да вообще ни о чем не говорит.
Это может быть мобила на 2g сети в гребенях, через 100500 радиорелеек с серым адресов. А NAT-шлюз у провайдера в областном центре с хорошим интернетом.
От клиента до сервера пинг долгий и с потерями, а от сервера взад (до NAT-шлюза) маленький и хороший. Какой вывод на этом основании можно сделать? Никакого.

alex_1065
12.04.2026 12:13я тут попинговал, попробуйте сделать вывод где я нахожусь:
ping 8.8.8.8
Обмен пакетами с 8.8.8.8 по с 32 байтами данных:
Ответ от 8.8.8.8: число байт=32 время<1мс TTL=128
Ответ от 8.8.8.8: число байт=32 время<1мс TTL=128
Ответ от 8.8.8.8: число байт=32 время<1мс TTL=128
Ответ от 8.8.8.8: число байт=32 время<1мс TTL=128ping 1.1.1.1
Обмен пакетами с 1.1.1.1 по с 32 байтами данных:
Ответ от 1.1.1.1: число байт=32 время<1мс TTL=128
Ответ от 1.1.1.1: число байт=32 время<1мс TTL=128
Ответ от 1.1.1.1: число байт=32 время<1мс TTL=128
Ответ от 1.1.1.1: число байт=32 время<1мс TTL=128ping ya.ru
Обмен пакетами с ya.ru [77.88.55.242] с 32 байтами данных:
Ответ от 77.88.55.242: число байт=32 время<1мс TTL=128
Ответ от 77.88.55.242: число байт=32 время<1мс TTL=128
Ответ от 77.88.55.242: число байт=32 время<1мс TTL=128
Ответ от 77.88.55.242: число байт=32 время<1мс TTL=128ping vk.ru
Обмен пакетами с vk.ru [87.240.132.78] с 32 байтами данных:
Ответ от 87.240.132.78: число байт=32 время<1мс TTL=128
Ответ от 87.240.132.78: число байт=32 время<1мс TTL=128
Ответ от 87.240.132.78: число байт=32 время<1мс TTL=128
Ответ от 87.240.132.78: число байт=32 время<1мс TTL=128

noknown
12.04.2026 12:13А как побороли возможность любому приложению отправить запрос через tun? Например,
curl https://chatgpt.com/cdn‑cgi/trace ‑interface tun0
что выдает?

alex_1065
12.04.2026 12:13лол! Нафига? Есть специальные сервисы для этого, а если надо его несложно сделать самому подняв впс в нидерланда с nginx или caddy который будет возвращать json с данными о твоем конечном адресе, браузере, операционной системе и твое локальное время.
Ну типа стандартного - https://ipinfo.io/json
noknown
12.04.2026 12:13Смысл в том, чтобы приложение не могло отправить запрос в туннель, а отправляла бы его через обычный интернет

alex_1065
12.04.2026 12:13а, ну для этого мы используем раздельное маршрутизирование для приложений, это решает проблему с тоннелем, но остается открытой проблема с socks5 прокси на локалхосте, которую как раз пытается решить автор этой статьи.
P.S. Мое лол-нафига относилось к этой части вашего комментария -
curl https://chatgpt.com/cdn‑cgi/trace ‑interface tun0
для определения обхода блокировок проще использовать так -
curl https://ipinfo.io/json ‑interface tun0
noknown
12.04.2026 12:13В этом-то и проблема, что маршрутизация для приложений в большинстве клиентов для vless не работает. Точнее не работает фильтрация "разрешенных приложений".
Сайты типа ipinfo - уж больно явно являются сервисами проверки. Кажется, что такие известные домены можно заблокировать в приложении. Но вот пример с chatgpt на самом деле иллюстрирует гораздо бОльшую проблему: все домены работающие через cloudflare имеют у себя эндпоинт /cdn-cgi/trace а их не заблокировать без блокировки домена. Да и утопия это - писать правила для блокировки всех доменов работающих через cloudflare

ViskasSP1vom
12.04.2026 12:13Без рута и нормальных правил iptables эта фильтрация по пакетам всегда будет работать как повезет...

noknown
12.04.2026 12:13Ну в целом, я согласен. Но вот клиент flclash действительно может отбрасывать пакеты от приложений не входящие в белые списки. Возможно, что какие-то udp-подключения или короткие tcp-сессии будут проскакивать, но вроде как пока лучше все равно ничего нет.

Aelliari
12.04.2026 12:13Нафига? Есть специальные сервисы для этого
О, нет, это наоборот прекрасно. Я оценил трюк. Клиент может использовать политику:
1. «По этому короткому списку в туннель».
2. «Все остальное напрямую».
В тоже время сервер может иметь политику:
1. «По этому короткому списку выпустить в интернет».
2. «Все остальное заблокировать».
И тогда твой сервис который ты любовно поместил в свой список начинает на тебя стучать. Даже при такой жесткой политике

Wendor Автор
12.04.2026 12:13Android без рут прав у приложения не дает биндить сокет в туннель, если приложение находится в списке исключений. Binding socket to network 207 failed: EPERM (Operation not permitted).

noknown
12.04.2026 12:13Ну вот на обычных клиентах, типа v2rayNG, из termux (если он даже не в списке проксируемых) получается, что вполне себе запрос отправляется легко.

noknown
12.04.2026 12:13Или имеется в виду, что надо делать типа "черный список" - то есть исключать приложения из прокси? Ну тогда получается весь служебный трафик будет через vps идти. Зачем? Имхо проще же заворачивать через прокси отдельные приложения, а по умолчанию все пускать напрямую, нет?

Wendor Автор
12.04.2026 12:13Как раз добавил "обратный" список

noknown
12.04.2026 12:13Протестировал - к сожалению, такая же штука, как и у большинства клиентов: выбор приложения для прохода через прокси не запрещает другим слать через tun0 запросы

Ip и регион - внешняя нода прокси. 
Wendor Автор
12.04.2026 12:13Балин, вы меня этой находкой часов на 10 выбили из равновесия, и еще столько же буду отходить вероятно. :)
Но благо есть варианты, в том же sing-box есть возможность понимать что за приложение шлет пакет - а это уже лучик надежды) Буду смотреть в эту сторону
orthoxerox
12.04.2026 12:13Как минимум, можно попробовать интерфейс поднимать под неочевидным именем вместо tunX.

jtraub
12.04.2026 12:13https://github.com/SagerNet/sing-box/issues/4009 - вот это по идее должно помочь
Меняете tun2socks на sing-box в качестве менеджера tun0 интерфейса и далее блокируете всё, что биндится напрямую.

alex_1065
12.04.2026 12:13я правильно понял что вы закрыли порт прокси рэндомным паролем и биндите его на разные порты, при этом тоннель впн доступен приложениям из списка определяемого пользователем. Сам статус впн в системе без рут прав скрыть не получится но спайваре им по крайне мере воспользоваться не могут, а прокси для них становится недоступен?

noknown
12.04.2026 12:13Вообще, работающую блокировку по приложению я только у flclash нашел. Но там конфиги все переписывать надо

alex_1065
12.04.2026 12:13к обратному списку стоит поменять красный перечеркнутый кружок на значке приложения на что-то другое. (v 1.0.5)

night-walker
12.04.2026 12:13а если я "настоящий" виндовз накачу на смартфон, это поможет ? видел, были такие "хакнутые" устройства или выйду через второй смартфон и с него буду раздавать трафик ?

Mupok
12.04.2026 12:13А можно добавить параметр, чтобы по умолчанию всё шло мимо впн, а то что надо пустить в впн, самому отметить?) А то сиди выбирай кучку программ которым не надо впн, вдруг чтото пропустим
А, всё, нашел галочку, извините. А не, не работает, вот что выдает:
Start failed: addAllowedApplicationalready called

Malomalsky
12.04.2026 12:13Я не силен в мобильной разработке. Но скормил Ваш клиент чатужпт, чтобы провести аудит. Вот его ответ:
Конфиги с секретами сохраняются в plaintext в SharedPreferences. В сериализацию попадают uuid, password, publicKey, shortId и даже rawUri, то есть фактически полный импортированный URI. Подписки тоже сохраняют URL целиком. См. /tmp/ teapod‑stream/lib/core/models/vpn_config.dart:99, /tmp/teapod‑stream/lib/core/models/vpn_config.dart:120, /tmp/teapod‑ stream/lib/core/models/vpn_config.dart:122, /tmp/teapod‑stream/lib/core/services/config_storage_service.dart:64, /tmp/ teapod‑stream/lib/core/services/config_storage_service.dart:117, /tmp/teapod‑stream/lib/core/services/ config_storage_service.dart:121.
Поверх этого в AndroidManifest не отключён backup/export rules. Там нет ни android:allowBackup=«false», ни явных dataExtractionRules. Для обычного пользователя это не мгновенная межприложная утечка, но для high‑risk сценария это плохая гигиена хранения секретов. См. /tmp/teapod‑stream/android/app/src/main/AndroidManifest.xml:11.
Клиент действительно генерирует случайные SOCKS‑логин/пароль и порт, это хороший ход. Но затем сервис логирует полный запуск tun2socks, включая строку вида socks5://user:password@127.0.0.1:port, в logcat. То есть секреты, которые только что защитили localhost‑прокси, тут же утекли в системный лог. См. /tmp/teapod‑stream/lib/providers/vpn_provider.dart: 96, /tmp/teapod‑stream/lib/providers/vpn_provider.dart:103, /tmp/teapod‑stream/lib/protocols/xray/xray_config_builder.dart:20, /tmp/teapod‑stream/android/app/src/main/kotlin/com/teapodstream/teapodstream/XrayVpnService.kt:234, /tmp/te apod‑stream/android/app/src/main/kotlin/com/teapodstream/teapodstream/XrayVpnService.kt:249, /tmp/teapod‑stream/andro id/app/src/main/kotlin/com/teapodstream/teapodstream/XrayVpnService.kt:430.
README обещает больше, чем реально реализовано. Заявлены Trojan, Shadowsocks, H2, QUIC, но builder по факту собирает полноценные outbound‑настройки только для VLESS и VMess; для остальных протоколов возвращается пустой settings‑блок, а supportsConfig() всё равно отвечает true. Это не прямая уязвимость, но плохой сигнал по зрелости и может дать ложное ощущение «всё поддерживается». См. /tmp/teapod‑stream/README.md:7, /tmp/teapod‑stream/README.md:8, /tmp/teapod‑stream/ lib/protocols/xray/xray_config_builder.dart:124, /tmp/teapod‑stream/lib/protocols/xray/xray_config_builder.dart:150, / tmp/teapod‑stream/lib/protocols/xray/xray_engine.dart:230.
Допускаю, что ИИ мог нафантазировать. И свою безграмотность в этом вопросе тоже беру в расчет. Но если я правильно понял - Вы сами несколько раз в открытом виде кладете секреты

VADemon
12.04.2026 12:13android:allowBackup - Чего плохого в возможности бэкапа пользователем? "Но затем сервис логирует полный запуск tun2socks" - кто имеет доступ к логам? Пользователь?

noknown
12.04.2026 12:13Не совсем понимаю претензии к автору. Он честно признался, что за пару дней подпивасно накодил прогу в качестве proof of concept. Он не позиционировал её как самое защищённое решение на свете, а просто показал, что исправление так сильно всех напугавшей уязвимости, в целом, несложное.

Wendor Автор
12.04.2026 12:13Давайте и за меня ответит ИИ.
Вот краткое резюме по каждому пункту:
Открытые конфиги (SharedPreferences): Не страшно. Защищено встроенной изоляцией Android. Без root-прав (взлома системы) другие приложения до этих файлов не доберутся.
Резервное копирование (Backup rules): Не критично. Конфиги просто сохраняются в ваш бэкап на Google Drive или доступны через USB-кабель. Если телефон запаролен и аккаунт защищен — угрозы нет.
Пароли в логах (Logcat): Не опасно. Утекли пароли от локального узла (внутри самого телефона). Из интернета к нему не подключиться, а Android запрещает обычным приложениям читать системный лог.
Заглушки вместо протоколов: Это не уязвимость. Разработчик просто не дописал код для Trojan и Shadowsocks, хотя пообещал их в описании.
Общий вывод: Для личного использования это безопасно. Это проблемы "чистоты" кода, а не реальные дыры, через которые ваш телефон могут взломать извне.

ViskasSP1vom
12.04.2026 12:13Постыдитесь и не тащите галлюцинации нейронки на технический ресурс) Большинство из этих "уязвимостей" в песочнице андроид вообще смысла не имеют

not_solitary_traveller
12.04.2026 12:13Отдельный айфон для адекватной жизни в неадекватной стране уже готов. Наивысшая сейчас мера - изоляция "плохих" и "хороших" приложений друг от друга на уровне физического устройства.

PeeWeee
12.04.2026 12:13А левую
симкуцифровую личность для “хороших” приложений© Вы заготовили? Боюсь, что без радикальных мер, типаспонсированияагитации статистически значимого процента пользователей иметь два активно используемых телефона, Вы добьетесь лишь попадания в ВИП-список товарища майора. И данные о Вас лично будут собирать не приложения на Вашем же телефоне - в общую большую кучу, а провайдеры Ваших подключений - в отдельную небольшую, тщательнее просматриваемую. Да, наверно, пока товарищу майору не до таких изысков, но закон Яровой (операторы хранят разговоры, SMS и трафик до 6 месяцев, а метаданные — до 3 лет) и мысленная экстрополяция тенденций не на стороне Вашейоптимистичностинеуловимости.
ReneSv
12.04.2026 12:13А что увидит товарищ майор? Кучу зашифрованного трафика, мимикрирующего под легальные сайты с одного телефона и реальный трафик на легальные сайты с другого?

PeeWeee
12.04.2026 12:13Да хз что он там увидит, я не сетевик-затейник. Хотел призвать взглянуть с обратной стороны тезиса
изоляция “плохих” и “хороших” приложений друг от друга на уровне физического устройства.
На первый взгляд вариант кажется более простым и надежным, чем все эти изолированные пространства, которые, при ближайшем рассмотрении, либо пользователь не сумеет квалифицированно разделить (особенно дилетант по статье из интернета), либо там заранее бэкдоров напихано для товарищей майоров всех стран.
Но вот то, что озвучил в предыдущем комментарии, заставило усомниться. Если кто-то
болтивыйграмотный прокомментирует соотношение рисков - с благодарностью послушаю. Если кто-торазумныйне столь грамотный хотя бы молча задумается - наверно это будет правильно.Hidden text
Ну и, с точки зрения дилетанта-циника-гуманиста, хотел напомнить, что все технические, но не замаскированные (под массовость или непреднамеренность) методы "за все хорошее против всего плохого" рискуют столкнуться с нетехническим брутфорсом:
есть задокументированная фиксация, но нет возможности расковырять что внутри - риск попасть под
презумцию виновностинет основания недоверять сотруднику/экспертуматериал уже есть, а статью напишем
терморектальный криптоанализ

ris58h
12.04.2026 12:13С iPhone по очевидным причинам проверить не могу. А есть ли возможность ограничивать некоторые приложения доступом только к некоторому сегменту сети (все ру идут в ру или идут нафиг)?

denisgrigoriev04
12.04.2026 12:13Даже Ркн не знает какие ip ру а какие нет

Aelliari
12.04.2026 12:13Как не знает? Есть в РАНР - русский IP. Нет - забанить

denisgrigoriev04
12.04.2026 12:13Тогда почему в белых списках 150 сайтов из сотен тысяч

Aelliari
12.04.2026 12:13Кажется вопрос не по адресу. Я могу строить только предположения, например что контролировать небольшой список проще, а также нет никаких гарантий что на части адресов в РАНР не развернуто что-то для обхода блокировок…

black_list_man
12.04.2026 12:13AmneziaWG лишена этой уязвимости?

banditto9
12.04.2026 12:13насколько я вижу у себя - нет, не лишена, у меня по команде в Термуксе из поста ниже (
curl --interface tun0whatismyip.akamai.com)показало адрес выхода

MrEternal
12.04.2026 12:13curl --interface tun0 whatismyip.akamai.comпоказывает выходной IP прокси даже если приложение из проксируемых исключено. Увы, но ваш клиент тоже уязвим на Android с ядром 5.7+. В Exclave уже починили и можно tun защитить правилами, а socks вообще отключить.

vp7
12.04.2026 12:13Есть версии андроида, в которой это уже починили? И сразу вопрос вдогонку - на Linux можно определить PID процесса, открывшего сокет, но с рутовыми привилегиями. На андроиде без рута это тоже не решается?

MrEternal
12.04.2026 12:13Ещё нет, в самом андроиде ждать фикс будем долго и до большинства смартфонов он никогда не дойдёт. Рут не нужен, на уровне самого приложения поднимающего tun можно определять UID приложения, которое шлёт трафик. Если трафик был насильно отправлен в tun-интерфейс через
SO_BINDTODEVICEилиNetwork.bindSocket, то определить UID не получится. Вот такой нераспознанный трафик можно блокировать или слать напрямую. Оба варианта защитят TUN.Посмотрите как это уже сделано в sing-box for Android. Sing-box через JNI получает колбэк
FindConnectionOwnerот кода на котлине и вызывает его для каждого соединения. Внутри этого колбека используются уже апи андроида чтобы отдать гошной части владельца соединения, отдаётся само имя пакета. Поэтому в сингбоксе есть рабочее правило роутингаpackage_nameи недавно добавилиpackage_name_regex. В xray этого нет, у них нет своего приложения под Андроид. Но эту логику можно портировать в любой xray-клиент просто взяв из сингбокса.
MrEternal
12.04.2026 12:13Ещё везде, где решили проблему запаролив сокс скорее всего есть проблема с UDP, по идее он обходит аутентификацию SOCKS. Поэтому от tun2socks нужно уходить и оставлять только родной Tun от Xray, или насильно отключать udp.

Wendor Автор
12.04.2026 12:13Написал свою tun2socks реализацию, которая при открытии подключения сверят от кого пришел пакет, а если пакет прошел минуя api андроида - дропается.

ash_lm
12.04.2026 12:13Уже вторая неделя статей про всякие утечки, методички, RKNHardering, кто-то там что-то тестирует. А хоть кто-нибудь делал хоть малейший аудит RKNHardering? Автору на гитхабе без году неделя отроду и все тупо ставят это приложение. Откуда вообще такое доверие? Почему вы решили, что это приложение просто не собирает определённую информацию? Кстати тоже самое относится и к TeapodStream. ))

Gambit132
12.04.2026 12:13Доверяй, но проверяй) TeapodStream - исходники открытые, я скачал и прогнал через Клод для скорости + вручную посмотрел методы. Идея то до безобразия простая и потому рабочая)

CitizenOfDreams
12.04.2026 12:13как думаете, какие еще неочевидные маркеры наличия VPN могут начать массово использовать для детекта в ближайшем будущем?
Например, пользователь цитирует "запрещенные" источники, недоступные без VPN. И вообще какой-то слишком умный.

sfinks777
12.04.2026 12:13Есть одна идея, которая потенциально может решить проблему с видимостью соединения хоста андроида из активного приложения в шелтере (Knox в Samsung, например). Два инстанса одного приложения, одно работает на хосте, второе в шелтере. И третий условный “сервер” на удаленном хосте (или какое-то логическое соединение между ними прямо внутри системы телефона). Если просыпается приложение в шелтере, то первый инстанс на хосте гасит активную сессию туннеля и наоборот, когда засыпает вся живность в шелтере, просыпается туннель. А если этот функционал и вовсе добавить в клиента квн, то будет здорово. Другой вопрос, можно ли из стороннего приложения с большой зеленой кнопкой вообще мониторить и управлять статусом шелтера и заставлять его уснуть, иначе придется это делать руками. Но, как минимум, можно не включать туннель, пока шелтер активен и сообщить об этом пользователю.

egoistlike
12.04.2026 12:13Karing - добавлена ручная авторизация (Add authorization settings for mixed type inbound connections(Settings-Mixed)), о чем упоминал автор той статьи на Хабре.
Throne -Add inbound authorization support в обновлении. Версии только для ПК.
v2rayN - В настройках: "Настройки" - "Настройки параметров" - "Ядро" - есть поля для ввода "имя пользователя (логин)" и "пароль аутентификации", но они не решают проблему из статьи. По отзывам.
Остальные пока нет.

Maggot84
12.04.2026 12:13Коллеги, а если реализовать такую схему: на европейском VPS с XRAY использовать несколько IP (один – для входящих подключений от клиентов или промежуточного российского VPS; второй – для выходного трафика). Если какое-то приложение сольёт в РКН внешний IP и РКН его заблокирует, то на работу системы это никак не повлияет, так как данный сервер находится за пределами России? Есть конечно риск, что под блокировку попадёт весь пул адресов хостера, но в остальном схема рабочая?

JerleShannara
12.04.2026 12:13А зачем для выходного адреса использовать незаблокированный диапазон? Выход с заблокированного гораздо смешнее будет выглядеть + вероятный косяк со сбросом соединения детекторами (драсьте тспу, спасибо за качественную работу).

rendov
12.04.2026 12:13У меня так сделано. Купил сначала первый VPS за рубежом и он быстро улетел в блок/шейпинг. Потом подобрал второй, в другой подсети у другого хостера, проверяя с первого до второго самый минимальный пинг (часто до покупки дают тестовые ip для проверки пинга). Потом на втором VPS прокинул порт КВН через iptables на первый, где уже тусит сам VPN. Получилось типа RU_CLIENT -> EN_VPS2 -> EN_VPS1 -> WORLD. Теперь, даже если РКН увидит мой выходной ip первого vps и забанит его (а в моём случае он и так уже забанен), всё продолжит работать, т.к. трафик этого ip вращается целиком за рубежом между двумя VPS.
Но это не решает проблему, что РКН увидит что используется КВН. По идее, самое стремное что могут там придумать, допустим при детекте ВПН вводить режим БС для конкретного абонента. Ну а че, ОПСОСЫ могут же при нулевом балансе врубать для абонента доступ тока в личный кабинет, так и тут сделают, а при БС никакие маскировки не спасут, т.к. тупо до EN_VPS2 не достучишься.

SergeMNE
12.04.2026 12:13Тогда уж и мое пиво подержите, друзья!
Тоже давно читаю весь этот околовпнный хайп. И предлагаю взглянуть на вопрос с чуть другой стороны.
Да, любое приложение на Андроиде (да и на IOS тоже) может детектировать (точнее, уверенно предположить) поднятый на устройстве впн. И даже сообщить «куда следует» ваш конечный IP. А что дальше?
Впн у нас в стране не под запретом. Поэтому важно исключить не возможность детектировать есть/нет у нас впн подключение, а как и для чего мы его используем. Или, юридическим языком, обходим мы с его помощью блокировки или нет. А вот здесь для РКН все ой как не гладко
Блокировки выявленного конечного IP по факту ничего им не дают. К примеру, мой голландский IP на прямое подключение заблочен уже почти месяц как, но и по сей день прекрасно трудится на каскаде, ибо межсерверный трафик РКН может блокировать только по принципу «сломаю пол-инета». Не пустят с включенным впн в аккредитованные приложения? Да и х… с ними. Выключу кнопку впн и зайду.
Понятно, что сейчас доступ к свободному Инету в РФ стал не просто «поставлю 3 хуи и весь мир открыт», а стал дороже и требует творчества и хотя бы 3 классов школы ИТ-инженера. Но для нашего русского ума это всего лишь легкая разминка.
Как краткий вывод, считаю, что сейчас надо направлять усилия не на поиск решения найдут «гос зловреды» впн или не найдут, а на то, чтобы впн работал несмотря ни на что и не отсвечивал «куда, как и для чего» мы его используем

ViskasSP1vom
12.04.2026 12:13Ну ты еще Конституцию вспомни
Им вообще плевать на юридический статус, они ломают связность чисто технически по площадям

alex_1065
12.04.2026 12:13... а на то, чтобы впн работал несмотря ни на что и не отсвечивал «куда, как и для чего» мы его используем
ну на самом деле впн работает (если не включены "белые списки" конечно), к примеру netbird и tailscale которые для создания локальной сети между устройствами через интернет используют wireguard vpn работают. По крайне мере до тех по пока вы не попробуете в локальную сеть пустить устройство из зарубежного сегмента интернета, точно работают.

Radjah
12.04.2026 12:13Обратите внимание на строку запуска tun2socks: порт 45478 и забористые логин/пароль генерируются на лету при каждом старте соединения.
А через список процессов такое не палится?

egoistlike
12.04.2026 12:13Насколько понял по поведению на моих устройствах(Throne), логин/пароль нужен для запрета неавторизованных для приложения подключится к квн(тайно/не тайно, неважно.)и проверить есть квн или нет. Грубо говоря, тот же Wildberries не сможет этого сделать если вы ему явно запретите. Итого, квн у вас есть, до всей этой истории, при запрете квн у вас "нет."

ViskasSP1vom
12.04.2026 12:13Подозреваю скоро просто начнут замерять RTT до своих же CDN, и тут никакой рандомный порт не спасет

Budyonovetz
12.04.2026 12:13Здавствуйте. Скажите пожалуйста, почему virustotal находит вирусы в вашем приложении? Спасибо.

Wendor Автор
12.04.2026 12:13Вы расскажите) Можете собрать из исходников, сравнить с выложенными бинарниками

Budyonovetz
12.04.2026 12:13Я пенсионер и только сегодня зарегистрировался на этом сайте. Я ищу способ оживить Телеграм, у меня внуки заграницей живут.

alex_1065
12.04.2026 12:13наш родной месенджер телега (перекомпилированный клиент телеграма с отключением шифрования, сохранением сообщений на рф серверах и т.д.) поможет вам в этом нелегком деле, только ничего важного и секретного через него не передавайте, ибо чистое спайваре, но работает.

Budyonovetz
12.04.2026 12:13Я читал про Телегу - это шпионское приложение, там всё подслушивают и все сообщения записывают. Его нельзя устанавливать.

denisgrigoriev04
12.04.2026 12:13Похоже единственный способ этого избежать - это использовать Firefox с плагином умного прокси и вручную галочки ставить напротив нужных сайтов. Вот только где найти приложение который превращает vless/что-то другое в локальный прокси, причем без создания tun

alex_1065
12.04.2026 12:13
Нейросети говорят что это работает и даже картинку нарисовали как это переключается :)
Но имейте в виду что прокси открывается на 127.0.0.1:[ПОРТ] и если вы этот адрес-порт не заблокируете то вредоносный JS код с вредоносных страничек может его определить и найти куда он ведет. Для того чтобы этого не произошло, нужно создать специальные правила настройки прокси в браузере firefox.
Выглядит это примерно так:
в этом файле прописано примерно такое правило:

ну а все что не попадает под "if" то делать
return "DIRECT";
denisgrigoriev04
12.04.2026 12:13Я имел ввиду, где найти такое приложение под Андроид. Http/socks прокси позволяют сделать авторизацию, но это похоже даже клиенты под Windows не умеют

denisgrigoriev04
12.04.2026 12:13Не надо url автомастерской настройки, браузеры не проксируют localhost, если он вообще нужен. Запись 0.0.0.0 не имеет смысла, он используется только для прослушивания адресов. Localhost тоже не имеет смысла он мгновенно решится в 127.0.0.1 из файла hosts. Что за нейронная сеть написала такую бредятину?

denisgrigoriev04
12.04.2026 12:13Вместо такой грубой настройки лучше использовать расширение Smart proxy https://addons.mozilla.org/ru/firefox/addon/smartproxy. И зачем это делать под детали, у вас что Я браузер стоит, что у вас там может шпионить?
Более того не сработает, потому что SYSTEM proxy, это значит, что все приложения, которые умеют видеть прокси, его видят и пытаются использовать

Hoksmur
12.04.2026 12:13Есть ещё подход, простой до безобразия: поднимаем прокси, для всех приложений. Но одни сочетания логинов/паролей/ключей имеют точку выхода в белой зоне, а другие - в тех, где приложение будет работать и сервисы доступны.
Автору статьи +100500! Не стал ломать замок, просто открыл соседнюю дверь.




jarkevithwlad
а что если в виртуалку спрятать не скам / яндекс / госуслуги, а наоборот все нужные приложения? это не поможет?
alex_1065
я как раз тут вчера пробегая по постам и комментариям натыкался на примерно аналогичный вариант чтобы не светить интерфейс впн использовать - шелтер, в нем прокси на локалхосте и там уже телеграм и прочее. Но тот товарищ ошибается насчет того что локалхосты у шелтера и основного профиля разные, локалхост у них один и те-же и прокси поднятый на порту в шелтере так-же доступен из основного профиля и наоборот.
Закрыть прокси на локалхосте для всех приложений и оставить только для выбранных не смогут даже файрволы которые запускаются без рута.
P.S. впрочем он сам написал что бухой, так что простительно :-)
Gambit132
На Секлабе писали Шелтер течет=(
Upd: На ведроиде
alex_1065
походу создателям андроида и шелтера и в страшном сне не снилось, что кому-то нужно будет целенаправленно на своем устройстве запускать спайваре работать с ней и пытаться от нее-же защитится.
Gambit132
Вы правы, никто к подобному не был готов. Ни создатели операционных систем, ни создатели приложений. Новая эпоха, новые вызовы)
isumix
Таков путь
denisgrigoriev04
Мне ещё не нравится что из под него видна локальная сеть и приложение может свободно сканировать порты других устройств и даже посылать пакеты (привет 22)
glebliutsko
Интересно, у если ядро собрано с поддержкой network namespace можно ли прикостылить их к профилям?