Zapret: what's new

Реализовано задание раздельных опций nfqws для http и https и для версий ip протоколов 4,6 :

NFQWS_OPT_DESYNC_HTTP="--dpi-desync=split --dpi-desync-ttl=0 --dpi-desync-fooling=badsum"
NFQWS_OPT_DESYNC_HTTPS="--wssize=1:6 --dpi-desync=split --dpi-desync-ttl=0 --dpi-desync-fooling=badsum"
NFQWS_OPT_DESYNC_HTTP6="--dpi-desync=split --dpi-desync-ttl=5 --dpi-desync-fooling=none"
NFQWS_OPT_DESYNC_HTTPS6="--wssize=1:6 --dpi-desync=split --dpi-desync-ttl=5 --dpi-desync-fooling=none"

Если какая-то из переменных NFQWS_OPT_DESYNC_HTTP/NFQWS_OPT_DESYNC_HTTPS не определена, берется значение NFQWS_OPT_DESYNC.
Если какая-то из переменных NFQWS_OPT_DESYNC_HTTP6/NFQWS_OPT_DESYNC_HTTPS6 не определена, берется значение NFQWS_OPT_DESYNC_HTTP/NFQWS_OPT_DESYNC_HTTPS.
Значит, если определено только NFQWS_OPT_DESYNC, то все 4 специализированные переменные берут это значение.
Зачем это надо : вряд ли ttl будет одинаковым для ipv4 и ipv6. Если для ipv4 он окажется 9, то таким ttl вы вполне можете начать рубить сайты на ipv6

Скрипты всеми силами стараются минимизировать количество процессов nfqws, чтобы сохранить память. Для роутеров это важно, а если используется hostlist, то 4 процесса nfqws легко забьют и 256 Mb. Если для разных вариантов используются одни параметры, то запускается только один процесс с ними.

В nfqws добавлен параметр --dpi-desync-ttl6, задающий отдельное значение для ipv6 hop limit.
Если --dpi-desync-ttl6 не указан, то берется значение --dpi-desync-ttl. Чтобы отключить hop limit на ipv6 используйте --dpi-desync-ttl6=0.
Параметр позволяет не запускать лишний процесс nfqws, когда стратегия обхода для ipv4 и ipv6 отличается лишь ttl.

В blockcheck.sh добавлена поддержка FreeBSD через фаервол ipfw
Необходима загрузка модулей ipfw.ko и ipdivert.ko

В nfqws добавлена возможность настройки дурения badseq.
Можно указать инкремент seq и ack_seq в виде знакового десятичного числа или знакового hex числа (1000,-1000,0x8000,-0x8000)

--dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default -10000
--dpi-desync-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment. default -65000

Изменены значения по умолчанию. Раньше инкремент для обоих параметров был 0x80000000.
Как оказалось, на некоторых провайдерах дурение badseq работает только в пределах ограниченного окна от текущего seq.

В nfqws добавлен параметр --dpi-desync-fake-unknown=<filename>
в нем указывается файл, который будет отсылаться в качестве фейка при --dpi-desync=fake --dpi-desync-any-protocol
и обработке не http запроса и не TLS ClientHello вместо стандартного пакета из 256 нулей

cutoff теперь имеет 3 варианта ограничителя
n - номер пакета, начиная с 1
d - номер пакета с данными, начиная с 1
s - relative sequence number. он равен количеству переданных от клиента байтов + 1
Отсечение происходит, если параметр равен или больше заданного значения.

--wssize-cutoff=[n|d|s]N               ; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--dpi-desync-cutoff=[n|d|s]N           ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N

Вкратце поясню зачем вся эта котовасья.
Представьте , что настал очередной печальный день в истории рунета, и РКН решил заблокировать SMTP (тут можете подставить ваш любимый протокол, телеграм, допустим). Шлите почту только через авторизованные сервисы, где сидит тов. майор. SMTP протокол распознается и режется на ТСПУ.

Задача точно такая же, как и в случае http/https. Сделать так, чтобы DPI не понял, что это SMTP.
Но так случилось, что абы чего DPI не пропускает. Левые протоколы тоже стали резать.
Надо подсунуть что-то безобидное. Например, http запрос.
И делать это до того момента, как начнется шифрованная фаза (ведь вы конечно же не будете слать почту без TLS ?)
Обычно при отсылке почты через SMTP клиент выдает 2 открытых сообщения “EHLO servername” и “STARTTLS”, потом идет TLS handshake.

