Игра в имитацию: как современные решения делают Wireguard невидимым для DPI

Игра в имитацию: как современные решения делают Wireguard невидимым для DPI — Cybersecurity | Versia.media

WireGuard довольно быстро завоевал популярность как VPN-протокол: простой, быстрый, с аккуратной архитектурой и без тяжёлого наследия, он выгодно отличался от таких монстров, как IPsec и OpenVPN. Однако, как это нередко случается, сильная сторона со временем превратилась и в слабое место. Протокол оказался не только удобным и предсказуемым, но и легко распознаваемым, а значит, и относительно простым для блокировки.

Как только начались попытки блокировать WireGuard, появились и первые способы его маскировки. Сначала это были довольно простые приёмы, например, добавление нескольких мусорных UDP-пакетов перед хендшейком, чтобы сбить DPI в начале сессии. Затем появилась AmneziaWG, которая пошла дальше и начала изменять сам внешний вид пакетов WireGuard: заголовки, размеры, дополнительные junk-данные внутри хендшейка. В AmneziaWG 2.0 пространства для манёвра стало ещё больше: к уже существовавшим S1–S2 добавились S3–S4, и управляемые вставки стало можно применять не только к хендшейку, но и к другим типам сообщений, включая основной поток данных. Параллельно развивалась и идея имитационных пакетов: перед хендшейком можно было отправлять не просто случайный мусор, а пакеты, похожие на трафик другого протокола, чтобы сбить первичную классификацию.

В этой статье речь пойдёт о следующем шаге в развитии этой идеи. Если раньше имитационные пакеты работали в основном как короткая дымовая завеса перед хендшейком, то теперь имитация переносится в сам поток: транспортные пакеты AmneziaWG на проводе начинают выглядеть как QUIC, DNS, STUN или SIP.

«Простота хуже воровства»

Когда говорят, что DPI распознаёт WireGuard, легко представить себе нечто почти магическое: умная коробка где-то у провайдера вскрывает пакеты, анализирует содержимое, строит сложные модели и только потом выносит вердикт. На практике всё часто проще.

У WireGuard есть фиксированные типы сообщений, предсказуемые размеры начальных пакетов, характерный порядок обмена в начале сессии. Handshake Initiation, затем Handshake Response, потом транспортные данные. Всё это передаётся поверх UDP, и первые пакеты соединения выглядят достаточно узнаваемо.

Для DPI это удобная ситуация. Достаточно посмотреть на внешние признаки: размер пакетов, порядок, первые байты, поведение UDP-сессии. Если они складываются в знакомую картину, поток можно пометить как WireGuard. А дальше с ним можно делать всё что угодно: блокировать, замедлять, рвать после хендшейка или просто отправлять в отдельную категорию для дальнейшего анализа.

В этом смысле WireGuard слишком «честно» представляется. Он аккуратный, минималистичный и предсказуемый. Для протокола это достоинство, но в сетях с агрессивной фильтрацией такая предсказуемость превращается в серьёзный недостаток.

«За деревьями леса не видеть»

Первая идея была простой: если DPI ловит WireGuard по характерному началу сессии, нужно сделать так, чтобы это начало перестало быть таким заметным.

Перед настоящим хендшейком клиент отправляет несколько UDP-пакетов со случайными данными. Они не несут никакой смысловой нагрузки, а просто создают шумовой фон. В результате Handshake Initiation уже не появляется первым пакетом в пустом потоке, а теряется среди других датаграмм.

На серьёзную стеганографическую защиту это, конечно, не похоже. Но сама идея важная: клиент больше не просто надеется, что DPI его не узнает, а начинает сам формировать картину, которую увидит наблюдатель.

Против простых фильтров такой подход действительно может работать. Особенно если блокировка завязана на первые пакеты UDP-сессии. Увидели Handshake Initiation на чистом месте, пометили поток как WireGuard и заблокировали. Не увидели, значит у соединения есть шанс пройти дальше.

Но у случайного шума есть понятный предел. Если перед VPN-сессией внезапно появляется набор пакетов со случайным содержимым, это тоже становится признаком. Да, это уже не чистый WireGuard. Но это и не нормальный прикладной протокол. Обычные протоколы редко начинают разговор с бессмысленного набора байтов без какой-либо структуры.

Шум может сбить с толку простой классификатор, но сам по себе он не делает трафик похожим на что-то разрешённое. Он скорее говорит: «я не WireGuard, я просто что-то странное». А в сетях с жёсткой фильтрацией «что-то странное» тоже не самая удачная маска.

AmneziaWG: стереть отпечатки WireGuard

Следующим заметным шагом стала AmneziaWG. Если первые попытки маскировки прятали WireGuard за шумом перед сессией, то AmneziaWG пошла глубже и начала менять уже сам внешний вид пакетов.

