Когда нужно защитить передаваемые данные, мы прибегаем к помощи VPN. Однако порой возникает желание скрыть даже сам факт его использования. Так сильно мы заботимся о своей безопасности!?

Поскольку WireGuard сейчас на пике популярности, а он работает на основе протокола UDP, возникла идея замаскировать его трафик под другой популярный протокол на базе UDP — QUIC. QUIC объединяет в себе функции TCP и TLS, поэтому было логично попытаться сделать так, чтобы трафик WireGuard выглядел как трафик QUIC. Для реализации этой идеи потребовалось детально изучить исходный код WireGuard в ядре Linux. Патч для ядра Linux доступен на QUICWireGuard.

Выбор порта

Трафик QUIC использует тот же порт, что и HTTPS — 443. Поэтому WireGuard также будет подниматься на этом порту. Однако, поскольку данное решение предназначено исключительно для работы между двумя устройствами на Linux, мы оставляем возможность подключения других устройств на альтернативных портах. Мимикрия будет активироваться только при использовании порта 443 для WireGuard либо при входящем соединении на этот порт. Во всех остальных случаях WireGuard останется без изменений.

Установка на Arch, Ubuntu, OpenWRT

Для сборки и установки были сделаны два скрипта:

linux_auto_install.sh - подходит для сборки модуля wireguard.ko для данной системы и установки его.

openwrt_auto_install.sh - подходит для сборки модуля wireguard.ko для OpenWRT и установки, как аргумент скрипта надо указать или ssh имя роутера или xxxx@x.x.x.x

Оба скрипта не удаляют из системы предыдущий модуль wireguard.ko. Удаление старого модуля, загрузка обновленного и последующая перезагрузка устройства WireGuard должны выполняться вручную. Чтобы убедиться, что загружена именно новая версия, проверьте вывод команды dmesg | grep -i wireguard.

[    8.975692] wireguard: QUICWireGuard 1.0.0 loaded. See https://github.com/karen07/QUICWireGuard for information.
[    8.975694] wireguard: Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
[    8.975695] wireguard: Copyright (C) 2024 Karen.

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

Для систем, отличных от Arch, Ubuntu и OpenWRT, скрипт также применим, однако вам потребуется добавить в него установку пакетов, необходимых для сборки модуля ядра под ваш конкретный дистрибутив.

Формирование пакета WireGuard

Если открыть код WireGuard в ядре Linux. Там есть файл messages.h, в котором описаны две структуры:

struct message_handshake_initiation {
	struct message_header header;
	__le32 sender_index;
	u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN];
	u8 encrypted_static[noise_encrypted_len(NOISE_PUBLIC_KEY_LEN)];
	u8 encrypted_timestamp[noise_encrypted_len(NOISE_TIMESTAMP_LEN)];
	struct message_macs macs;
};
struct message_handshake_response {
	struct message_header header;
	__le32 sender_index;
	__le32 receiver_index;
	u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN];
	u8 encrypted_nothing[noise_encrypted_len(0)];
	struct message_macs macs;
};

Переменных этих структур, отправляются при WireGuard handshake в файле send.c в функциях wg_packet_send_handshake_initiation и wg_packet_send_handshake_response. Поэтому если отправлять не только packet, а еще и QUIC header, то WireGuard handshake будет похож на QUIC handshake.

struct message_handshake_initiation packet;
wg_socket_send_buffer_to_peer(peer, &packet, sizeof(packet),
					      HANDSHAKE_DSCP);
struct message_handshake_response packet;
wg_socket_send_buffer_to_peer(peer, &packet,
						      sizeof(packet),
						      HANDSHAKE_DSCP);

Описание QUIC header

Если смотреть в WireShark, то QUIC init header стоит из семи полей.

Поля init header
Поля init header

Поле

Описание, значение

Flags

Битовое поле, заполняю значением 0xC0

Version

Версия, сейчас всегда 0x1 в BE порядке

DCID len

Длина DCID в байтах, для init header значение 0x8

DCID

Номер получения

SCID len

Длина SCID в байтах, для init header значение 0x0

Token len

Длина SCID в байтах, для init header значение 0x0

Data len

Состоит из двух частей, первые два бита, первого байта это показатель общей длины Data len, в моем случае 0b01(0x4), а к 0x4 прибавляем длину init header

