Skip to content

bol-van/ipobfs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English
-------

see readme.eng.txt

Для чего это нужно
------------------

На нас надвигается эпоха, когда повсеместно могут начать блокировать уже не отдельные IP адреса или домены, а протоколы.
Одним из способов возможного преодоления сигнатурного анализа на DPI является модификация протокола.
Лучший способ - модифицировать сам софт, работающий с этими протоколами. Но не всегда это бывает просто или возможно.
Для TCP существует obfsproxy. Однако, в случае VPN - по TCP работают только не очень быстрые решения (openvpn).

Что же делать в случае udp ?
Если оба endpoint-а находятся на белом IP, то можно как следует поизвращаться прямо на уровне IP и дальше.
Например, если у вас VPS, а дома роутер на openwrt, вы имеете прямой IP от провайдера, то можно применить эту технику.
Если один endpoint находится за NAT, то можно как следует поизвращаться на уровне udp или tcp,
не трогая IP и начало заголовков tcp/udp.

Схема выглядит следующим образом :
 peer 1 <=> обфускатор/деобфускатор IP <=> сеть <=> обфускатор/деобфускатор IP <=> peer 2

Чтобы пакет был доставлен от peer 1 до peer 2, оба имеющих белые IP, достаточно лишь иметь корректные IP заголовки.
Можно выставить любой protocol number, проксорить или зашифровать IP payload, включая tcp/udp хедеры.
DPI от такого может выпасть в осадок. Оно не будет понимать с чем имеет дело.
Какие-то нестандартные IP протоколы с непонятно каким наполнением.
Конечно, DPI может обрезать весь мусор (по его мнению), но совсем не обязательно будет это делать.
Обычная программа не может сгененировать подобного типа "мусор", если только не использует raw sockets.
Значит разработчики DPI вряд ли поставят себе целью ловить рыбку в "мутной воде", если только какое-то решение
по обходу DPI на базе этой техники не станет достаточно популярным или белосписочность фильтров не достигнет высокого уровня.


ipobfs
------

Обработчик очереди NFQUEUE, обфускатор/деобфускатор пакетов.

 --daemon			; демонизировать прогу
 --pidfile=<file>		; сохранить PID в файл
 --user=<username>		; менять uid процесса
 --uid=uid[:gid]		; менять uid процесса
 --qnum=200			; номер очереди
 --debug                        ; вывод отладочной информации
 --ipproto-xor=0..255|0x00..0xFF; проксорить protocol number с указанным значением
 --data-xor=0xDEADBEAF          ; проксорить содержимое IP payload указанным 32-битным HEX значением
 --data-xor-offset=<position>   ; начинать проксоривание со смещения position после IP хедера
 --data-xor-len=<bytes>         ; ксорить не более указанного количества байтов, начиная с позиции data-xor-offset
 --csum=none|fix|valid 		; режим работы с чексуммой транспортных хедеров tcp и udp

Операция xor симметрична, поэтому для обфускатора и деобфускатора задаются одни и те же параметры.
На каждой стороне запускается по одному экземпляру программы.

Придется немного повозиться с iptables. Отфильтровать исходящий пакет просто, потому что он
идет "в открытую". На входящий пакет придется писать фильтры через u32.
Номер протокола ("-p") в фильтре - это результат xor исходного протокола с ipproto-xor.

server ipv4 udp:16 :
iptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 "0>>22&0x3C@0&0xFFFF=16" -j NFQUEUE --queue-num 300 --queue-bypass
iptables -t mangle -I POSTROUTING -o eth0 -p udp --sport 16  -j NFQUEUE --queue-num 300 --queue-bypass

client ipv4 udp:16 :
iptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 "0>>22&0x3C@0>>16&0xFFFF=16" -j NFQUEUE --queue-num 300 --queue-bypass
iptables -t mangle -I POSTROUTING -o eth0 -p udp --dport 16  -j NFQUEUE --queue-num 300 --queue-bypass

ipobfs --qnum=300 --ipproto-xor=128 --data-xor=0x458A2ECD --data-xor-offset=4 --data-xor-len=44