nfqws --qnum=250 --dpi-desync-any-protocol --dpi-desync-cutoff=d4 --dpi-desync=fake,split2 --dpi-desync-ttl=5 --dpi-desync-fake-unknown=/opt/zapret/files/fake/fake_http_req_example.bin

Будут обработаны первые 3 исходящих пакета TCP соединения с данными (то есть не будут в нумерации учтены пустые пакеты с ACK)
Будут отосланы следующие сообщения : Будут отосланы следующие сообщения :

фейк : GET / HTTP/1.1 ... Host: iana.org
реал : EH
реал : LO mail.myserver.com
фейк : GET / HTTP/1.1 ... Host: iana.org
реал : ST
реал : ARTTLS
фейк : TLSClientHello от w3.org
реал : TLSClientHello от mail.myserver.com - первые 2 байта
реал : TLSClientHello от mail.myserver.com - остальные байты

после этого наступает состояние desync-cutoff (отсечение desync).
nfqws больше не трогает это соединение, чтобы не замусоривать канал всякой ерундой и не снижать скорость

если добавите --debug, все станет понятно из лога nfqws

Разумеется, описанный сценарий пока чисто гипотетический, он служит лишь для иллюстрации техники. Будет ли это работать и с какими нюансами - покажет время.
Если тупо забанят 25 порт или по IP startmail, protonmail, … - не сработает точно

Реализован задел для десинхронизации udp протоколов.
Нацелено прежде всего всего на quic и dht, хотя далеко не только.
Поддерживается только режим десинхронизации fake, поскольку фрагментация udp невозможна на транспортном уровне. Возможна только ip фрагментация - она пока не реализована.
Fake нацелен на атаку на stateful DPI и бесполезен для stateless.

quic - новый протокол для доступа к сайтам по http3. Полностью шифрован. DPI вынужден будет расшифровывать непростым алгоритмом каждый пакет, чтобы хотя бы выдернуть SNI. Слишком дорого для stateless анализа и слишком опасно быть за-ddos-еным. Потому если ему скормить фейк, это может решить вопрос. Пока еще тестировать не на чем, но все впереди. Возможно, будут просто резать quic.

DHT бутстрап выполняется по UDP. Уже однажды пытались его заблокировать. Исследователи сообщали, что если перед запросом скормить левый udp пакет с теми же source/dest port, то DPI отстает от UDP канала до истечения таймаута неактивности.

Задействование атаки на первый исходящий пакет данных :

iptables -t mangle -I POSTROUTING -p udp --dport 443 -j NFQUEUE --queue-num 200 --queue-bypass
nfqws --qnum=200 --dpi-desync-any-protocol --dpi-desync=fake --dpi-desync-ttl=5 --dpi-desync-cutoff=d2

Фулинг работает только по ttl и badsum. Другое к udp неприменимо.
Можно заменить fake payload так : --dpi-desync-fake-unknown-udp=/opt/zapret/files/fake/fake_quic_initial_example.bin

Реализована фрагментация ipv4 и ipv6 на уровне ip : --dpi-desync=ipfrag2
К сожалению, это не прорыв, а скорее научный интерес. Современная сеть крайне враждебна к IP фрагментации.
Фрагментированные пакеты часто застревают или пересобираются/перефрагментируются по пути.
Часто их отбрасывают принимающие узлы, возможно как мера против ddos атак.
Linux всегда пересобирает проходящие (forwarded) фрагментированные ipv6, если это возможно.
Но как последнее средство для какой-то конкретной цели при соблюдении определенных условий теоретически может сработать
Можно проверить с некоторой долей вероятности сработает ли обход на конкретном ресурсе через команду ping с размером пакета >1472.
Если обычный пинг ходит, а длинный не ходит, то фрагментация режется.

IP фрагментация доступна для tcp и udp протоколов.
Возможно, с cutoff хороший вариант для доступа к своему wireguard серверу в случае блокировки wireguard протокола, если провайдер и хостер не режут фрагменты