Идея простая: если DPI узнаёт протокол по характерным признакам, нужно сделать эти признаки менее стабильными. Поэтому AmneziaWG может добавлять junk-пакеты перед хендшейком, менять значения заголовков типов сообщений и добавлять дополнительные данные внутрь хендшейка, чтобы он не выделялся привычным размером.

В результате маскируется уже не только начало сессии, но и сами пакеты, по которым раньше было удобно определять WireGuard. DPI больше не видит эталонный хендшейк: размеры меняются, типы пакетов выглядят иначе, сигнатуры размываются.

На практике self-hosted AmneziaWG во многих случаях действительно оказывается заметно устойчивее обычного WireGuard. И всё же это по-прежнему игра в сокрытие: сделать WireGuard менее похожим на самого себя.

AmneziaWG 2.0: больше места для манёвра

В AmneziaWG 2.0 идея маскировки получила больше пространства. Если раньше основные вставки касались хендшейка, то теперь управляемая добавка появилась и для других типов сообщений.

С практической точки зрения это четыре поля S1–S4. Каждое из них отвечает за свой тип WireGuard-пакета:

S1 для Handshake Initiation; S2 для Handshake Response; S3 для Cookie Reply; S4 для Transport Data.

Рядом с ними идут H1–H4. Это значения, которыми AmneziaWG заменяет стандартный заголовок типа сообщения WireGuard. Причём во второй версии их можно задавать не только конкретными числами, но и диапазонами. В результате пакет становится менее стабильным внешне: перед ним появляется управляемая вставка, а привычный WireGuard-заголовок перестаёт быть таким очевидным.

Однако, на мой взгляд, самое интересное расширение AmneziaWG 2.0 это S4, который относится к Transport Data, то есть к основному потоку данных. Это значит, что пространство для маскировки появляется не только в начале соединения, а на протяжении всей жизни туннеля.

И тут возникает естественный вопрос: если в каждом пакете уже есть место для управляемой вставки, обязательно ли заполнять его случайными байтами? Почему бы не использовать это место так, чтобы пакет стал похож не на случайный шум, а на другой протокол?

От случайности к имитации

Итак, идея в том, чтобы заменить случайный шум в S1–S4 осмысленной структурой, похожей на трафик другого протокола. А в сочетании с правильно сгенерированными имитационными и мусорными пакетами это помогает сформировать более правдоподобный внешний профиль соединения.

Очевидно, для полноценной двусторонней имитации нужны обе стороны VPN-туннеля. На сервере за это отвечает amneziawg-proxy: отдельный UDP-прокси, который ставится перед AmneziaWG-сервером. Сам AmneziaWG при этом уводится на loopback, например на 127.0.0.1, а публичный UDP-порт занимает прокси. Именно этот порт видит DPI снаружи, и именно с ним взаимодействуют внешние проверки.

Задача серверного прокси состоит в двух вещах. Во-первых, он отвечает на активные пробники как настоящий сервис. Если на порт приходит QUIC Initial, DNS-запрос, STUN Binding Request или SIP-запрос, прокси возвращает ожидаемый ответ соответствующего протокола: для QUIC это Version Negotiation, для DNS это DNS-ответ, для STUN это Binding Success, для SIP это 100 Trying.

Это важно, потому что пассивной маскировки уже недостаточно. Фильтр может не только смотреть на проходящий трафик, но и сам проверять подозрительный порт: действительно ли там QUIC, DNS, STUN или SIP. Если порт молчит или отвечает не так, как должен отвечать настоящий сервис, маска быстро слетает.

Во-вторых, прокси работает с серверным направлением трафика. AmneziaWG-пакеты уже содержат управляемые S-вставки, и прокси может перезаписать это место не случайными байтами, а байтами, характерными для выбранного протокола. В результате ответные пакеты со стороны сервера начинают выглядеть как QUIC, DNS, STUN или SIP, хотя внутри по-прежнему остаётся зашифрованная нагрузка AmneziaWG.

Но это только половина картины. Чтобы имитация была двунаправленной, клиент тоже должен уметь формировать пакеты в том же стиле. Эту часть реализует WireSock Secure Connect 3.5+. Он создаёт клиентские имитационные пакеты и заполняет S-вставки на своей стороне так, чтобы направление от клиента к серверу выглядело не как обычный AmneziaWG, а как трафик выбранного протокола.

Именно связка WireSock Secure Connect 3.5+ и amneziawg-proxy делает подход полноценным. Прокси отвечает за серверную сторону, probe responses и маскировку ответного потока. WireSock Secure Connect берёт на себя клиентскую сторону. Вместе они меняют внешний вид датаграмм на проводе так, чтобы для стороннего наблюдателя это был не WireGuard и не «обфусцированный WireGuard», а трафик выбранного протокола: QUIC, DNS, STUN или SIP.