Почему data-xor-offset=4 : у tcp и udp в начале загловка идут номера порта источника и приемника, по 2 байта на каждый.
Чтобы проще было писать u32 не трогаем номера портов. Можно и тронуть, но тогда придется вычислить что же получится
после проксоривания и писать в u32 уже эти значения.
Почему data-xor-len=44 : пример приведен для wireguard. 44 байта достаточно, чтобы заксорить udp header и все заголовки wireguard.
Дальше идут шифрованные данные wireguard, их ксорить смысла нет.

Можно даже превратить udp в "tcp мусор" при ipproto-xor=23. Согласно заголовку ip это tcp, но на месте tcp хедера мусор.
Такого рода пакеты с одной стороны могут нарваться на middle-боксы, и на них сойдет с ума conntrack.
С другой стороны это может оказаться даже хорошо.

С ipv6 есть нюансы. В ipv6 нет понятия номера протокола. Зато есть понятие "next header".
Как и в ipv4 можно туда записать все что угодно. Но на практике это может вызвать лавину ICMPv6 собщений "Type 4 - Parameter Problem".
Чтобы этого избежать, надо приводить протокол к значению 59. Оно означает "no Next Header".
Проксорьте 59 с номером исходного протокола, получите параметр для "ipproto-xor".
Например, для udp номер протокола - 17. ipproto-xor=17^59=42
Для tcp номер протокола - 6. ipproto-xor=6^59=61

server ipv6 tcp:12345 :
ip6tables -t mangle -I PREROUTING -i eth0 -p 59 -m u32 --u32 "40&0xFFFF=12345" -j NFQUEUE --queue-num 300 --queue-bypass
ip6tables -t mangle -I POSTROUTING -o eth0 -p tcp --sport 12345 -j NFQUEUE --queue-num 300 --queue-bypass

client ipv6 tcp:12345 :
ip6tables -t mangle -I PREROUTING -i eth0 -p 59 -m u32 --u32 "38&0xFFFF=12345" -j NFQUEUE --queue-num 300 --queue-bypass
ip6tables -t mangle -I POSTROUTING -o eth0 -p tcp --dport 12345 -j NFQUEUE --queue-num 300 --queue-bypass

ipobfs --qnum=300 --ipproto-xor=61 --data-xor=0x458A2ECD --data-xor-offset=4


IP ФРАГМЕНТАЦИЯ
Если отсылающий хост посылает слишком длинный пакет, он фрагментируется на уровне IP.
Принимающий хост собирает пакеты только адресованные самому хосту.
В цепочке PREROUTING идут еще фрагментированные пакеты. При применении деобфускации
только к части пакета чексумма неизбежно оказывается инвалидной. csum=fix не помогает.
Для ipv4 помогает добавление правила в цепочку INPUT вместо PREROUTING.
Само собой, так ловятся только пакеты, адресованные самому хосту, зато они приходят
в NFQEUEUE уже собранными и корректно деобфусцируются.
Фрагментация IP - нежелательное явление, с ней следует бороться выставлением корректного mtu
внутри тоннеля. Есть некоторые протоколы, которые расчитывают на ip фрагментацию. К ним относится IKE (без rfc7383).

IPV6 ФРАГМЕНТАЦИЯ
В ipv6 тоже возможна фрагментация, однако она выполняется только отсылающим хостом, как правило только для
udp и icmp, когда фрейм не влезает в mtu. Ко всем фрагментам сразу после ipv6 хедера добавляется хедер "44".
К сожалению, все попытки поймать реконструированный полный фрейм в различных таблицах не приводят к успеху.
Ловится только первый фрагмент. Причину так и не удалось выяснить. Баг это или фича известно разве что Торвальдсу.
Так что лучший совет - не допускать фрагментации вовсе.

ЧЕКСУММЫ :
Работа с чексуммами начинается, когда через обфускатор проходит пакет tcp или udp.
Для входящих пакетов сначала выполняется операция ipproto-xor, и уже после нее анализируется получился ли пакет tcp или udp.
Для исходящих - наоборот.
--csum=none - не трогать чексуммы вообще. если после деобфускации чексумма окажется инвалидной, система отбросит пакет.
--csum=fix - режим игнорирования чексуммы. технически невозможно в NFQUEUE отключить проверку чексумм, потому чексумма
 приводится к валидному значению, чтобы система не отбросила пакет. работает только для входящих пакетов.
