# Критическая уязвимость 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)