Обеспечена совместимость с последними версиями pfsense.
Добавлены бинарики , собранные на FreeBSD 11 x64. Они должны работать на системах с ядром FreeBSD 11 и выше.
Хотя они и запускаются как минимум на FreeBSD 10, бинарик dvtws нельзя использовать на этих системах !
В коде присутствует условная компиляция , исправляющая баг/фичу FreeBSD 10.x и более старых.
На более старых системах нужно собирать самостоятельно, при этом скорее всего придется обновлять и компилятор CLang не из репозитория. Эти системы уже out-of-support, могут быть даже проблемы с удаленными с серверов репозиториями. Лучше проапгрейдиться

Для запуска в pfsense требуется выполнить ряд действий, описанных в docs/bsd.txt

Только для ipv6 режим десинхронизации hopbyhop, fooling hopbyhop,hopbyhop2.

Fooling : добавляется ipv6 extenstion header “hop-by-hop options” во все фейки.
В варианте hopbyhop2 добавляются 2 хедера, что является нарушением стандарта и гарантированно отбрасывается стеком протоколов во всех ОС. Один хедер hop-by-hop принимается всеми ОС, однако на некоторых каналах/провайдерах такие пакеты могут фильтроваться и не доходить. Расчет идет на то, что DPI проанализирует пакет с hop-by-hop, но он либо не дойдет до адресата всилу фильтров провайдера, либо будет отброшен сервером, потому что хедера два.

Desync: (не путать с fooling !) заключается в добавлении хедера “hop-by-hop options” во все оригинальные пакеты, попадающие под десинхронизацию. Здесь надо обязательно понимать, что добавление хедера увеличивает размер пакета, потому не может быть применено к пакетам максимального размера. Это имеет место при передаче больших сообщений. В случае невозможности отослать модицифированный пакет дурение будет отменено, пакет будет выслан в оригинале.
Расчет идет на то, что DPI увидит 0 в поле next header основного заголовка ipv6 и не будет скакать по
extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет без анализа. Возможно, какие-то DPI на это купятся.
Может сочетаться с любыми режимами 2-й фазы.
Например, “hopbyhop,split2” означает разбить tcp пакет на 2 сегмента, в каждый из них добавить hop-by-hop.
При “hopbyhop,ipfrag2” последовательность хедеров будет : ipv6,hop-by-hop,fragment,tcp/udp.
При поступлении пакета ipv4 режим hopbyhop игнорируется, но работает десинхронизация 2-й фазы, если таковая имеется.
Может применяться как к tcp, так и к udp.

Добавлены режимы десинхронизации ipv6 destopt и ipfrag1.
Заключаются в добавлении хедеров “destination options” и “fragment” (фрагмент единственный).
В thc-ipv6 toolkit есть прога firewall6. Она пытается применить различные экзотические комбинации extension headers для проверки что пропускается, а что нет.
hopbyhop часто режется. на 2 провайдерах проверил. destopt и единственный фрагмент обычно не режутся.
остается понять как будут DPI воспринимать эти хедеры. будут ли они реагировать.
пока не на чем проверить

По поводу режима ipfrag1 внимательно читать комментарии по IP фрагментации в readme.txt
Обычно для ухода фрагментов ipv6 с локальной системы (OUTPUT) достаточно занести эти пакеты в notrack, но если фрагмент только один, fragment header может быть молча убран модулем nf_defrag_ipv6.
Как повезет. На некоторых системах это случается, на некоторых нет. Гарантии может дать только загрузка модуля ip6table_raw с параметром raw_before_defrag=1.
В openwrt нужно изменить файл вот так :

cat /etc/modules.d/ipt-raw6
ip6table_raw raw_before_defrag=1

и ребутнуть систему.

Проведен мощный рефакторинг скриптов запуска с целью поддержки nftables.
Основная мотивация - переход будущей версии OpenWRT на firewall4+nftables.
iptables уходят из мейнстрима openwrt, хотя могут быть доустановлены и использованы параллельно с nftables.
Но есть и некоторая нерешаемая боль, связанная с nftables, о которой можно почитать здесь :

https://github.com/bol-van/zapret/blob/master/docs/nftables_notes.txt

nftables можно использовать на linux с достаточно новыми версиями ядра и утилиты nft.
На OpenWRT nftables можно использовать, начиная с 21.xx. На более старых скорее всего будут проблемы.
На OpenWRT 21.xx в nftables недоступен hardware flow offloading и не будет работать software flow offloading,если есть названия интерфейсов , начинающихся с цифры. Обычно это происходит, когда включен ipv6 и имеется 6to4,6in4. (fixed в snapshot версиях после 21.x)