--csum=valid - приводить чексумму к валидному состоянию для всех пакетов - входящих и исходящих. Режим полезен при работе через NAT,
не пропускающие пакеты с инвалидной чексуммой.
Пересчет чексумм увеличивает нагрузку на cpu.
См. так же раздел "пробитие NAT".

НЕДОСТАТКИ :
Каждый пакет будет забрасываться в nfqueue, потому скорость значительно снизится. В 2-3 раза.
Если сравнивать wireguard+ipobfs с openvpn на soho роутере, то openvpn все равно окажется медленней.


ipobfs_mod
-----------

То же самое, что и ipobfs, только выполнен в виде модуля ядра linux. Дает просадку производительности всего в пределах 20%.

По логике функционирования дублирует ipobfs и совместим с ним.
Значит можно на 1 хосте включить ipobfs, на другом ipobfs_mod, и они вместе будут работать.
Однако, по умолчанию ipobfs_mod будет продуцировать tcp и udp пакеты с инвалидной чексуммой, система
с ipobfs их будет отбрасывать. Используйте csum=fix на стороне ipobfs_mod.

Команды iptables те же самые, только вместо направления на очередь NFQEUEUE выставляется бит в fmwark.
ipobfs_mod реагирует на выставленные биты и производит обработку пакетов.

Настройки передаются через параметры модуля ядра, задаваемые командой insmod.

server ipv4 udp:16 :
iptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 "0>>22&0x3C@0&0xFFFF=16" -j MARK --set-xmark 0x100/0x100
iptables -t mangle -I POSTROUTING -o eth0 -p udp --sport 16 -j MARK --set-xmark 0x100/0x100

client ipv4 udp:16 :
iptables -t mangle -I PREROUTING -i eth0 -p 145 -m u32 --u32 "0>>22&0x3C@0>>16&0xFFFF=16" -j MARK --set-xmark 0x100/0x100
iptables -t mangle -I POSTROUTING -o eth0 -p udp --dport 16 -j MARK --set-xmark 0x100/0x100

rmmod ipobfs
insmod /lib/modules/`uname -r`/extra/ipobfs.ko  mark=0x100 ipp_xor=128 data_xor=0x458A2ECD data_xor_offset=4 data_xor_len=44

Модуль поддерживает до 32 профилей. Настройки параметров для каждого профиля идут через запятую.
Например, следующая команда объединит функции 2 обработчиков NFQUEUE из предыдущих примеров :
insmod /lib/modules/`uname -r`/extra/ipobfs.ko  mark=0x100,0x200 ipp_xor=128,61 data_xor=0x458A2ECD,0x458A2ECD data_xor_offset=4,4 data_xor_len=44,0
Возможно применение разных профилей для исходящих и входящих пакетов. Так вы еще больше запутаете DPI, уменьшив корреляцию in/out потоков.
Если задан параметр markmask, то профиль ищется по маске mask/markmask, в противном случае по маске mark/mark.
markmask - один на все профили, через запятую перечилять для каждого профиля не нужно.
Используйте markmask, когда у вас много профилей, чтобы не расходовать по биту на каждый.
Например, следующий вариант может вместить 15 профилей в 4 бита : 0x10/0xf0, 0x20/0xf0, ..., 0xf0/0xf0
0x00/0xf0 означает, что пакет не обрабатывается модулем.

По умолчанию модуль устанавливает хук на входящие пакеты с приоритетом mangle+1, чтобы к моменту вызова была выполнена таблица mangle.
Если на вход поступают нестандартные протоколы, все в порядке. Но если идут пакеты с транспортным протоколом, в которых предусмотрена
чексумма, такие как tcp или udp, то модифицированные пакеты с инвалидной чексуммой до хука mangle+1 не доходят. Модуль их не получает.
Чтобы решить эту проблему, укажите параметр pre=raw и делайте : iptables -t raw -I PREROUTING ...
Исходящие пакеты можно обрабатывать в обычном порядке через mangle.