Этот подход развивает идею имитационных пакетов I1–I4 и переносит её на следующий уровень. Сами эти параметры, на мой взгляд, не самые удобные для пользователя, поэтому в WireSock Secure Connect я заменил их более понятной схемой: домен, протокол и профиль браузера. Но идея эффективная: вместо случайного шума перед хендшейком показать DPI трафик, похожий на другой протокол. А если сервер ещё и отвечает на эти пакеты как настоящий сервис, а последующий поток продолжает выглядеть как выбранный протокол, то маска получается намного убедительнее.

QUIC-режим на публичном порту сервера. Wireshark разбирает поток как обычный QUIC: Initial с CRYPTO, затем Handshake и Protected Payload. Внутри при этом остаётся AmneziaWG-туннель, а внешняя форма пакетов задаётся amneziawg-proxy и Wiresock Secure Connect.

Почему имитация лучше шума

Случайный padding помогает спрятать узнаваемые признаки, но не делает трафик похожим на нормальный протокол. Он скорее создаёт неопределённость: это уже не чистый WireGuard, но и не что-то привычное для сети.

Протокольная имитация работает иначе. Её задача не просто убрать сигнатуру WireGuard, а подставить на её место более правдоподобную картину. Чтобы пакет выглядел не как набор случайных байтов, а как QUIC, DNS, STUN, SIP или что-то еще.

Для DPI это уже другая ситуация. Пакет не просто перестаёт быть похожим на WireGuard, а начинает выглядеть как нормальный пакет выбранного протокола. В начале датаграммы появляется осмысленная структура QUIC, DNS, STUN или SIP, а не случайные байты, которые просто маскируют WireGuard-заголовок.

Конечно, это не делает трафик невидимым. Но меняется стоимость анализа. Одно дело заблокировать всё, что похоже на WireGuard или на странный UDP-шум. Другое дело начать агрессивно подозревать трафик, который выглядит как QUIC, DNS, STUN или SIP, и при этом не задеть настоящие сервисы, использующие те же протоколы.

В сетях, где ложные срабатывания нежелательны, это уже совсем другая игра.

Режимы имитации

На данный момент в amneziawg-proxy доступны несколько режимов имитации: QUIC, DNS, STUN, SIP и auto. В режиме auto протокол имитации выбирается отдельно для каждого клиента по первому пакету, который приходит от него на публичный порт.

QUIC выглядит наиболее естественным выбором по умолчанию. Современный интернет и так полон QUIC/HTTP3-трафика: он ходит поверх UDP, зашифрован, высокоэнтропиен и обычно не выглядит как старый добрый текстовый протокол. Если нужно выбрать маску «по умолчанию», QUIC выглядит логично.

Другой интересный вариант это DNS. С одной стороны, DNS часто разрешён даже в довольно строгих сетях. С другой стороны, DNS-трафик обычно ожидается на 53-м порту, и его поведение могут внимательно контролировать. Поэтому здесь особенно важно, чтобы пакет не выглядел как WireGuard со случайной вставкой в начале, а нормально разбирался как DNS-сообщение.

DNS-режим. На захвате видны обычные DNS-запросы и ответы: A/AAAA/HTTPS-записи, поддомены, OPT-записи. При включённой пересылке DNS-пробники могут получать реальные ответы upstream-резолвера, поэтому порт выглядит не просто как заглушка, а как рабочий DNS-сервис.

STUN полезен там, где в сети ожидается WebRTC или другой NAT traversal-трафик. В таком режиме пакеты выглядят как обычные Binding Request / Binding Success Response. Это не самый универсальный камуфляж, но в подходящей среде он хорошо вписывается в ожидаемую картину.

STUN-режим. Клиентские пакеты выглядят как Binding Request, серверные — как Binding Success Response с XOR-MAPPED-ADDRESS. Для наблюдателя это похоже на обычный NAT traversal/WebRTC-подобный обмен.

SIP выглядит самым необычным режимом. Идея маскировать VPN под VoIP-сигнализацию на первый взгляд кажется странной, но на практике картина получается довольно убедительной: текстовые SIP-заголовки, запросы INVITE, OPTIONS, REGISTER, SUBSCRIBE и привычные ответы вроде 100 Trying или 200 OK. Этот режим хорошо показывает, насколько далеко мы ушли от простого случайного padding: трафик уже не просто прячется, а пытается сыграть роль вполне конкретного сервиса.

SIP-режим. В захвате видны INVITE, CANCEL, NOTIFY, OPTIONS, REGISTER, SUBSCRIBE и ответы вроде 100 Trying, 180 Ringing, 200 OK. На проводе такой поток выглядит как сигнализация VoIP-инфраструктуры.

Режим auto нужен для сценария, когда один и тот же сервер принимает клиентов с разными режимами имитации. В этом режиме прокси не фиксирует один протокол заранее, а смотрит на первый пакет от каждого клиента. Если

← Cybersecurity