На более новых системах, где уже основной фаервол firewall4+nftables, можно переключить zapret на iptables.
Плюс - возможность использования больших ip lists на роутерах, минус - отсутствие поддержки flow offloading в любом варианте.

Что нужно понимать про скрипты запуска в OpenWRT : на системах с firewall3 при использовании iptables производится интеграция в fw3, как это было и раньше.
/etc/init.d/zapret {start,stop} не трогают фаервол
Вместо этого правила применяются через firewall include в /etc/config/firewall : /etc/firewall.zapret
При использовании nftables, либо iptables на системах с firewall4, управление фаерволом происходит в init скрипте, как это делается на традиционных linux.
В custom скриптах появилась отдельная функция для применения фаервола nft
В openwrt custom теперь в iptables функции требуется и стартовать, и останавливать правила, как это сделано в традиционном linux.
Функция стала полностью совместима с версией из sysv.
Еще одно небольшое новшество - написан openrc адаптер для запуска sysv init.
Оказалось, что некоторые системы с openrc (alpine linux), не автозапускают скрипты из /etc/init.d, если они не #!/sbin/openrc-run

Новости про nftables.

Выпустили, наконец, релиз nft 1.0.2.
Пофиксена проблема с кавычками в flowtable

Боль с количеством RAM и nfset-ами по прежнему там. Создал баг в багзилле netfilter.

В связи сами знаете с чем банят известные ресурсы типа МОРДО КНИГИ
Они используют QUIC. QUIC базируется на UDP и полностью зашифрован. Для DPI довольно сложно расшифровать QUIC , чтобы распознать домен. Потому начали на ТСПУ блочить QUIC. Видимо на все IP адреса, кроме whitelist (на ВКОНТАКТ QUIC не блокируется)
Точно блокируется на youtube, google, facebook.

Я сделал поддержку UDP в nfqws упреждающе (см выше мои посты). Она сработала как надо.
Вот это решает проблему :

iptables -t mangle -I POSTROUTING -p udp --dport 443 -m mark ! --mark 0x40000000/0x40000000 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:3 -j NFQUEUE --queue-num 205 --queue-bypass
nfqws --qnum=205 ---dpi-desync=fake --dpi-desync-any-protocol --dpi-desync-cutoff=d4

Небольшое изменение в custom скриптах. Теперь необходимо указывать протокол “-p tcp” в фильтре zapret_custom_firewall
Написан custom скрипт custom-nfqws-quic4all, который наследует режим MODE=nfqws и включает обход блокировки QUIC на все адреса, вне зависимости от фильтра ipset/hostlist.

Как оказалось, движок QUIC от chromium ловит не только UDP, но и ICMP ttl expired in transit, связанные со своими UDPшками. Они приходят от фейк-пакетов с низким TTL. Как только броузер славливает такой ICMP, сеанс считает порванным, дальше ничего не проходит. Mozilla этим НЕ страдает.
Лечение : либо резать ttl expired in transit (нехорошо, поломаете трейсилки), либо отказаться от ttl fooling и заменить на любой другой, который сработает. Например, badsum.
А можно вообще убрать fooling. В этом случае фейки будут доходить до сервера, но поскольку там трэш, они будут отбрасываться процессом веб сервера.
Но если вы вдруг используете badsum и что-то на пути будет дропать пакеты с badsum (роутеры mediatek, заводские прошивки с linux), то обход не сработает
Проще и надежнее убрать fooling

В последнее время начали банить крупные сайты по типу facebook. У них прыгающий IP. Если ресолвить facebook.com и добавлять в ip list, то очень быстро перестает работать.
Можно использовать некоторые листы с antifilter.download, где эта проблема решена (например, allyouneed). Если это не вариант, теперь можно самому закидывать дополнительные ip из скрипта IPSET_HOOK в конфиге. Этот скрипт вызывается при каждом заполнении ipset/nfset/table с параметром $1=имя таблицы. IP адреса или подсети должны быть выведены в stdout.
Проблема пересечения интервалов в nfset решается автоматически. Об этом думать не нужно.
Если задана переменная LISTS_RELOAD, IPSET_HOOK будет вызван 1 раз без параметров.