Если смотреть в WireShark, то QUIC response header стоит из семи полей.

Поля response header
Поля response header

Поле

Описание, значение

Flags

Битовое поле, заполняю значением 0xC0

Version

Версия, сейчас всегда 0x1 в BE порядке

DCID len

Длина DCID в байтах, для response header значение 0x0

SCID len

Длина SCID в байтах, для response header значение 0x8

SCID

Номер отправителя

Token len

Длина SCID в байтах, для response header значение 0x0

Data len

Состоит из двух частей, первые два бита, первого байта это показатель общей длины Data len, в моем случае 0b01(0x4), а к 0x4 прибавляем длину response header

Соответственно, если спереди к struct message_handshake_initiation packet добавить QUIC init header, то начало сессии WireGuard будет похоже на начало сессии QUIC. Тоже самое, если спереди к struct message_handshake_response packet добавить QUIC response header, то начало сессии WireGuard будет похоже на начало сессии QUIC.

Прием пакета WireGuard

Открыв код WireGuard в ядре Linux, вы найдете файл receive.c, где обрабатывается прием входящих пакетов. В функции prepare_skb_header производится удаление заголовка UDP из буфера skb. Нам же понадобится в некоторых случаях удалять также заголовок QUIC. Добавим проверку на номер порта, а также учтем, что все пакеты WireGuard начинаются с числа, не превышающего четыре, тогда как заголовок QUIC начинается с 0xC0. Таким образом, добавив условие проверки первого байта пакета, мы сможем различать пакеты типа initiation response и data.

Результат

Wireshark
Wireshark

Вот пример того, как Wireshark интерпретирует рукопожатие WireGuard. Wireshark определяет соединение как QUIC. Мы достигли желаемого результата.

