# Критическая уязвимость VLESS-клиентов: неаутентифицированный SOCKS5 на localhost **Дата публикации:** 7 апреля 2026 **Источник:** [runetfreedom — ntc.party](https://ntc.party/t/topic/), [Habr](https://habr.com/ru/articles/1020080/) **POC:** [github.com/runetfreedom/per-app-split-bypass-poc](https://github.com/runetfreedom/per-app-split-bypass-poc) **Severity:** Critical (массовая эксплуатация неизбежна) --- ## TL;DR Все популярные VLESS/xray/sing-box клиенты создают на localhost SOCKS5-прокси **без аутентификации**. Любое приложение-шпион на устройстве может подключиться к нему напрямую (минуя VpnService и per-app split tunneling), отправить запрос через прокси и узнать **выходной IP VPN-сервера**. Android Private Spaces (Knox, Shelter, Island) **не защищают** — loopback-интерфейс общий. Минцифры РФ разослало методичку по обнаружению VPN, включая сканирование характерных портов. --- ## 1. Предпосылки Минцифры РФ официально потребовало от аккредитованных российских IT-компаний внедрения шпионских модулей в свои продукты для обнаружения и блокировки персональных VPN. Выпущена подробная методичка, описывающая: - Какие порты сканировать для обнаружения прокси - Какие VPN-признаки искать (флаги, интерфейсы, паттерны трафика) - Какую информацию собирать и передавать **Характерные Proxy-порты из методички Минцифры:** | Тип | Порты | |-----|-------| | SOCKS | 1080, 9000, 5555, 16000-16100 | | HTTP | 80, 443, 3128, 3127, 8000, 8080, 8081, 8888 | | Прозрачные Proxy | 80, 443, 4080, 7000/7044, 8082, 12345 | | Tor | 9050, 9051, 9150 | > Примечание: стандартные порты xray-клиентов (10808, 10809, 2080) в методичке пока не указаны, но скан всех портов localhost занимает секунды. Яндекс **уже использовал** подобную технику ранее ([[Localhost-tracking-Meta-Yandex-SOCKS5|сканирование localhost для обнаружения прокси]]). **Обновление (апрель 2026):** Зафиксированы случаи активного подавления VPN-соединений сервисами Яндекса. При работе Яндекс.Музыки на мобильном интернете VPN-соединение через NekoBox (sing-box) принудительно сбрасывается. Переход на другой VPN-клиент (Karing) решает проблему, что указывает на fingerprinting конкретных VPN-протоколов/клиентов на уровне DPI. --- ## 2. Архитектура VPN-клиентов на Android ### Нормальная цепочка прохождения трафика ``` Приложение ↓ VpnService (Android системный API) ↓ tun2socks (TUN-интерфейс → SOCKS) ↓ xray/sing-box SOCKS5 (localhost:10808) ↓ VPN-сервер (VLESS/Trojan/SS) ↓ Интернет (freedom) ``` **VpnService** — системный механизм Android, перехватывающий весь сетевой трафик и перенаправляющий его в TUN-интерфейс. Через него реализуется per-app split tunneling — выбор, какие приложения идут через VPN. ### Ключевая деталь SOCKS5-прокси xray/sing-box — это **отдельный TCP-сервер**, слушающий на `127.0.0.1:PORT`. Он доступен **любому процессу** на устройстве через loopback, **независимо от VpnService**. --- ## 3. Суть уязвимости ### Атака ``` ┌─ Обычное приложение → VpnService → tun2socks ──┐ │ ↓ │ xray SOCKS5 (:10808) → VPN-сервер → Интернет │ ↑ └─ Шпион (Яндекс/WB/Ozon/...) ─→ localhost:10808 ─┘ (минуя VpnService, split tunneling не работает) ``` **Шаг 1.** Шпионский модуль сканирует порты localhost. Полный скан 65535 портов на loopback — **секунды**, нулевая нагрузка. Можно сканировать по триггеру (VPN-флаг системы). **Шаг 2.** Найдя открытый SOCKS5 без аутентификации, шпион подключается и отправляет запрос на `ifconfig.me` (или аналог). **Шаг 3.** Ответ содержит **выходной IP VPN-сервера**. IP передаётся в РКН → сервер блокируется. ### Почему не помогает per-app split tunneling Split tunneling работает через VpnService, который управляет TUN-интерфейсом. Но прямое подключение к `127.0.0.1:10808` **не проходит через TUN** — это внутренний loopback, он не маршрутизируется через VPN. ### Почему не помогают приватные пространства (Knox/Shelter/Island) Android Private Spaces изолируют: - ✅ Файловую систему - ✅ VpnService (отдельный VPN-профиль) - ❌ **Loopback-интерфейс НЕ изолирован** Процесс из Knox/Shelter **видит** `127.0.0.1:10808` основного профиля. Это создаёт **ложное чувство безопасности**. ### Почему смена порта не помогает > «Достаточно заменить порты на рандомные» — нет. Скан портов на localhost занимает секунды. Шпиону не нужно знать порт заранее — он проверит все открытые порты и попробует SOCKS5 handshake на каждом. ### Метод /proc/net/route: IP VPN-сервера раскрыт через таблицу маршрутизации Помимо сканирования портов, существует ещё более простой метод обнаружения IP VPN-сервера: Когда VPN активен, Android создаёт **host route** (маска /32 = `255.255.255.255`) для IP VPN-сервера через **реальный сетевой интерфейс** (wlan0/rmnet0), минуя TUN-интерфейс VPN. Это необходимо, чтобы пакеты VPN-туннеля доходили до сервера через физическую сеть. ``` $ cat /proc/net/route Iface Destination Gateway Flags Mask tun0 00000000 00000000 ... 00000000 ← default через VPN wlan0 B9A5C041 0201A8C0 ... FFFFFFFF ← host route к 65.197.165.185 через WiFi ``` **Любое приложение может прочитать `/proc/net/route` без root.** Это раскрывает IP VPN-сервера для **ВСЕХ типов VPN**: - WireGuard / Amnezia VPN (UDP) - OpenVPN (TCP/UDP) - xray / VLESS / Trojan (TCP) - IPSec / IKEv2 **Работает даже при раздельном туннелировании** — маршрут к серверу всегда проходит через реальный интерфейс, иначе VPN-туннель не смог бы функционировать. Вместе с `/proc/net/udp` и `/proc/net/tcp` (которые показывают ESTABLISHED-соединения всех приложений) шпион получает: - IP и порт VPN-сервера - UID приложения-владельца (→ имя пакета через `PackageManager.getPackagesForUid()`) - Протокол (TCP/UDP → угадать тип VPN: UDP:51820 = WireGuard, TCP:443 = VLESS) --- ## 4. Happ: отдельная категория опасности Клиент Happ (iOS) помимо уязвимого SOCKS5 включает **xray gRPC API с HandlerService БЕЗ аутентификации**. ### Что такое xray API xray-core поддерживает gRPC API с несколькими сервисами: | Сервис | Назначение | Опасность | |--------|-----------|-----------| | **StatsService** | Статистика трафика, онлайн-пользователи | Низкая | | **LoggerService** | Управление логированием | Низкая | | **HandlerService** | Управление inbound/outbound, добавление/удаление пользователей, **дамп конфигурации** | 🔴 **КРИТИЧЕСКАЯ** | | **RoutingService** | Управление правилами маршрутизации | Средняя | | **ReflectionService** | Список доступных API-методов | Средняя | ### Что позволяет HandlerService - Дампить **полные конфиги outbound** (включая ключи шифрования, IP сервера, SNI) - Добавлять/удалять inbound и outbound - Управлять пользователями протоколов (VMess, VLESS, Trojan, Shadowsocks) - Получать список всех inbound/outbound ### Цепочка атаки на пользователей Happ ``` Шпион → скан портов → находит API (gRPC) → HandlerService → дамп outbound конфига → извлечение: ключ + IP сервера + SNI → [+ вторая уязвимость конфигурации] → расшифровка трафика ``` ### Позиция разработчиков Happ Заявили, что API нужен для «сбора статистики подключения». Это **ложь** — для статистики достаточно `LogService` или `StatsService`. HandlerService предоставляет полный контроль над конфигурацией. Реверс-инжиниринг показал: при смене конфига Happ **перезапускает xray целиком**, а не использует HandlerService для hot-swap. Легитимного применения HandlerService не найдено. ### Рекомендация - **Немедленно прекратить использование Happ** - Заблокировать Happ на серверах подписок по UserAgent: `Happ/*` - Один пользователь с Happ компрометирует **весь сервер** (включая входной IP и SNI) ### Подписочная система НЕ защищает Распространённое заблуждение: «мой сервер на подписке, его не найдут». Это не так. Через xray gRPC API (HandlerService) любое приложение на устройстве получает **полную конфигурацию VPN-подключения**: | Поле | Пример | Что это даёт шпиону | |------|--------|---------------------| | `protocolName` | splithttp | Протокол транспорта | | `address:port` | server.amazonaws.com:443 | **Адрес сервера → заблокировать** | | `uuid` | f7fd6ed1-8cbb-... | **UUID → клонировать подключение** | | `reality sni` | checkip.amazonaws.com | SNI → идентификация | | `reality publicKey` | qTdiDYiav3Rtv0kw... | **Публичный ключ** | **Подписка = сервер + UUID + ключи. Всё это хранится в xray-core и отдаётся через gRPC API без пароля.** Любое приложение может прочитать вашу платную подписку целиком и передать данные для блокировки. Доказательство: приложение [per-app-split-bypass-poc](https://github.com/runetfreedom/per-app-split-bypass-poc) демонстрирует полный дамп конфигурации через xray API на Android. --- ## 5. Техническая документация: аутентификация SOCKS5 ### xray-core: конфигурация SOCKS5 inbound #### Без аутентификации (уязвимая конфигурация) ```json { "tag": "socks-in", "listen": "127.0.0.1", "port": 10808, "protocol": "socks", "settings": { "auth": "noauth", "udp": true } } ``` #### С аутентификацией (безопасная конфигурация) ```json { "tag": "socks-in", "listen": "127.0.0.1", "port": 10808, "protocol": "socks", "settings": { "auth": "password", "accounts": [ { "user": "randomUser_a8f3b2", "pass": "randomPass_7c4e91d0" } ], "udp": false } } ``` **Поля:** | Поле | Тип | Значение | |------|-----|----------| | `auth` | `"noauth"` / `"password"` | Режим аутентификации. По умолчанию `"noauth"` | | `accounts` | `[{user, pass}]` | Массив учётных записей. Работает только при `auth: "password"` | | `udp` | `boolean` | Включение UDP. **Важно:** UDP в SOCKS5 фактически работает без авторизации | | `ip` | `string` | Локальный IP для UDP-соединений | | `userLevel` | `number` | Уровень пользователя для политик | > ⚠️ **SOCKS5 UDP проблема:** протокол SOCKS5 аутентифицирует только TCP-соединение. UDP ASSOCIATE команда привязывает UDP-порт, но сами UDP-пакеты **не проходят повторную аутентификацию**. Для полной защиты UDP нужно отключать. ### sing-box: конфигурация SOCKS/mixed inbound #### Без аутентификации (уязвимая конфигурация) ```json { "type": "mixed", "tag": "mixed-in", "listen": "0.0.0.0", "listen_port": 2080 } ``` #### С аутентификацией (безопасная конфигурация) ```json { "type": "mixed", "tag": "mixed-in", "listen": "127.0.0.1", "listen_port": 2080, "users": [ { "username": "randomUser_d4c1a7", "password": "randomPass_9e2f5b83" } ] } ``` **Поля:** | Поле | Тип | Значение | |------|-----|----------| | `type` | `"socks"` / `"mixed"` | `mixed` поддерживает SOCKS4/4a/5 и HTTP | | `users` | `[{username, password}]` | Массив учётных записей. Без этого поля — без аутентификации | | `listen` | `string` | Адрес прослушивания | | `listen_port` | `number` | Порт | | `set_system_proxy` | `boolean` | Автоматическая настройка системного прокси (Linux/Android/Windows/macOS) | > Примечание: sing-box **не создаёт mixed inbound автоматически** — это делают клиенты и генераторы конфигов. Позиция разработчика sing-box: это «skill issue» клиентов. --- ## 6. Статус проверенных клиентов Уведомление разработчикам разослано **10 марта 2026**. Статус на 7 апреля 2026: | Клиент | Платформа | Ядро | SOCKS5 auth | Статус | Примечание | |--------|-----------|------|-------------|--------|------------| | **Happ** | iOS | xray | ❌ Нет | 🔴 **Особо опасен** | API HandlerService без авторизации, дамп конфигов | | **v2RayTun** | Android | xray | ❌ Нет | 🟡 Уязвим | Разработчик поблагодарил, обещал исправить | | **V2BOX** | iOS/Android | xray | ❌ Нет | 🟡 Уязвим | | | **v2rayNG** | Android | xray | ❌ Нет | 🟡 Уязвим | Порт 10808 по умолчанию | | **Hiddify** | Android/iOS | sing-box | ❌ Нет | 🟡 Уязвим | | | **Exclave** | iOS | xray | ❌ Нет | 🟡 Уязвим | | | **Npv Tunnel** | Android | xray | ❌ Нет | 🟡 Уязвим | | | **Neko Box** | Android | sing-box | ❌ Нет | 🟡 Уязвим | Порт 2080 по умолчанию | | **Karing** | Android/iOS | sing-box | ❌ Нет | 🟡 Уязвим | Порты 3065-3067, можно добавить auth вручную | | **v2rayN** | Windows | xray | ❌ Нет | 🟡 Уязвим | Можно менять порт, но не добавить auth | | **Husi** | Android | sing-box | ✅ Есть | 🟢 Можно настроить | Login/pass на SOCKS | | **SFA (sing-box for Android)** | Android | sing-box | ✅ Есть | 🟢 Можно настроить | Ручная настройка JSON | | **Xray (saeeddev94)** | Android | xray | ✅ Есть | 🟢 Можно настроить | F-Droid, ручной JSON конфиг | --- ## 7. Статус XrayFluent (наш клиент) ### Текущая конфигурация **Файл:** `xray_fluent/engines/xray/config_builder.py`, строки 191-206 ```python { "tag": "socks-in", "listen": PROXY_HOST, # 127.0.0.1 "port": int(socks_port), # 10808 "protocol": "socks", "settings": { "auth": "noauth", # ❌ БЕЗ АУТЕНТИФИКАЦИИ "udp": True, # ⚠️ UDP включён }, "sniffing": { "enabled": True, "destOverride": ["http", "tls", "quic"], "routeOnly": True, }, } ``` **Константы** (`xray_fluent/constants.py`): - `PROXY_HOST = "127.0.0.1"` — только localhost ✅ - `DEFAULT_SOCKS_PORT = 10808` — стандартный порт ⚠️ - `DEFAULT_HTTP_PORT = 10809` — HTTP тоже без auth ⚠️ - `DEFAULT_XRAY_STATS_API_PORT = 19085` — API порт ⚠️ **Шаблоны** (`data/templates/xray/default.json`): - Слушают на `0.0.0.0` — **хуже рантайма**, доступны из сети ❌ ### Смягчающие факторы XrayFluent — **десктопный Windows-клиент**, поэтому: 1. Windows Firewall может блокировать доступ к localhost из других приложений (но не по умолчанию для loopback) 2. xray routing rules с `process name` позволяют ограничить доступ 3. Нет VpnService-подобного механизма — атака через loopback менее вероятна ### Что нужно исправить 1. Добавить `"auth": "password"` с рандомными credentials (генерировать при каждом запуске) 2. Отключить UDP или реализовать UDP auth 3. Исправить шаблоны: заменить `0.0.0.0` на `127.0.0.1` 4. Проверить, какие API-сервисы включены (не должно быть HandlerService) 5. Добавить настройку в UI для включения/отключения SOCKS5 auth --- ## 8. [[VLESS-localhost-protection-guide|Защитные меры]] ### Для пользователей (клиентская сторона) **На Android:** 1. Использовать клиенты с поддержкой SOCKS5 auth (Husi, SFA, saeeddev94/xray) 2. Если root: блокировать доступ к localhost через iptables для недоверенных приложений 3. **Немедленно удалить Happ** и заблокировать на серверах подписок 4. Держать российское ПО на **отдельном устройстве** 5. Не полагаться на Knox/Shelter/Island — loopback не изолирован **На Windows:** 1. Настроить Windows Firewall для блокировки доступа к портам 10808/10809 для всех процессов кроме xray.exe и доверенных приложений 2. Использовать рандомные порты (не 10808) 3. В xray routing rules добавить `process name` для разрешённых приложений **На iOS:** 1. Ситуация сложная — большинство клиентов удалено из App Store и не получит обновлений 2. Единственный вариант — клиенты с возможностью ручной настройки JSON (если доступны) ### Для администраторов серверов 1. **Раздельные входной/выходной IP** — если утёк выходной, входной продолжает работать 2. **CloudFlare WARP на выходе** — как альтернатива второму IP: `xray → WARP → интернет` 3. **geoip:ru → block на сервере** — заблокировать исходящий трафик на российские IP, чтобы шпионы не могли связать паттерн трафика с подключением 4. **Блокировка Happ по UserAgent**: `Happ/*` на серверах подписок 5. **Мониторинг** — отслеживать нетипичные запросы (ifconfig.me, ipinfo.io и аналоги) через прокси ### Для разработчиков клиентов 1. **SOCKS5 auth по умолчанию** — генерировать рандомные credentials при каждом запуске 2. **Отключить UDP** в SOCKS5 или реализовать UDP-level auth 3. **Не включать xray API HandlerService** — для статистики достаточно StatsService/LogService 4. **Не слушать на 0.0.0.0** — только 127.0.0.1 5. **Рандомизировать порт** — не использовать стандартные 10808/2080/1080 ### Маршрутизация (клиентская сторона) Рекомендуемая стратегия на клиенте: ``` geoip:ru → direct (российские IP напрямую) geoip:private → direct (локальные сети) всё остальное → proxy (через VPN) ``` На сервере: ``` geoip:ru → block (запретить выход на российские IP) всё остальное → freedom (разрешить) ``` > В v2rayN для Windows эта стратегия уже реализована как пресет «Все, кроме РФ». --- ## 9. Технические детали: xray API (для понимания угрозы Happ) ### Формат конфигурации API ```json { "api": { "tag": "api", "services": [ "HandlerService", "LoggerService", "StatsService", "RoutingService" ] } } ``` ### gRPC методы HandlerService | Метод | Описание | Опасность | |-------|----------|-----------| | `AddInbound` | Добавить новый inbound | Высокая | | `RemoveInbound` | Удалить inbound | Высокая | | `AlterInbound` | Изменить inbound | Высокая | | `AddOutbound` | Добавить outbound | Высокая | | `RemoveOutbound` | Удалить outbound | Высокая | | `AlterOutbound` | Изменить outbound | Высокая | | `GetInboundUsers` | Получить список пользователей inbound | Критическая | | `GetInboundUsersCount` | Количество пользователей | Низкая | ### Как это эксплуатируется 1. Шпион находит gRPC API на localhost (скан портов) 2. Вызывает ReflectionService для получения списка методов 3. Через HandlerService дампит outbound — получает ключ, IP, SNI 4. В сочетании с уязвимостью конфигурации xray → **расшифровка трафика** > О второй уязвимости автор намеренно не раскрывает деталей. --- ## 10. Источники - [Статья на ntc.party](https://ntc.party/) — оригинальная публикация runetfreedom - [Habr: Критическая уязвимость VLESS клиентов](https://habr.com/ru/articles/1020080/) - [POC: per-app-split-bypass](https://github.com/runetfreedom/per-app-split-bypass-poc) - [xray-core SOCKS inbound docs](https://xtls.github.io/en/config/inbounds/socks.html) - [xray-core API docs](https://xtls.github.io/en/config/api.html) - [sing-box SOCKS inbound docs](https://sing-box.sagernet.org/configuration/inbound/socks/) - [sing-box Mixed inbound docs](https://sing-box.sagernet.org/configuration/inbound/mixed/) - [XTLS BBS: Malicious Activity of Closed-Source Xray Clients](https://github.com/XTLS/BBS/issues/8) - [Meduza: Минцифры разослало методичку по VPN](https://meduza.io/news/2026/04/06/mintsifry-razoslalo-rossiyskim-kompaniyam-metodichku-po-poisku-vpn-na-ustroystvah-polzovateley)