Если необходимо работать с фрагментированными ipv4 протоколами, замените в iptables PREROUTING на INPUT (см замечание в разделе ipobfs),
укажите параметр модуля "prehook=input".

Параметры pre,prehook,post,posthook задаются индивидуально для каждого профиля, поэтому если профилей несколько, их нужно перечислять
через запятую.

Модуль отключает проверку и подсчет чексумм на уровне ОС для обрабатываемых пакетов, в некоторых случаях пересчитывая
чексуммы tcp и udp самостоятельно.
Если параметр csum=none, модуль не считает суммы вообще. Сумма исходящего пакета может оказаться невалидной.
При csum=fix модуль считает сумму исходящего пакета до модификации пейлоада, тем самым повторяя функции ОС
или offload-а на сетевой карточке, чтобы они не сделали это на уже модифицированном пейлоаде и не испортили 2 байта данных,
а передаваемый пакет после деобфускации содержал корректную чексумму.
Если csum=valid, пересчет чексуммы производится после модификации пейлоада, как для исходящих, так и для входящих пакетов.
Тем самым обеспечивается видимость передачи пакетов с валидной чексуммой. Коррекция чексуммы на входящем пакете необходима,
если устройство с ipobfs не является получателем пакета, а выполняет функцию роутера (forward). Чтобы на выходном интерфейсе был валидный пакет.
Обычный получатель не примет пакеты с инвалидной чексуммой.

Параметр debug=1 включает вывод отладочной информации. Вы увидите что делается с каждым обрабатываемым пакетом в dmesg.
Его стоит применять только для отладки. При большом количестве пакетов система сильно затормозится из-за избыточного вывода в dmesg.

Посмотреть и изменить некоторые параметры ipobfs можно без перезагрузки модуля : /sys/module/ipobfs/parameters

СБОРКА МОДУЛЯ ЯДРА на традиционной linux системе :
установить заголовки ядра. для debian :
sudo apt-get install linux-headers.....
cd ipobfs_mod
make
sudo make install

ЗАМЕЧАНИЕ ПО СКОРОСТИ :
Если задан только ipproto-xor, замеделение можно считать нулевым. Выполняется простейшая операция изменения 1 байта.
Тем не менее даже такой трюк может помочь обойти dpi.
Если используется data-xor, то старайтесь ксорить только начало пакета. Если ксорение не выходит за пределы ~100-140 байт,
скорее всего не придется выполнять ресурсоемкую операцию skb_linearize. Именно на ней тратится больше всего ресурсов cpu.
С debug=1 можно проверить выполняется ли linearize.

openwrt
-------