Комментарии (26)


  1. stanislav37
    23.12.2024 15:59

    Вы же в курсе, что в РФ QUIC заблокирован... ?


    1. karen07 Автор
      23.12.2024 15:59

      Можете сами проверить, взять свежий curl 8.11.1 с поддержкой HTTP3 и запросить

      curl --http3-only https://example.org



      1. Semy
        23.12.2024 15:59

        Можно запустить в докере: docker run --rm ymuski/curl-http3 curl --http3-only https://example.org


        1. karen07 Автор
          23.12.2024 15:59

          В Arch Linux дефолтный curl уже умеет HTTP3)


    1. onegreyonewhite
      23.12.2024 15:59

      Я вот как-то этот момент упустил. А где он заблокирован? У меня сайт на HTTP3 вроде исправно работает.


      1. karen07 Автор
        23.12.2024 15:59

        Да, именно протокол никто не блочит)


      1. dartraiden
        23.12.2024 15:59

        https://ntc.party/t/ограничение-http3-quic/1823/

        Одно время его резали полностью, пока не научились расшифровывать.

        Текущая ситуация по QUIC примерно такая. Опробовано порядка 12 провайдеров в RU из разных городов. На большинстве из них QUIC блокируется путем анализа SNI из расшифрованного QUIC initial. Если SNI плохой, то блокируется stateful все udp “соединение”. Анализируется только udp port 443.


    1. slinkinone
      23.12.2024 15:59

      Не заблокирован. Я полагаю вы хотели сказать что блокируются потоки, которые используются ECL (Encrypted Client Hello) - расширение для TLS протокола, которое позволяет шифровать запрашиваемый сайт. Так как QUIC использует TLS, то если TLS идущий поверх QUIC использует ECL, то такой поток скорее всего будет заблокирован.


  1. Semy
    23.12.2024 15:59

    Лучше конечно бы опцией в конфиге.


    1. karen07 Автор
      23.12.2024 15:59

      Конфиг пришлось бы пробросить через весь настроечный пайплайн WireGuard в OpenWRT. Поэтому решение через порт оказалось самым простым для внедрения. Если кто то хочет поменять порт, то этот дефайн легко найти в файле messages.h "#define QUIC_PORT 443"


  1. puchuu
    23.12.2024 15:59

    Для мимикрирования под общение веб сайта и клиента вебсайта нужен сетевой стек браузера либо сам браузер. Не имеет никакого смысла писать обертку вг прямо в квик. Фаерволл легко составит отпечаток поведения этой обертки, дифференцирует этот отпечаток от отпечатка сетевого стека браузера и забанит его. Я почти уверен, что китайский фаерволл уже сделал это, причём сделал сразу как только патч опубликовали.


  1. nidalee
    23.12.2024 15:59

    QUIC в РФ действительно блокируют, просто не очень активно. В зависимости от провайдера, могут либо резать вообще long header, либо фильтровать по SNI из QUIC initial. У некоторых QUIC рубится конкретно в Chrome.

    Но суть такова, что QUIC в РФ уже при смерти. Разворачивать на нем что-то нецелесообразно, отключат окончательно в любой момент. Для РКН нет смысла держать его рабочим, он дороже анализируется.


    1. JBFW
      23.12.2024 15:59

      вот лишь бы сломать, лишь бы испортить...

      а ведь создавалась эта контора как раз наоборот, типа для борьбы с недобросовестными провайдерами, которые развлекались прозрачными прокси с блокировкой всего что не нравилось (экономия трафика, жалобы обеспокоенных родителей и т.д.)


    1. slinkinone
      23.12.2024 15:59

      QUIC не причем. Дело в TLS ECL расширении. QUIC сам по себе это всего лишь транспорт. Сейчас львиная доля трафика в сети что провайдеров, что мобильных операторов идет с использованием QUIC.


  1. puchuu
    23.12.2024 15:59

    Для мимикрирования под общение веб сайта и клиента вебсайта нужен сетевой стек браузера либо сам браузер. Не имеет никакого смысла писать обертку вг прямо в квик. Фаерволл легко составит отпечаток поведения этой обертки, дифференцирует этот отпечаток от отпечатка сетевого стека браузера и забанит его. Я почти уверен, что китайский фаерволл уже сделал это, причём сделал сразу как только патч опубликовали.


    1. karen07 Автор
      23.12.2024 15:59

      Да, детектировать мимикрию не сложно, но в любом случае сложнее чем чистый WireGuard, который детектируется по двум if.


  1. figachit
    23.12.2024 15:59

    Вы сделали офигенную штуку.

    AWG делается для OpenWRT автоматически через Github, может, и вы так же могли бы?


    1. karen07 Автор
      23.12.2024 15:59

      Не совсем понял о чем речь?)


      1. figachit
        23.12.2024 15:59

        Спасибо, что разбанили) уж искал, как с вами связаться

        Для AWG есть пайплайны в Github для сбора под любую архитектуру OpenWRT например


        1. karen07 Автор
          23.12.2024 15:59

          посмотрю, но я сделал скрипт, который при наличии линукса без проблем соберет под ваш роутер, просто надо указать его ssh имя)


          1. figachit
            23.12.2024 15:59

            1. karen07 Автор
              23.12.2024 15:59

              Пакет не стал делать, почему то модуль ядра затирался из дефолтного пакета) И собирать под все архитектуры тоже не стал, потому что скрипт сам ходит на роутер, узнает его архитектуру, и тд)


              1. figachit
                23.12.2024 15:59

                а вот по ссылке Github сам собирает

                у себя-то собрать практически нереально


                1. karen07 Автор
                  23.12.2024 15:59

                  WSL или Linux?) Зависимости скрипт сам поставит)


                  1. figachit
                    23.12.2024 15:59

                    too hard


  1. Kil1J0y
    23.12.2024 15:59

    Я использую haproxy за ним спрятан vless + tls, мне не удалось запустить reality в данной конфигурации, однако, у меня открыты 80 и 443 порт для всего, простой сайт обманка т.к. нужно правдоподобное отрицание висит nextcloud aio на бекенде default, однако магия вся в том что сначала принимаю tcp и если там правила не подошли то идёт на фронтент уже http а там так же sni и маршрутизация пути, wstunnel работает отлично, сейчас в процессе написания скрипта перезапустит сервер wstunnel при падении и так же пишу скрипт клиента для deb12 и openwrt чтоб перезапускал wstunnel, канал на nl 200, загрузил на 140-160 мбит, на уровне с wg где то, но трафик идёт легитимный, в планах также прикрутить stunnel со sni тоже попробовать как socks5, до adguardhome не дошел еще, но прикруьить doh это обязательно, все это тянет на целую статью. С базовыми системами все понятно, с openwrt интереснее, там есть tun2socks, и прикрутить туда маршрутизацию