# 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)