В 2021 году VPN протокол WireGuard стал настолько популярен в Египте, что удостоился чести пополнить список заблокированных, несказанно “обрадовав” не только клиентов Cloudflare Warp+, Mullvad Wireguard и других коммерческих VPN-провайдеров, но и некоторых пользователей корпоративных VPN. Предварительные исследования показали, что по всей видимости DPI нацелен на WireGuard Handshake Initiate пакеты, которые имеют фиксированный размер (148 байт) и узнаваемую структуру (первые четыре байта UDP пакета [0x01, 0x00, 0x00, 0x00]).
Чтобы понять, как DPI может обнаружить и заблокировать WireGuard нужно привнести немного теории. Сам по себе WireGuard протокол предельно простой, трафик упаковывается в совершенно типовой UDP с добавлением небольшого заголовка. Для согласования WireGuard туннеля, как правило, достаточно двух (трех, если считать Keepalive) пакетов:
Сторона желающая установить туннель (клиент) отправляет Hadshake Initiation другой стороне (сервер).
Если сервер в данный момент готов к подключению, то он отвечает клиенту пакетом Handshake Response.
Клиент получает Handshake Response и отвечает Keepalive пакетом, который представляет собой UDP пакет с WireGuard заголовком типа "данные" нулевого размера. В дальнейшем ключи обновляются повторяя описанную процедуру примерно каждые две минуты.
Таким образом, кажется, что для блокировки WireGuard DPI достаточно отслеживать UDP пакеты размером в 148 байт и проверять в них первые четыре байта на соответствие сигнатуре [0x01, 0x00, 0x00, 0x00]. Однако, стоит упомянуть, что корпоративные реализации WireGuard могут использовать зарезервированные три байта (поле Reserved) для собственных нужд (например, в Jamf Private Access они используются как идентификатор сессии). К тому же не исключено, что рано или поздно им найдется применение и в официальном клиенте. Так что для большей точности имеет смысл ограничиться только первым байтом UDP пакета. С другой стороны блокировка всех UDP пакетов размером в 148 байт с первым байтом 0x01 выглядит довольно рискованно. То же самое можно сказать и о Handshake Response пакете, который так же имеет фиксированный размер (92 байта) и схожую сигнатуру с тремя зарезервированными байтами [0x02, 0x00, 0x00, 0x00].
Как же тогда может выглядеть возможный критерий блокировки WireGuard с минимизированной вероятностью ложных срабатываний? По всей видимости DPI стоит пропустить пакет "похожий" на Handshake Initiate, запомнить состояние сессии (IP адреса и порты) и поставить ее на блокировку только после ответного пакета "похожего" на Handshake Response. Нельзя утверждать наверняка, но по косвенным признакам весьма вероятно, что с чем-то подобным мы и имеем дело.
С чего же началась эта история? Скорее всего я не стал бы специально заниматься этой темой, если бы меня неожиданно не заинтересовал пост на форуме, в котором пользователь описывал любопытный метод обхода блокировки WireGuard с помощью другого VPN. Кому интересно, можете почитать нашу переписку, но если в двух словах, то он использовал Windscribe VPN для того, чтобы активировать WireGuard туннель, затем выключал Windscribe VPN и продолжал пользоваться WireGuard (официальный клиент с конфигурацией от Cloudflare Warp+) как ни в чем ни бывало.
В ходе нашего общения постепенно прояснились и прочие подробности, включая государство, которое таким оригинальным способом "подталкивает" население к более глубокому изучению сетевых технологий. Последний раз я был в Египте в 2010 и не могу сказать, что сильно по нему скучаю. Однако в настоящее время это одна из немногих легкодоступных стран, и я в последнее время всерьёз задумывался не слетать ли туда на дайвинг. Надо ли говорить, что невозможность доступа к домашним сетям оказалась бы крайне неприятным сюрпризом.
Далее предстояло решить, что использовать для маскировки Handshake Initiate и Handshake Response вместо Windscribe VPN. Можно конечно построить самописный сервис для инкапсуляции и форвардинга этих пакетов, тем более что нагрузка на него была бы минимальной (два пакета раз в две минуты на туннель). Однако уже довольно давно существует и более подходящий кандидат на эту роль и имя ему SOCKS5. Пятая версия этого популярного протокола поддерживает UDP, что нам собственно от неё и нужно.
Ранее я уже писал о своём пет-проекте VPN клиента на основе Cloudflare реализации протокола WireGuard. В него то я и решил добавить поддержку SOCKS5. В процессе тестирования промежуточных сборок выяснились кое-какие особенности реализации DPI. Например, если первый Handshake Initiate пролетел мимо SOCKS и попал в DPI, то неважно что потом вы отправляете в рамках данной UDP сессии, DPI будет ее настойчиво блокировать. Что любопытно, ничтожная часть пакетов каким-то образом все таки прорывается (возможно их маршрут проходит мимо DPI). Так же похоже, что верно и обратное, если UDP сессия не началась с Handshake Initiate - Handshake Response, то претензий к данной сессии у DPI не будет, даже если эти пакеты покажутся там в дальнейшем. Это могло бы объяснить почему у пользователя WireGuard туннель не ломался через две минуты при следующем Handshake Initiate. Однако это только лишь неподтвержденная гипотеза.
Для поддержки SOCKS5 в Wiresock VPN Client были добавлены следующие дополнительные параметры:
Socks5Proxy – указывает адрес и порт сервера SOCKS5, например
Socks5Proxy = socks5.sshvpn.me:1080
илиSocks5Proxy = 13.134.12.31:1080
Socks5ProxyUsername – имя пользователя SOCKS5 (опционально)
Socks5ProxyPassword – пароль пользователя SOCKS5 (опционально)
Так же для тестирования нам понадобился SOCKS5 сервер с поддержкой UDP ASSOCIATE. Полагаю большинство читателей вполне способно сконфигурировать такой сервер самостоятельно, поэтому думаю не стоит раздувать размер статьи приводя здесь детальную инструкцию. Остальным же могу порекомендовать памятку, которую я составил для себя по установке Dante от Inferno Nettverk A/S на Ubuntu 20.04 в Oracle Cloud.
Мы протестировали как вариант с применением SOCKS только к Handshake Initiate/Response пакетам, так и полностью ко всем пакетам WireGuard туннеля. Оба варианта прекрасно справились с блокировкой, поэтому остановились на первом, поскольку он более выигрышный с точки зрения производительности и ресурсов. Из неожиданных побочных эффектов, отправка Hanshake Initiate через SOCKS5 прокси "починила" возможность построения вложенного WireGuard туннеля с использованием официального клиента WireGuard for Windows (в качестве внешнего туннеля) и Wiresock VPN Client (в качестве внутреннего), которая "поломалась" где-то между релизами 0.5 и 0.5.3. Официальный клиент внезапно перестал пропускать Handshake Initiate в туннель (возможно это связано с работой по поддержке собственных вложенных туннелей), но после оборачивания в SOCKS он перестал его узнавать и все снова заработало.
Так же хотел отметить, что не представляет большой сложности сделать отдельное Windows приложение для официального клиента, которое будет перехватывать исходящие Handshake Initiate и отправлять их через SOCKS5. Если подобное будет востребовано, то я постараюсь найти время для реализации.
На этом все, надеюсь эта информация будет кому-то полезной!
Комментарии (28)
Uint32
07.02.2022 11:06+1Так же похоже, что верно и обратное, если UDP сессия не началась с Handshake Initiate — Handshake Response, то претензий к данной сессии у DPI не будет, даже если эти пакеты покажутся там в дальнейшем.
Может тогда будет достаточнл отправлять перед началом сессии WG произвольные UDP пакеты со случайными данными?
SerpentFly Автор
07.02.2022 11:19Возможно, здесь мы вступаем области предположений. Меня главным образом смущает, что у пользователя туннель не падал через две минуты. Так же вполне вероятно, что нужен еще и произвольный пакет в обратном направлении. А для этого придется еще и сервер модифицировать.
Возможно SOCKS - это действительно overkill и может отработать и более простой вариант. Но главное, что работает.
GritsanY
07.02.2022 13:51+1Что-то похожее было в KZ в начале января, в самом начале блокировок интернета -
у меня перестали подключаться клиенты к Wireguard VPN серверу в облаке Oracle, хотя доступ по SSH к нему оставался. Успел выяснить, что как раз Handshake Initiate до сервера доходил, и Handshake Response отправлялся, но до клиента не добирался - терялся на территории KZ провайдеров.
Решил проблему, подняв на том же сервере рядом с Wireguard сервис Outline, который как раз прячется в shadowsocks. Но на заметку вашу статью возьму, спасибо.
Sazonov
07.02.2022 14:43+1WireGuard vpn (свой сервер, клиент iOS / Mac) отлично работал в начале года в Египте, в отличие от многих других сервисов. Может они ещё и адрес смотрят?
SerpentFly Автор
07.02.2022 15:02Возможно, у пользователя был конфиг от Warp+. С другой стороны здесь вообще корпоративный VPN заблокировали, в отличие от того же Warp вряд ли адреса были засвечены. Может быть еще не у всех провайдеров блокировка налажена, насколько я понимаю история довольно свежая.
Sazonov
07.02.2022 16:18Если что, провайдер Orange. Пользователь - я. Сервер от DigitalOcean в Германии.
Earthsea
07.02.2022 16:08+3государство, которое таким оригинальным способом "подталкивает" население к более глубокому изучению сетевых технологий
Тем временем, граждане Египта более глубоко изучают сетевые технологии:
GektorTM
07.02.2022 20:29А OpenVPN там так же блокируют?
SerpentFly Автор
07.02.2022 20:31У меня не было возможности проверить, но судя по тому, что пишут, OpenVPN работает только в паре с shadowsocks.
GektorTM
07.02.2022 21:02Даже через TCP с TLS crypt и на 443 порту? Не знал, что DPI умеет распознавать такое...
UserAd
08.02.2022 14:11+1Да, сейчас нахожусь в египте. Работает только через obfsproxy, ssh tunnel. Shadowsock еще не пробовал. 443 tls ломается на фазе автоиизации
GektorTM
08.02.2022 15:58Еще в прошлом году у меня получалось подключаться, видимо что-то изменили совсем недавно.
UserAd
08.02.2022 17:38+1Неделю назад у нашего сотрудника тоже получалось подключиться просто по 443 TLS, но видимо когда я приехал что-то случилось. Очень похоже что ломается пакет ответа от VPN на стадии авторизации.
Я пробовал как отельный wifi (Link Egypt), так и vodafone 4G.
К сожалению сейчас нет времени особо пробовать другие методы обфусцирования траффика, так что не получится сделать нормальное исследование.
m0tral
07.02.2022 22:59А откуда вообще проблема, был осенью там и не испытал никаких проблем с доступом ко всему включая домашний сервер.
SerpentFly Автор
07.02.2022 23:13Ссылки на пруфы разбросаны по статье. То с чего я начал этим заниматься: https://www.ntkernel.com/forums/topic/can-i-select-the-default-interface-when-using-wiresock-vpn-client-on-win10/
Еще пара ссылок, которые я нашел пока, разбирался:
https://www.reddit.com/r/Egypt/comments/lsnyk7/is_wireguard_blocked/
https://twitter.com/6h4n3m/status/1459462360003919875Не исключено, что наличие блокировок зависит от конкретных провайдеров.
splitfire
08.02.2022 09:42+1Постепенно прихожу к мысли, что реализации VPN без качественной обфускации(а в перспективе и без маскировки под «легальный» сервис), нинужны. Всё больше и больше государств решают, что вахтёр на Интернет-границе — это отличная идея, которая избавит от всех проблем.
SerpentFly Автор
08.02.2022 10:25При этом маскировка под TLS возможно не лучший выбор. Как вариант можно было бы маскироваться под трафик популярных онлайн игр, в особенности построенных на основе UDP.
dakuan
08.02.2022 18:22+1У VPN все же немного иное предназначение - создание виртуальной сети поверх другой сети (даже шифрование не является обязательным), а использование VPN для обхода блокировок и сокрытия реального местоположения пользователя - это лишь частный случай. Поэтому подавляющее большинство реализаций VPN задачу сокрытия факта использования даже не пытается решать.
Zolg
А зачем тогда хитрые телодвижения с соксом ? (я при беглом прочтении так и не понял, как именно вы его используете).
Шлите первым пакетом со стороны клиента какой-нибудь мусор, сервер этот пакет проигнорирует, но на брандмауэре сессия создастся и она будет начинаться с этого мусора, а не с wireguard handshake
SerpentFly Автор
Если послать мусорный пакет, то сервер ничего не ответит, а DPI похоже ждет первую подходящую пару в обоих направлениях. Впрочем, наверняка сложно сказать, это только мои предположения.
Через SOCKS пробрасывается Handshake Initiate и приходит ответный Hadnshake Response, остальные пакеты ходят напрямую между клиентом и сервером WireGuard. Другими словами SOCKS используется для маскировки handshake.
13werwolf13
я что-то немного упустил.. почему хэндшейк не шифруется так же как остальные пакеты? вроде же это собирались добавить в мейнстримный wg??
SerpentFly Автор
Вопрос скорее к Джейсону, но вообще все кроме первого байта для DPI выглядит как произвольные бинарные данные. Основная проблема в фиксированном размере пакетов handshake и первом байте указывающем тип пакета, который 1 для initiate, 2 для response и 4 для пакетов с данными.
13werwolf13
Мда, ну тут я тоже не вижу проблемы подмешивать в пакет рандома для изменения веса..
SerpentFly Автор
Согласен, можно было бы сделать Wireguard более стойким к детектированию. Но по всей видимости это вряд ли уже произойдёт. Никто не захочет ломать совместимость. А ее к слову ломает даже использование зарезервированных байтов.
Revertis
Насколько я знаю UDP, там всё равно вычитывается вся датаграмма, которая влезет в буфер. Неужели сервер создаёт буфер для чтения всего в 148 байт?
SerpentFly Автор
wireguard-go код проверяет размер полученного пакета и отбрасывает его если он не совпадает с ожидаемым. Другие реализации не проверял, но думаю там все аналогично. Поэтому дописывать в Handshake Initiate рандомный мусор в конец пакета не получится.
Revertis
Тогда придётся форкать. Проблема только в том, что теперь он аж в ядре линуха :(
SerpentFly Автор
Можно использовать альтернативный BoringTun от Cloudflare. Не kernel mode, но производительность неплохая, существенно лучше чем wireguard-go.