# Localhost-атака: как Meta, Яндекс и шпионское ПО эксплуатируют loopback-интерфейс
**Дата:** 7 апреля 2026
**Статус:** Верифицировано — исследование USENIX Security 26, POC опубликован
**Оригинал исследования:** [localmess.github.io](https://localmess.github.io/)
**Научная статья:** [Bridges to Self: Silent Web-to-App Tracking on Mobile via Localhost](https://localmess.github.io/assets/bridges-to-self-localmess-usenix-security-26.pdf) (USENIX Security 26)
**POC (Meta/Яндекс):** [github.com/localmess/localhost-abuse](https://github.com/localmess/localhost-abuse)
**POC (VLESS обход split tunneling):** [github.com/runetfreedom/per-app-split-bypass-poc](https://github.com/runetfreedom/per-app-split-bypass-poc)
---
## Оглавление
1. [Корневая проблема: архитектура loopback в ОС](#1-корневая-проблема-архитектура-loopback-в-ос)
2. [Яндекс: localhost-трекинг с 2017 года](#2-яндекс-localhost-трекинг-с-2017-года)
3. [Meta (Facebook): WebRTC STUN SDP Munging](#3-meta-facebook-webrtc-stun-sdp-munging)
4. [Побочный эффект: утечка истории браузера](#4-побочный-эффект-утечка-истории-браузера)
5. [VLESS/xray/sing-box: та же дыра, другой вектор](#5-vlessxraysing-box-та-же-дыра-другой-вектор)
6. [Happ: бэкдор через xray API HandlerService](#6-happ-бэкдор-через-xray-api-handlerservice)
7. [Протокол SOCKS5: как работает аутентификация (RFC 1928/1929)](#7-протокол-socks5-как-работает-аутентификация-rfc-19281929)
8. [xray-core: настройка аутентификации SOCKS5 inbound](#8-xray-core-настройка-аутентификации-socks5-inbound)
9. [sing-box: настройка аутентификации SOCKS/mixed inbound](#9-sing-box-настройка-аутентификации-socksmixed-inbound)
10. [Сводная таблица атак и защит](#10-сводная-таблица-атак-и-защит)
11. [Как защитить VPN-клиенты](#11-как-защитить-vpn-клиенты)
12. [Статус браузеров и патчей](#12-статус-браузеров-и-патчей)
13. [Хронология событий](#13-хронология-событий)
14. [Источники](#14-источники)
---
## 1. Корневая проблема: архитектура loopback в ОС
Все описанные атаки — трекинг Meta, трекинг Яндекса, и уязвимость VLESS-клиентов — эксплуатируют **одну и ту же фундаментальную архитектурную слабость**: неконтролируемый доступ к loopback-интерфейсу (`127.0.0.1`).
### Как устроен loopback
```
┌─────────────────────────────────────────────────────┐
│ Android / Windows │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ App A │ │ App B │ │ Browser │ │
│ │ (Яндекс) │ │ (Шпион) │ │ (Chrome) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ 127.0.0.1 (loopback) │ │
│ │ Общий для ВСЕХ процессов устройства │ │
│ │ НЕ маршрутизируется через VPN/TUN │ │
│ │ НЕ изолирован между профилями │ │
│ │ НЕ требует специальных разрешений │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
```
### Что ОС разрешает на loopback
| Действие | Android | Windows | iOS |
|----------|---------|---------|-----|
| Слушать порт на localhost | ✅ Любое приложение с `INTERNET` | ✅ Любой процесс | ✅ Ограничено фоновым режимом |
| Подключиться к чужому порту на localhost | ✅ Без ограничений | ✅ Без ограничений | ✅ Технически возможно |
| Сканировать все порты localhost | ✅ За секунды | ✅ За секунды | ⚠️ Ограничено фоном |
| Изоляция между профилями (Knox/Shelter) | ❌ Loopback общий | N/A | N/A |
| Изоляция VPN/TUN от loopback | ❌ Loopback вне VPN | ❌ Loopback вне TUN | ❌ |
| JavaScript из браузера → localhost | ✅ Без согласия | ✅ Без согласия | ✅ |
### Почему loopback не изолирован
Это **by design**, а не баг ОС. Приложениям легитимно нужен localhost для:
- Межпроцессного взаимодействия (IPC)
- Локальных серверов разработки
- Коммуникации между компонентами одного приложения
Проблема в том, что **никто не предусмотрел**, что это станет вектором атаки для трекинга и обнаружения VPN.
---
## 2. Яндекс: localhost-трекинг с 2017 года
### Архитектура атаки
```
┌─────────────────────┐ ┌──────────────────────────┐
│ Мобильный браузер │ │ Нативное приложение │
│ (Chrome/Firefox) │ │ Яндекса (Карты/Браузер) │
│ │ │ │
│ ┌────────────────┐ │ │ ┌─────────────────────┐ │
│ │ Яндекс.Метрика │ │ │ │ SDK Яндекс.Метрики │ │
│ │ JavaScript │──┼─────┼─▶│ Слушает TCP порты: │ │
│ │ │ │ HTTP│ │ 29009, 30102 (HTTP) │ │
│ │ │ │ на │ │ 29010, 30103 (HTTPS) │ │
│ │ │◀─┼─────┼──│ │ │
│ └───────┬────────┘ │200OK│ │ Отвечает: AAID + │ │
│ │ │+IDs │ │ зашифрованные ID │ │
│ ▼ │ │ └─────────────────────┘ │
│ Отправляет ID на │ │ │
│ mc.yango.com │ │ │
└─────────────────────┘ └──────────────────────────┘
```
### Пошаговый процесс
**Шаг 1.** Пользователь открывает приложение Яндекса (Карты, Навигатор, Браузер, Поиск, Go). Приложение уходит в фон и запускает фоновый сервис, слушающий порты.
**Шаг 2.** Приложение обращается к `startup.mobile.yandex.net` для получения конфигурации:
```json
{
"ports": [30102, 29009],
"first_delay_seconds": 259200
}
```
Параметр `first_delay_seconds` задаёт задержку перед началом прослушивания (~3 дня после установки). Вероятно, для маскировки — не начинать слушать сразу после установки.
**Шаг 3.** Пользователь открывает браузер, заходит на сайт с Яндекс.Метрикой (~3 млн сайтов по BuiltWith, ~575K по HTTP Archive).
**Шаг 4.** JavaScript Яндекс.Метрики запрашивает зашифрованные параметры с серверов Яндекса.
**Шаг 5.** Скрипт отправляет HTTP/HTTPS-запросы на localhost:
- `http://127.0.0.1:29009/...`
- `https://yandexmetrica.com:30103/...` ← домен резолвится в `127.0.0.1`
> **Ключевой трюк:** домен `yandexmetrica.com` сконфигурирован резолвиться в `127.0.0.1`. Это позволяет использовать HTTPS (порты 29010, 30103) — запросы выглядят как обычные HTTPS к «настоящему» домену, но идут на localhost. Обнаружить это стандартными средствами **практически невозможно**.
**Шаг 6.** SDK Яндекс.Метрики в нативном приложении получает запрос и отвечает HTTP 200 OK с телом, содержащим Base64-закодированные бинарные данные:
- Android Advertising ID (AAID)
- Google Advertising ID
- UUID, специфичные для Яндекса
**Шаг 7.** JavaScript получает эти идентификаторы и отправляет их на `mc.yango.com` вместе с зашифрованными параметрами и информацией о посещённой странице.
**Результат:** Яндекс связывает **эфемерную веб-сессию** (кука, IP, User-Agent) с **постоянным идентификатором устройства** (AAID). Пользователь деанонимизирован на всех сайтах с Яндекс.Метрикой.
### Проверенные приложения Яндекса
| Приложение | Package name | Тестовая версия | Порты |
|------------|-------------|-----------------|-------|
| Яндекс.Карты | `ru.yandex.yandexmaps` | 23.5.0 | 29009, 30102 |
| Яндекс.Навигатор | `ru.yandex.yandexnavi` | 23.5.0 | 29009, 30102 |
| Яндекс.Браузер | `com.yandex.browser` | 25.4.1.100 | 29009, 30102 |
| Яндекс.Поиск | `com.yandex.searchapp` | 25.41 | 29009, 30102 |
| Метро в Европе | `ru.yandex.metro` | 3.7.3 | 29009, 30102 |
| Яндекс Go: Такси | `ru.yandex.taxi` | 5.24.1 | 29009, 30102 |
### Что обходит этот метод
Метод работает **даже если пользователь:**
- ❌ Не залогинен в Яндексе в браузере
- ❌ Использует режим инкогнито
- ❌ Очистил куки и данные браузера
- ❌ Сбросил рекламный идентификатор Android
---
## 3. Meta (Facebook): WebRTC STUN SDP Munging
### Архитектура атаки
```
┌─────────────────────┐ ┌──────────────────────────┐
│ Мобильный браузер │ │ Facebook / Instagram │
│ (Chrome/Edge) │ │ (нативное приложение) │
│ │ │ │
│ ┌────────────────┐ │ │ ┌─────────────────────┐ │
│ │ Meta Pixel JS │ │ │ │ Фоновый сервис │ │
│ │ │──┼─────┼─▶│ TCP: 12387, 12388 │ │
│ │ SDP Munging: │ │WebRTC│ │ UDP: 12580-12585 │ │
│ │ ice-ufrag = │ │STUN │ │ (STUN) │ │
│ │ _fbp cookie │ │ │ │ UDP: 12586-12591 │ │
│ │ │ │ │ │ (TURN) │ │
│ └───────┬────────┘ │ │ └─────────────────────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ Отправляет _fbp на │ │ GraphQL → graph. │
│ facebook.com/tr │ │ facebook.com/graphql │
│ + URL, событие, │ │ + _fbp + Device ID │
│ метаданные │ │ + Facebook Account ID │
└─────────────────────┘ └──────────────────────────┘
```
### Техника SDP Munging
**SDP Munging** — это манипуляция полями SDP (Session Description Protocol) в WebRTC-соединении для передачи произвольных данных.
Meta Pixel вставляет значение куки `_fbp` в поле `ice-ufrag` SDP-предложения. При WebRTC handshake генерируется STUN Binding Request, который отправляется на localhost UDP-порты 12580-12585. Нативное приложение Facebook/Instagram слушает эти порты и извлекает `_fbp` из STUN-пакета.
**Почему это невидимо:**
- WebRTC STUN-трафик **не отображается** в Chrome DevTools
- Нет HTTP-запросов, которые можно было бы перехватить в инспекторе
- Сетевая активность происходит на уровне UDP, ниже HTTP-слоя
### Эволюция техник Meta
| Метод | Начало | Конец | Порты | Обнаружимость |
|-------|--------|-------|-------|---------------|
| HTTP | Сентябрь 2024 | Октябрь 2024 | TCP 12387 | Средняя (видно в DevTools) |
| WebSocket | Ноябрь 2024 | Январь 2025 | TCP 12387 | Средняя |
| WebRTC STUN + SDP Munging | Ноябрь 2024 | Июнь 2025 | UDP 12580-12585 | **Очень низкая** |
| WebRTC TURN (без SDP Munging) | Май 2025 | Июнь 2025 | UDP 12586-12591 | Низкая |
> Meta 4 раза сменила технику за 9 месяцев, каждый раз усложняя обнаружение.
### О куке `_fbp`
- Third most popular first-party cookie в интернете (Web Almanac 2024)
- Присутствует на **~25% из топ-1М сайтов**
- Срок жизни: 90 дней
- First-party cookie = разная на каждом сайте → **нельзя** использовать для кросс-сайтового трекинга
- **НО:** через localhost-атаку Meta связывает разные `_fbp` с одним устройством → кросс-сайтовый трекинг становится возможен
### Масштаб
| Регион | Сайтов с Meta Pixel (из 100K) | Без согласия пользователя |
|--------|-------------------------------|---------------------------|
| США | 17,223 | 13,468 (78.2%) |
| Европа | 15,677 | 11,890 (75.8%) |
| Регион | Сайтов с Яндекс.Метрикой (из 100K) | Без согласия пользователя |
|--------|-------------------------------------|---------------------------|
| США | 1,312 | 1,095 (83.5%) |
| Европа | 1,260 | 1,064 (84.4%) |
### Пошаговый процесс Meta
1. Пользователь открывает Facebook/Instagram → приложение уходит в фон → слушает TCP 12387/12388 и UDP 12580-12585
2. Пользователь открывает браузер → заходит на сайт с Meta Pixel (~5.8 млн сайтов)
3. Meta Pixel JS читает куку `_fbp`
4. JS создаёт WebRTC peer connection с SDP Munging: `ice-ufrag = <_fbp value>`
5. STUN Binding Request с `_fbp` в поле ice-ufrag летит на `127.0.0.1:12580-12585`
6. Facebook/Instagram получают `_fbp`
7. Приложение отправляет GraphQL на `graph.facebook.com/graphql`: `_fbp` + Device ID + Facebook Account ID
8. Meta связывает: веб-сессия ↔ устройство ↔ аккаунт Facebook
---
## 4. Побочный эффект: утечка истории браузера
Поскольку Яндекс использует **обычные HTTP-запросы** (не WebRTC), любое стороннее приложение, слушающее те же порты, может перехватить эти запросы и прочитать заголовок `Origin`, который содержит **URL посещённого сайта**.
### Кто уязвим
| Браузер | Яндекс (утечка истории) | Meta (утечка истории) | Защита |
|---------|------------------------|----------------------|--------|
| Chrome | ✅ Уязвим | ✅ Уязвим | Chrome 137+ — блокировка портов и SDP Munging |
| Firefox | ✅ Уязвим | ❌ Не затронут | В разработке |
| Edge | ✅ Уязвим | ✅ Уязвим | Неизвестно |
| Brave | ❌ Защищён | ❌ Защищён | С 2022: blocklist + запрос согласия на localhost |
| DuckDuckGo | ⚠️ Минимально | ❌ Защищён | Blocklist (3 домена Яндекса отсутствовали — исправлено) |
**Работает в режиме инкогнито: ДА.** Инкогнито не влияет на доступ к localhost.
### POC-демонстрация
Исследователи создали POC-приложение ([github.com/localmess/localhost-abuse](https://github.com/localmess/localhost-abuse)), которое:
1. Слушает порты 29009, 30102 (как приложения Яндекса)
2. Перехватывает HTTP-запросы от Яндекс.Метрики
3. Извлекает `Origin` заголовок
4. Отображает список посещённых сайтов в реальном времени
---
## 5. VLESS/xray/sing-box: та же дыра, другой вектор
### Связь с Meta/Яндекс
Meta и Яндекс доказали: **localhost на Android не изолирован, и это можно массово эксплуатировать.** [[VLESS-SOCKS5-vulnerability|Уязвимость VLESS-клиентов]] — **тот же фундаментальный баг**, но другой вектор атаки.
| Аспект | Meta/Яндекс | VLESS-клиенты |
|--------|------------|---------------|
| **Что на localhost** | Трекинг-сервис приложения | SOCKS5-прокси без аутентификации |
| **Кто атакует** | JavaScript с веб-страницы | Шпионский модуль в приложении |
| **Что получает атакующий** | Device ID (AAID) + связь с аккаунтом | Выходной IP VPN-сервера |
| **Цель** | Деанонимизация + таргетированная реклама | Блокировка VPN-серверов |
| **Фундаментальная проблема** | Loopback не изолирован | Loopback не изолирован |
| **Knox/Shelter помогает?** | Нет | Нет |
| **Инкогнито помогает?** | Нет | N/A |
### Как эксплуатируется VLESS
```
┌──────────────────┐
│ Шпионский модуль │ (Яндекс/WB/Ozon/гос.приложение)
│ в приложении │
└────────┬─────────┘
│ 1. Скан портов localhost (секунды)
▼
┌──────────────────────┐
│ 127.0.0.1:10808 │ SOCKS5 без аутентификации
│ (xray-core inbound) │
└────────┬─────────────┘
│ 2. SOCKS5 handshake (method 0x00 = NO AUTH)
│ 3. CONNECT ifconfig.me:80
▼
┌──────────────────┐
│ xray-core │ → VPN-сервер → ifconfig.me
│ outbound │ ← ответ: "185.xxx.xxx.xxx"
└──────────────────┘
│ 4. Шпион получает выходной IP
▼
РКН блокирует сервер
```
### Почему per-app split tunneling не помогает
```
VpnService (TUN)
┌──────────────┐
Обычное приложение ──▶ │ tun2socks │ ──▶ xray SOCKS5 ──▶ VPN-сервер
└──────────────┘ ▲
│
Шпион ──▶ 127.0.0.1:10808 ────────────────────────┘
(минуя VpnService)
```
VpnService перехватывает трафик через TUN-интерфейс. Но подключение к `127.0.0.1` **не проходит через TUN** — это внутренний loopback. Split tunneling бессилен.
### Почему Knox/Shelter/Island не помогают
Android Private Spaces изолируют файловую систему и VpnService, но **loopback-интерфейс общий** для всех профилей. Процесс из Knox видит `127.0.0.1:10808` основного профиля.
### Контекст: методичка Минцифры РФ
Минцифры разослало аккредитованным IT-компаниям методичку по обнаружению VPN. Характерные порты из методички:
| Тип | Порты |
|-----|-------|
| 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) и nekobox (2080) **пока не указаны**, но полный скан 65535 портов на localhost — секунды.
### Список уязвимых VLESS-клиентов
| Клиент | Платформа | Ядро | SOCKS5 auth | Статус |
|--------|-----------|------|-------------|--------|
| **Happ** | iOS | xray | ❌ | 🔴 Особо опасен (API без auth) |
| 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) |
| Husi | Android | sing-box | ✅ | 🟢 Можно настроить |
| SFA (sing-box) | Android | sing-box | ✅ | 🟢 Ручной JSON |
| saeeddev94/xray | Android | xray | ✅ | 🟢 F-Droid, ручной JSON |
---
## 6. Happ: бэкдор через xray API HandlerService
Клиент Happ — отдельная категория. Помимо уязвимого SOCKS5, он включает **xray gRPC API с HandlerService БЕЗ аутентификации**.
### Сервисы xray API
| Сервис | Назначение | Опасность |
|--------|-----------|-----------|
| **StatsService** | Статистика трафика, онлайн-пользователи | Низкая |
| **LoggerService** | Управление логированием | Низкая |
| **HandlerService** | Добавление/удаление inbound/outbound, дамп конфигов, управление пользователями | 🔴 **Критическая** |
| **RoutingService** | Управление маршрутизацией | Средняя |
| **ReflectionService** | Список доступных API | Средняя |
### gRPC методы HandlerService
| Метод | Что делает |
|-------|-----------|
| `AddInbound` | Добавить новый inbound |
| `RemoveInbound` | Удалить inbound |
| `AlterInbound` | Изменить inbound |
| `AddOutbound` | Добавить outbound |
| `RemoveOutbound` | Удалить outbound |
| `AlterOutbound` | Изменить outbound |
| `GetInboundUsers` | Получить пользователей (включая ключи) |
### Цепочка атаки
```
Шпион → скан портов → gRPC API (без auth)
→ ReflectionService → список методов
→ HandlerService → дамп outbound конфига
→ ключ шифрования + IP сервера + SNI
→ [+ вторая уязвимость конфигурации] → расшифровка трафика
```
**Позиция Happ:** заявили, что API нужен «для статистики». Это ложь — для статистики достаточно `StatsService`. При смене конфига Happ перезапускает xray целиком, а не использует HandlerService.
**Рекомендация:** немедленно удалить Happ. Заблокировать `Happ/*` по UserAgent на серверах подписок. Один пользователь с Happ компрометирует весь сервер.
---
## 7. Протокол SOCKS5: как работает аутентификация (RFC 1928/1929)
### Зачем нужна аутентификация SOCKS5
Без аутентификации SOCKS5-прокси — это **открытая дверь**: кто угодно может подключиться и маршрутизировать через него трафик. В контексте VPN это означает, что шпионский модуль может:
- Обнаружить прокси сканированием портов
- Подключиться без учётных данных
- Узнать выходной IP VPN-сервера
- Гонять через прокси произвольный трафик
Аутентификация добавляет **барьер**: без знания логина/пароля подключение отклоняется.
### Фаза 1: Согласование метода (RFC 1928)
```
Клиент → Сервер:
+-----+----------+----------+
| VER | NMETHODS | METHODS |
+-----+----------+----------+
| 0x05| 0x01 | 0x02 | ← «Я хочу SOCKS5, предлагаю метод 0x02 (пароль)»
+-----+----------+----------+
Сервер → Клиент:
+-----+--------+
| VER | METHOD |
+-----+--------+
| 0x05| 0x02 | ← «Принято, используем метод 0x02»
+-----+--------+
```
**Методы аутентификации:**
- `0x00` — **NO AUTHENTICATION REQUIRED** ← уязвимый вариант
- `0x01` — GSSAPI
- `0x02` — **USERNAME/PASSWORD** ← безопасный вариант
- `0xFF` — нет подходящего метода (отказ)
### Фаза 2: Аутентификация по логину/паролю (RFC 1929)
```
Клиент → Сервер:
+-----+------+----------+------+----------+
| VER | ULEN | UNAME | PLEN | PASSWD |
+-----+------+----------+------+----------+
| 0x01| 0x08 | "user123"| 0x0C | "pass456abc"|
+-----+------+----------+------+----------+
Сервер → Клиент:
+-----+--------+
| VER | STATUS |
+-----+--------+
| 0x01| 0x00 | ← 0x00 = успех, любое другое = отказ
+-----+--------+
```
**Поля:**
- `VER` = `0x01` (версия подпротокола аутентификации)
- `ULEN` = длина логина (1-255 байт)
- `UNAME` = логин
- `PLEN` = длина пароля (1-255 байт)
- `PASSWD` = пароль
> ⚠️ **Важно:** логин и пароль передаются **открытым текстом**. RFC 1929: *«Since the request carries the password in cleartext, this subnegotiation is not recommended for environments where 'sniffing' is possible and practical.»* Но для localhost это не проблема — трафик не выходит за пределы устройства.
### Фаза 3: Запрос (после успешной аутентификации)
```
Клиент → Сервер:
+-----+-----+------+------+----------+----------+
| VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+-----+-----+------+------+----------+----------+
| 0x05| 0x01| 0x00 | 0x01 | IP addr | port |
+-----+-----+------+------+----------+----------+
```
**CMD:**
- `0x01` — CONNECT (TCP)
- `0x02` — BIND
- `0x03` — UDP ASSOCIATE
### Проблема UDP ASSOCIATE
UDP ASSOCIATE (`CMD = 0x03`) создаёт UDP-ретранслятор. **Но:** после успешной TCP-аутентификации UDP-датаграммы **не проходят повторную аутентификацию**.
```
UDP-датаграмма (RFC 1928, Section 7):
+-----+------+------+----------+----------+----------+
| RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
+-----+------+------+----------+----------+----------+
| 2 | 1 | 1 | Variable | 2 | Variable |
+-----+------+------+----------+----------+----------+
```
В заголовке UDP-датаграммы **нет поля для аутентификации**. RFC 1928 допускает опциональное шифрование/целостность, но большинство реализаций (включая xray и sing-box) **не реализуют** per-packet auth для UDP.
**Вывод:** для полной защиты от localhost-атак **UDP нужно отключать** или блокировать фаерволом.
---
## 8. xray-core: настройка аутентификации SOCKS5 inbound
### Уязвимая конфигурация (как есть сейчас)
```json
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
]
}
```
**Проблемы:**
- `"auth": "noauth"` — любой процесс на устройстве может подключиться
- `"udp": true` — UDP тоже доступен без ограничений
- Порт 10808 — стандартный, легко угадывается
### Безопасная конфигурация
```json
{
"inbounds": [
{
"tag": "socks-in",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "password",
"accounts": [
{
"user": "xf_a8b3c2d1",
"pass": "7e4f91d0c3b8a2e5"
}
],
"udp": false
}
}
]
}
```
**Что изменилось:**
- `"auth": "password"` — требуется аутентификация
- `"accounts"` — массив учётных записей `{user, pass}`
- `"udp": false` — UDP отключён (убирает вектор атаки через UDP ASSOCIATE)
### Поля конфигурации xray SOCKS inbound
| Поле | Тип | По умолчанию | Описание |
|------|-----|-------------|----------|
| `auth` | `"noauth"` / `"password"` | `"noauth"` | Режим аутентификации |
| `accounts` | `[{user, pass}]` | — | Учётные записи (только при `auth: "password"`) |
| `udp` | `boolean` | `false` | Включение UDP ASSOCIATE |
| `ip` | `string` | auto | IP для UDP-ответов |
| `userLevel` | `number` | `0` | Уровень политик |
### Рекомендации для xray-клиентов
1. **Генерировать credentials при каждом запуске** — рандомные user/pass, не хардкодить
2. **Отключить UDP** (`"udp": false`) — UDP не аутентифицируется per-packet
3. **Слушать только на 127.0.0.1** — никогда на `0.0.0.0`
4. **Рандомизировать порт** — не использовать стандартный 10808
5. **Не включать HandlerService** в API — достаточно StatsService/LogService
---
## 9. sing-box: настройка аутентификации SOCKS/mixed inbound
### Уязвимая конфигурация
```json
{
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "0.0.0.0",
"listen_port": 2080
}
]
}
```
**Проблемы:**
- Нет массива `users` — аутентификации нет
- `"listen": "0.0.0.0"` — доступен из сети (ещё хуже чем localhost)
- Порт 2080 — стандартный для nekobox
### Безопасная конфигурация
```json
{
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 2080,
"users": [
{
"username": "sb_d4c1a7f2",
"password": "9e2f5b83a1c7d0e4"
}
]
}
]
}
```
### Поля конфигурации sing-box SOCKS/mixed inbound
| Поле | Тип | Описание |
|------|-----|----------|
| `type` | `"socks"` / `"mixed"` | `mixed` поддерживает SOCKS4/4a/5 + HTTP |
| `listen` | `string` | Адрес прослушивания |
| `listen_port` | `number` | Порт |
| `users` | `[{username, password}]` | Учётные записи (без этого поля — без auth) |
| `set_system_proxy` | `boolean` | Автоматическая системная прокси-настройка |
### Разница SOCKS vs Mixed
| Возможность | `type: "socks"` | `type: "mixed"` |
|-------------|-----------------|-----------------|
| SOCKS4/4a | ✅ | ✅ |
| SOCKS5 | ✅ | ✅ |
| HTTP-прокси | ❌ | ✅ |
| `users` (аутентификация) | ✅ | ✅ (для SOCKS и HTTP) |
| `set_system_proxy` | ❌ | ✅ |
### Позиция разработчика sing-box
Разработчик sing-box считает эту проблему **«skill issue»** клиентов: sing-box **не создаёт** mixed inbound автоматически — это делают конкретные клиенты и генераторы конфигов. Если не добавлять mixed inbound в конфиг, прокси не будет.
---
## 10. Сводная таблица атак и защит
| Механизм | Против Meta/Яндекс | Против VLESS-скана | Против Happ API |
|----------|--------------------|--------------------|-----------------|
| SOCKS5 auth (password) | N/A | ✅ Блокирует подключение | N/A |
| Отключение UDP | N/A | ✅ Убирает вектор | N/A |
| Рандомный порт | N/A | ⚠️ Замедляет, не блокирует | N/A |
| iptables/root | ✅ Можно заблокировать | ✅ Можно заблокировать | ✅ |
| Knox/Shelter/Island | ❌ Не помогает | ❌ Не помогает | ❌ |
| Режим инкогнито | ❌ Не помогает | N/A | N/A |
| Очистка куки | ❌ Не помогает | N/A | N/A |
| Brave Browser | ✅ Blocklist + consent | N/A | N/A |
| Chrome 137+ | ✅ Блокировка портов | N/A | N/A |
| Отключение HandlerService | N/A | N/A | ✅ Убирает API |
| Блокировка Happ по UserAgent | N/A | N/A | ✅ На сервере |
| Отдельное устройство | ✅ Физическая изоляция | ✅ | ✅ |
| Windows Firewall (process) | ✅ | ✅ | ✅ |
---
## 11. Как защитить VPN-клиенты
### Для разработчиков клиентов
1. **SOCKS5 auth по умолчанию** — генерировать рандомные credentials при каждом запуске, передавать их только доверенным приложениям
2. **Отключить UDP** в SOCKS5 inbound — UDP ASSOCIATE не поддерживает per-packet auth
3. **Рандомизировать порт** — не стандартные 10808/2080/1080
4. **Только 127.0.0.1** — никогда 0.0.0.0
5. **Не включать xray API HandlerService** — для статистики хватит StatsService/LogService
6. **На Android с root:** добавить iptables-правила, ограничивающие доступ к порту по UID процесса
### Для пользователей Android
1. Использовать клиенты с SOCKS5 auth: **Husi**, **SFA**, **saeeddev94/xray**
2. **Удалить Happ** немедленно
3. Держать российское ПО на **отдельном устройстве**
4. Не полагаться на Knox/Shelter — loopback не изолирован
5. С root: `iptables -I OUTPUT -p tcp -d 127.0.0.1 --dport 10808 -j DROP` для всех кроме VPN-клиента
### Для пользователей Windows
1. Windows Firewall: правила на блокировку доступа к портам 10808/10809 для всех процессов кроме xray.exe и доверенных
2. В xray routing: `process name` для разрешённых приложений
3. Рандомный порт вместо стандартного 10808
### Для администраторов серверов
1. **Раздельные входной/выходной IP** — утечка выходного не компрометирует входной
2. **CloudFlare WARP на выходе** — маскировка выходного IP
3. **geoip:ru → block на сервере** — шпион не сможет связать трафик
4. **Блокировка Happ** по UserAgent `Happ/*`
### Маршрутизация
**На клиенте:**
```
geoip:ru → direct (российские IP напрямую)
geoip:private → direct (локальные сети)
остальное → proxy (через VPN)
```
**На сервере:**
```
geoip:ru → block (запретить выход на РФ)
остальное → freedom (разрешить)
```
---
## 12. Статус браузеров и патчей
### Meta Pixel
| Дата | Событие |
|------|---------|
| Сентябрь 2024 | Начало HTTP-коммуникации с localhost |
| Ноябрь 2024 | Переход на WebSocket, затем WebRTC STUN |
| Май 2025 | Добавлен WebRTC TURN (обход блокировки SDP Munging) |
| 26 мая 2025 | Chrome 137 — блокировка SDP Munging и абьюзимых портов |
| **3 июня 2025** | **Meta полностью прекратила localhost-трекинг, код удалён** |
### Яндекс.Метрика
| Дата | Событие |
|------|---------|
| Февраль 2017 | Начало HTTP-коммуникации с localhost |
| Май 2018 | Добавлен HTTPS (порты 29010, 30103) |
| Июнь 2025 | Яндекс сообщил о прекращении использования и удалении функции |
### Браузеры
| Браузер | Статус | Детали |
|---------|--------|--------|
| Chrome 137+ | ✅ Исправлено | Блокировка портов + SDP Munging |
| Brave 1.54+ | ✅ Защищён с 2022 | Blocklist + localhost consent |
| DuckDuckGo | ✅ Исправлено | Обновлён blocklist |
| Firefox 139 | 🟡 В разработке | Патч в процессе |
| Edge | ❌ Неизвестно | Chromium-based, должен следовать за Chrome |
### VLESS-клиенты
На **7 апреля 2026** ни один массовый VLESS-клиент не добавил SOCKS5 аутентификацию, несмотря на уведомление 10 марта 2026.
---
## 13. Хронология событий
| Дата | Событие |
|------|---------|
| Февраль 2017 | Яндекс начинает localhost-трекинг через HTTP |
| Май 2018 | Яндекс добавляет HTTPS-канал (yandexmetrica.com → 127.0.0.1) |
| 2022 | Brave добавляет защиту localhost (consent + blocklist) |
| Сентябрь 2024 | Meta начинает localhost-трекинг через HTTP |
| Ноябрь 2024 | Meta переходит на WebRTC STUN SDP Munging |
| 10 марта 2026 | runetfreedom уведомляет разработчиков VLESS-клиентов об уязвимости SOCKS5 |
| Апрель 2026 | Минцифры рассылает методичку по обнаружению VPN |
| 7 апреля 2026 | Публикация уязвимости VLESS-клиентов, ни один клиент не исправлен |
| 26 мая 2025 | Chrome 137 блокирует SDP Munging и абьюзимые порты |
| 3 июня 2025 | Meta полностью удаляет код localhost-трекинга |
| Июнь 2025 | Публикация исследования USENIX Security 26 (localmess.github.io) |
---
## 14. Источники
### Исследование Meta/Яндекс (LocalMess)
- [localmess.github.io — оригинал](https://localmess.github.io/)
- [Bridges to Self — PDF статья USENIX Security 26](https://localmess.github.io/assets/bridges-to-self-localmess-usenix-security-26.pdf)
- [POC: localhost-abuse](https://github.com/localmess/localhost-abuse)
- [The Register: Meta halts localhost tracking](https://www.theregister.com/2025/06/03/meta_pauses_android_tracking_tech/)
- [AdGuard: Meta and Yandex abuse localhost](https://adguard.com/en/blog/meta-yandex-abuse-localhost-to-track-users.html)
- [Android Police: researchers catch Meta](https://www.androidpolice.com/meta-yandex-apps-de-anonymize-localhost-tracking/)
- [Habr: перевод статьи](https://habr.com/ru/articles/920714/)
### Уязвимость VLESS
- [ntc.party: оригинальная публикация runetfreedom](https://ntc.party/)
- [Habr: Критическая уязвимость VLESS клиентов](https://habr.com/ru/articles/1020080/)
- [POC: per-app-split-bypass](https://github.com/runetfreedom/per-app-split-bypass-poc)
- [XTLS BBS: Malicious Activity of Closed-Source Xray Clients](https://github.com/XTLS/BBS/issues/8)
### Документация протоколов
- [RFC 1928 — SOCKS Protocol Version 5](https://www.rfc-editor.org/rfc/rfc1928)
- [RFC 1929 — Username/Password Auth for SOCKS V5](https://www.rfc-editor.org/rfc/rfc1929)
- [xray-core SOCKS inbound](https://xtls.github.io/en/config/inbounds/socks.html)
- [xray-core API](https://xtls.github.io/en/config/api.html)
- [sing-box SOCKS inbound](https://sing-box.sagernet.org/configuration/inbound/socks/)
- [sing-box Mixed inbound](https://sing-box.sagernet.org/configuration/inbound/mixed/)
### Защита браузеров
- [Brave: localhost permission](https://brave.com/privacy-updates/27-localhost-permission/)
- [Chrome 137 release](https://chromereleases.googleblog.com/2025/05/)
- [Meduza: методичка Минцифры](https://meduza.io/news/2026/04/06/mintsifry-razoslalo-rossiyskim-kompaniyam-metodichku-po-poisku-vpn-na-ustroystvah-polzovateley)