На системе linux скачайте и распакуйте SDK от вашей версии прошивки для вашего девайса.
Версия SDK должна в точности соответствовать версии прошивки, иначе вы не соберете подходящий модуль ядра.
Если вы собирали прошивку самостоятельно, вместо SDK можно и нужно использовать этот buildroot.
scripts/feeds update -a
scripts/feeds install -a
Скопируйте openwrt/* в SDK, сохраняя структуру директорий.
В packages/ipobfs, там где Makefile, поместите каталоги ipobfs и ipobfs_mod с исходниками.
Из корня SDK : make package/ipobfs/compile V=99
Должны получиться 2 ipk : bin/packages/..../ipobfs..ipk и bin/targets/..../kmod-ipobfs..ipk
Переносите их на девайс, устанавливаете через "opkg install ...ipk". Устанавливать можно только то, что вам нужно : ipobfs или kmod-ipobfs.


Пробитие NAT
------------

В общем случае можно утверждать, что NAT способны пропускать лишь трафик tcp, udp, icmp.
+ некоторые NAT еще содержат хелперы для пропуска особых протоколов (GRE). Но не все и не на всех устройствах.
NAT может пропускать нестандартные IP протоколы, но у него нет средств отследить исходный IP, который инициировал
отсылку. Если нестандартные протоколы и будут работать через NAT, то только для одного устройства за NAT.
Использование одного IP протокола более чем одним устройством за NAT невозможно. Будет конфликт.
Учитывая этот факт, ipproto-xor применять можно, но осторожно.

Рассмотрим NAT на базе linux (почти все домашние роутеры) без хелперов.
Как показывает исследование, для него важны поля транспортного хедера, содержащие длину пейлоада и флаги.
Поэтому, минимальный xor-data-offset для tcp - 14 , для udp - 6. Иначе пакет вовсе не пройдет NAT.

Любой NAT обязательно будет следить за флагами tcp, ведь по ним conntrack определяет начало установки соединения.
Без conntrack не работает ни один NAT. Смещение флагов в tcp header - 13.

Linux conntrack по умолчанию проверяет чексуммы транспортных протоколов и не учитывает пакеты с инвалидной чексуммой.
Такие пакеты не вызывают появление или изменение записей в таблице conntrack, статус пакетов - INVALID, операция SNAT
к ним применена не будет, тем не менее форвардинг пакета все равно случится в неизменном виде, с сохранением адреса источника
из внутренней сети. Чтобы этого избежать, правильно настроенные роутеры применяет правила вида
"-m state --state INVALID -j DROP" или "-m conntrack --ctstate INVALID -j DROP", тем самым запрещая форвардинг
пакетов, от которых отказался conntrack.
Поведение можно изменить командой "sysctl -w net.netfilter.nf_conntrack_checksum=0". В этом случае чексуммы считаться не будут,
conntrack будет принимать пакеты даже с инвалидной чексуммой, NAT работать будет.
В openwrt по умолчанию net.netfilter.nf_conntrack_checksum=0, так что система пропускает инвалидные пакеты.
Но остальные роутеры как правило не меняют значение по умолчанию, которое =1.

Все без исключения NAT будут исправлять 2-байтовую чексумму в tcp (смещение 18) и udp (смещение 6),
поскольку она считается с учетом ip источника и назначения. NAT меняет ip источника при отсылке, при невозможности
сохранить исходный source port меняется и он. Для экономии ресурсов полный пересчет суммы обычно не производится.
За основу берется исходная сумма, к ней добавляется разница между исходными и измененными значениями.
К получателю приходит пакет с инвалидной суммой, далее он деобфусцируется средствами ipobfs и сумма становится валидной,
если при обфускации не была затронута исходная сумма, то есть data-xor-offset>=20 для tcp и data-xor-offset>=8 для udp.
Обфускатор работает по xor, checksum считается сложением, поэтому они несовместимы.
ipobfs по умолчанию не пересчитывает чексуммы транспортных хедеров, поэтому если на принимающем конце используется он, то
data-xor-offset необходимо делать таким, чтобы чексумма не затрагивалась, иначе пакет будет отброшен системой после деобфускации.
альтернатива - использовать опцию --csum=fix
ipobfs_mod отключает проверку чексумм, поэтому при его использовании этой проблемы нет. поведение по умолчанию аналогично --csum=fix
Если применяется ipproto_xor, то роутер не будет пересчитывать суммы, пакет придет с инвалидной суммой после деобфускации.

Большинство роутеров выполняют mss fix ( -j TCPMSS --clamp-mss-to-pmtu  или  -j TCPMSS --set-mss ).
mss находится в опциях tcp header. Windows и linux шлют mss первой опцией. Сама опция занимает 4 байта.
Выходит, минимальный xor-data-offset для tcp поднимается до 24, чтобы не трогать mss, который попортит роутер.

ИТОГ :
 tcp : data-xor-offset>=24
 udp : data-xor-offset>=8

Если NAT не пропускает пакеты с инвалидной чексуммой, используйте настройку --csum=valid.
С точки зрения нагрузки на cpu предпочительней будет не использовать режим --csum=valid, если NAT пропускает пакеты с инвалидной чексуммой.

Есть информация, что некоторые мобильные операторы производят терминацию tcp на своих серверах для последующего
проксирования к точке исходного назначения. В этом случае любая модификация tcp не на уровне потока данных обречена на провал.
Терминирующий middlebox отвергнет пакеты с испорченным заголовком или неверной чексуммой.
Исходящее соединение от middlebox не будет повторять такое же разбиение на пакеты, как исходное соединение.
Пользуйтесь obfsproxy.

Releases

No releases published

Packages

No packages published