Пример как добавить в таблицу zapret все IPv4 адреса из автономной системы facebook/instagram :

#!/bin/sh

# AS32934 - facebook,instagram

AS="32934"

aslist()
{
    whois -h whois.radb.net -- "-i origin AS$1" | sed -nre 's/^route: *(.*)/\1/p'
}

[ "$1" = "zapret" ] && {
    for as in $AS; do
        aslist $as
    done
}

Номера автономных систем перечисляются в переменной AS через пробел

реализовано определение протокола QUIC
теперь не обязательно указывать --dpi-desync-any-protocol, чтобы работать по QUIC initial
расшифровка QUIC initial и извлечение имени хоста, чтобы работал --hostlist, не поддерживается

К сожалению, было замечено на одном из провайдеров с ТСПУ, что начало QUIC сеанса устанавливается успешно, но пакеты вскоре внезапно перестают ходить, так что качество броузинга становится значительно хуже с таким QUIC, чем без него
причины пока не ясны

Еще немного по поводу IP фрагментации.

При использовании iptables и NAT, похоже, что нет способа прицепить обработчик очереди после NAT.
MASQUERADE является финальным таргетом, после него NFQUEUE не срабатывает.
Проходящий пакет попадает в nfqws с source адресом внутренней сети, затем фрагментируется и уже не обрабатывается NAT.
Так и уходит во внешюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает.
Видимо единственный рабочий метод - отказаться от iptables и использовать nftables.
Хук должен быть с приоритетом 101 или выше.

В nftables скриптах от zapret реализован перехват пакетов в NFQUEUE после NAT. Там это сделать достаточно просто.
Но при этом если сгенерированные в nfqws пакеты не вводить в состояние notrack, то они заново процессятся NAT, происходит подмена src port. Потому добавлен хук predefrag, где эти пакеты помечаются как notrack. Дополнительно открывается путь , чтобы отсылать ipv6 фрагменты на ядрах 4.16+

Сделал расшифровку и анализ QUIC initial crypto frames в nfqws.
Теперь работает hostlist и видно куда лезем в режиме --debug

Алгоритм довольно сложный. Требует крипто библиотеки. Чтобы не тащить тяжелые зависимости, минимально необходимый код понадерган из разных исходников + написано своего
Нежатый бинарик вырос примерно на 20 кб

Реализованы режимы десинхронизации fakeknown и udplen
fakeknown - это то же самое, что fake, только применяется исключительно к распознанным протоколам, даже если выставлено --dpi-desync-any-protocol. При этом вторая фаза десинхронизации применяется как раньше.
udplen - режим, относящийся только к udp. Пакет увеличивается в размере, дополняясь нулями, на указанное в --dpi-desync-udplen-increment количество байт. По умолчанию - 2. Нацелено на обман DPI, ориентирующихся на размеры пакетов. Может сработать при условии, что протокол приложения не завязан жестко на размер udp payload и не обижается на изменение размера.
К сожалению, обмануть таким образом статистическое застревание QUIC на ТСПУ не удается

Сделал инструкцию для быстрого старта

nfqws теперь учитывает fwmark пакетов, попадающих к нему на обработку
если в результате обработки пакета nfqws генерит свой пакет, его fwmark будет не 0x40000000, а 0x40000000 | <original_fwmark>

добавлен алгоритм лечения проблемы неверного выбора исходящего интерфейса для сгенерированных пакетов
параметры --bind-fix4 и --bind-fix6
лечение идет через вызов bind() на raw socket и setsockopt SO_BINDTODEVICE
интерфейс назначения берется из NFQUEUE. куда оригинальный пакет уходил, туда и отправляем сгенеренные. это возможно как для OUTPUT, так и POSTROUTING
linux не был бы linux-ом, если бы это не работало по разному для ipv4 и ipv6

в случае отсутствия опции bind-fix для соответствующего семейства ip отправка идет так, как было раньше. то есть система решает сама
Проблема неверного выбора интерфейса может случиться, когда у вас несколько аплинков или присутствует policy routing на базе source ip.
Причина - особенности маршрутизации пакетов , отправленных через raw sockets.
Для сценария стандартного роутера LAN-WAN это не актуально