wiki.zapret.moe заблокирован в России, читатели до редиректа не доходят. Во всех заметках, плашках [!mirror], ссылках и скриптах домен заменён на wiki2.zapret.moe; в AGENTS.md записано правило — ссылаться только на wiki2. Скрипт статистики считает своими заходы с обоих доменов. Чужие незакоммиченные правки в nuc/ в коммит не попали — только замена домена. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
699 lines
40 KiB
Markdown
699 lines
40 KiB
Markdown
---
|
||
description: "Как Meta и Яндекс следят через localhost и почему SOCKS5 без пароля в VLESS-клиентах выдаёт VPN: разбор атак и защита в xray и sing-box."
|
||
---
|
||
|
||
> [!mirror] Резервное зеркало
|
||
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Localhost-tracking-Meta-Yandex-SOCKS5](https://wiki2.zapret.moe/Localhost-tracking-Meta-Yandex-SOCKS5)
|
||
|
||
# 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)
|
||
**Связанная заметка:** [[cloudflare-quick-tunnel|Quick Tunnel от Cloudflare]] — обратный случай: локальный порт выставляют в интернет намеренно, одной командой и без аутентификации
|
||
|
||
---
|
||
|
||
## Оглавление
|
||
|
||
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
|
||
|
||
![[wiki-anim-loopback.svg|Схема: 127.0.0.1 общий для всех процессов устройства]]
|
||
|
||
### Что ОС разрешает на 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 года
|
||
|
||
### Архитектура атаки
|
||
|
||
![[wiki-anim-yandex-metrica.svg|Анимация: Яндекс.Метрика в браузере получает идентификаторы у приложения по localhost]]
|
||
|
||
### Пошаговый процесс
|
||
|
||
**Шаг 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
|
||
|
||
### Архитектура атаки
|
||
|
||
![[wiki-anim-meta-pixel.svg|Анимация: Meta Pixel связывает веб-сессию с устройством и аккаунтом через localhost]]
|
||
|
||
### Техника 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
|
||
|
||
![[wiki-anim-socks5-spy.svg|Анимация: как шпионский модуль получает выходной IP через открытый SOCKS5]]
|
||
|
||
### Почему per-app split tunneling не помогает
|
||
|
||
![[wiki-anim-socks5-bypass.svg|Анимация: шпион обращается на localhost: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` | Получить пользователей (включая ключи) |
|
||
|
||
### Цепочка атаки
|
||
|
||
![[wiki-anim-grpc-chain.svg|Схема: цепочка атаки через открытый gRPC API прокси-ядра]]
|
||
|
||
**Позиция Happ:** заявили, что API нужен «для статистики». Это ложь — для статистики достаточно `StatsService`. При смене конфига Happ перезапускает xray целиком, а не использует HandlerService.
|
||
|
||
**Рекомендация:** немедленно удалить Happ. Заблокировать `Happ/*` по UserAgent на серверах подписок. Один пользователь с Happ компрометирует весь сервер.
|
||
|
||
> [!warning] Открытый API убрали в день огласки
|
||
> По коду приложения ([[xray/happ-problems|подробный разбор]]) `HandlerService` была в Happ для Android 3.16.1 от 1 апреля 2026 года и исчезла в 3.16.2 от 7 апреля — в день публикации разбора на Хабре. Но закрытый код, собственные домены телеметрии и SOCKS5 без пароля по умолчанию у Happ остаются актуальными на 18 октября 2026 года.
|
||
|
||
---
|
||
|
||
## 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-адрес | порт |
|
||
|
||
**CMD:**
|
||
- `0x01` — CONNECT (TCP)
|
||
- `0x02` — BIND
|
||
- `0x03` — UDP ASSOCIATE
|
||
|
||
### Проблема UDP ASSOCIATE
|
||
|
||
UDP ASSOCIATE (`CMD = 0x03`) создаёт UDP-ретранслятор. **Но:** после успешной TCP-аутентификации UDP-датаграммы **не проходят повторную аутентификацию**.
|
||
|
||
UDP-датаграмма (RFC 1928, раздел 7):
|
||
|
||
| RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||
|:--|:--|:--|:--|:--|:--|
|
||
| 2 байта | 1 | 1 | переменный | 2 | переменный |
|
||
|
||
В заголовке 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)
|