Compare commits

...
8 Commits
Author SHA1 Message Date
bol-van 8283f4cd76 update makefiles 2026-10-29 15:55:59 +03:00
bol-van 019bbe845f update docs 2026-10-28 10:03:42 +03:00
bol-van 9679d58499 cosmetic 2026-10-28 06:43:50 +03:00
bol-van ca7ed6a986 zapret-obfs: udp2icmp update id cache access time in both directions 2026-10-27 19:01:17 +03:00
bol-van c460884213 zapret-obfs: support cgnat in udp2icmp 2026-10-27 18:50:51 +03:00
bol-van 3570f193d5 update docs 2026-10-27 18:48:28 +03:00
bol-van 8be19e079d update docs 2026-10-27 11:23:37 +03:00
bol-van 4a33055448 nfqws2: fix UAF 2026-10-22 11:06:42 +03:00
5 changed files with 104 additions and 24 deletions

No files matched your search

+7 -1
View File
@@ -430,7 +430,13 @@ nfqws2 \
### Не только лишь автономный обман DPI
Рабочий тестовый пример icmp обфускатора udp от винды к серверу на vps.
Для теста используем wireguard. Ничего в конфигах менять не надо - wireguard будет думать, что он работает по udp, но на самом деле он преобразуется в пинги icmp, которые могут проходить NAT. Размер пакетов не изменяется, потому проблемы MTU нет.
Для теста используем wireguard. Ничего в конфигах менять не надо - wireguard будет думать, что он работает по udp, но на самом деле он преобразуется в пинги icmp, которые могут проходить NAT.
Если NAT не меняет поле Identifier в icmp (типично для Linux NAT), можно использовать легкий stateless вариант, когда размер пакета не меняется, и он с гарантией влезает в MTU.
Провайдерский CGNAT меняет Identifier. Для его поддержки существует параметр udp2icmp `cgnat`. Клиент увеличит размеры исходящих пакетов на 2 байта, поэтому нужно предварительно
уменьшить MTU интерфейса wireguard на 2, если он выбран "под завязку". В режиме cgnat серверный конец будет вести базу измененных идентификаторов и сохранять их при ответе.
Обычно на CGNAT время жизни icmp identifier небольшое - 30 секунд, но может быть еще меньше, и освежаться может только исходящими пакетами. Поэтому нужен частый keepalive со стороны клиента.
Если этого не сделать, будет обрыв до момента, когда wireguard клиент что-либо пошлет. Некоторые операторы троттлят icmp, поэтому могут быть проблемы со скоростью.
Будем загонять исходящие с клиента в icmp type 8 (echo request) code 199 , исходящие с сервера в icmp type 0 (echo reply) code 199.
Код у обоих концов делаем одинаковый, иначе NAT не соотнесет. Без NAT можно коды делать разными для клиента и сервера.
+9 -7
View File
@@ -1,11 +1,10 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS_PIC = -fPIC
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto $(CFLAGS_PIC)
CFLAGS += -std=gnu99 $(OPTIMIZE)
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LDFLAGS_PIE = -pie
LDFLAGS += $(LDFLAGS_PIE)
LIBS =
LIBS_WIN = -lws2_32
@@ -14,24 +13,27 @@ SRC_FILES = ip2net.c qsort.c
all: ip2net
ip2net: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
systemd: ip2net
android: ip2net
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
strip ip2neta ip2netx
lipo -create -output ip2net ip2netx ip2neta
rm -f ip2netx ip2neta
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
win-arm64-cross:
clang -s $(CFLAGS) $(CFLAGS_WIN) --sysroot=/clangarm64 --target=aarch64-w64-mingw32 -B/clang64/bin -rtlib=compiler-rt -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f ip2net *.o
+69 -4
View File
@@ -173,6 +173,8 @@ end
-- arg : scode - server icmp code
-- arg : dataxor - blob to xor udp payload
-- arg : server=[0|1] - override server mode. by default use "--server" nfqws2 parameter
-- arg : cgnat - do not rely on identifier - cgnat in most cases changes it. expand udp packet size by 2 (may exceed MTU !)
-- arg : id_lifetime - mapped identifiers lifetime in seconds
function udp2icmp(ctx, desync)
local dataxor
local bserver
@@ -221,6 +223,25 @@ function udp2icmp(ctx, desync)
desync.dis.payload = bxor(desync.dis.payload, pattern(dataxor,1,#desync.dis.payload))
end
end
local function map_purge()
if udp2icmp_map then
local last = udp2icmp_map.purge or 0
local now = os.time()
local timeout = desync.arg.id_lifetime and tonumber(desync.arg.id_lifetime) or 60
if last~=now then
for k,v in pairs(udp2icmp_map) do
if type(v)=='table' and v.t then
if now>=(v.t + timeout) then
udp2icmp_map[k] = nil
end
end
end
udp2icmp_map.purge=now
end
end
end
map_purge()
if desync.arg.dataxor then
dataxor = blob(desync,desync.arg.dataxor)
@@ -234,19 +255,63 @@ function udp2icmp(ctx, desync)
if b_debug then -- save some cpu
DLOG("udp2icmp: udp => icmp sport="..desync.dis.udp.uh_sport.." dport="..desync.dis.udp.uh_dport.." size="..#desync.dis.payload)
end
local id = bitxor(desync.dis.udp.uh_sport,desync.dis.udp.uh_dport)
if desync.arg.cgnat then
if bserver then
-- extract original icmp identifier from the map. it must match original or NAT will not deliver
local map_id = (desync.dis.ip6 and desync.dis.ip6.ip6_dst or desync.dis.ip.ip_dst)..
(desync.dis.ip6 and desync.dis.ip6.ip6_src or desync.dis.ip.ip_src)..
bu16(desync.dis.udp.uh_dport)..bu16(desync.dis.udp.uh_sport)
if udp2icmp_map then
local id_ = udp2icmp_map[map_id]
if id_ then
id=id_.id
if b_debug then DLOG(string.format("udp2icmp: found mapped icmp identifier %04X",id)) end
id_.t=os.time() -- update time
else
DLOG("udp2icmp: mapped icmp identifier NOT found !")
end
end
else
-- cgnat changes identifier. save it in the end of the packet, expand packet size by 2
desync.dis.payload = desync.dis.payload .. bu16(id)
DLOG("udp2icmp: expanding udp size to "..#desync.dis.payload)
end
end
desync.dis.icmp = {
icmp_type = ictype(true),
icmp_code = iccode(true),
icmp_data = u32(
bu16(bitxor(desync.dis.udp.uh_sport,desync.dis.udp.uh_dport))..
(bserver and bu16(desync.dis.udp.uh_sport) or bu16(desync.dis.udp.uh_dport)))
icmp_data = u32(bu16(id)..(bserver and bu16(desync.dis.udp.uh_sport) or bu16(desync.dis.udp.uh_dport)))
}
desync.dis.udp = nil
fix_ip_proto(desync.dis)
return VERDICT_MODIFY
elseif desync.dis.icmp and desync.dis.icmp.icmp_type==ictype(false) and desync.dis.icmp.icmp_code==iccode(false) then
local pl = bitand(desync.dis.icmp.icmp_data,0xFFFF)
local pm = bitxor(bitrshift(desync.dis.icmp.icmp_data,16),pl)
local pm
local id = bitrshift(desync.dis.icmp.icmp_data,16)
if bserver and desync.arg.cgnat then
if #desync.dis.payload<2 then
DLOG("udp2icmp: udp is less than 2 bytes and cgnat is on ! doing nothing")
return VERDICT_PASS
end
if not udp2icmp_map then udp2icmp_map={} end
pm = bitxor(u16(desync.dis.payload,#desync.dis.payload-1),pl) -- use right 2 bytes
desync.dis.payload = string.sub(desync.dis.payload,1,-3) -- shrink right 2 bytes
DLOG("udp2icmp: shrinking udp size to "..#desync.dis.payload)
-- keep original icmp identifier indexed by ip/port tuple
local map_id = (desync.dis.ip6 and desync.dis.ip6.ip6_src or desync.dis.ip.ip_src)..
(desync.dis.ip6 and desync.dis.ip6.ip6_dst or desync.dis.ip.ip_dst)..
bu16(pm)..bu16(pl)
udp2icmp_map[map_id] = { id=id, t=os.time() }
if b_debug then DLOG(string.format("udp2icmp: stored mapped icmp identifier %04X",id)) end
else
pm = bitxor(id,pl)
end
desync.dis.udp = {
uh_sport = bserver and pm or pl,
uh_dport = bserver and pl or pm,
+11 -9
View File
@@ -1,38 +1,40 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS_PIC = -fPIC
CFLAGS += -std=gnu99 $(OPTIMIZE) $(CFLAGS_PIC)
CFLAGS += -std=gnu99 $(OPTIMIZE)
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LDFLAGS_PIE = -pie
LDFLAGS += $(LDFLAGS_PIE)
LIBS = -lpthread
LIBS_ANDROID =
LIBS_WIN = -lws2_32
LIBS_WIN = -lws2_32 -lpthread
SRC_FILES = *.c
all: mdig
mdig: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
systemd: mdig
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS) $(LDFLAGS_PIE)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) $(LDFLAGS_PIE)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) $(LDFLAGS_PIE)
strip mdiga mdigx
lipo -create -output mdig mdigx mdiga
rm -f mdigx mdiga
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
win-arm64-cross:
clang -s $(CFLAGS) $(CFLAGS_WIN) --sysroot=/clangarm64 --target=aarch64-w64-mingw32 -B/clang64/bin -rtlib=compiler-rt -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f mdig *.o
+8 -3
View File
@@ -1627,9 +1627,7 @@ static uint8_t dpi_desync_tcp_packet_play(
{
// likely exceeded packet limit or unlikely out of memory
DLOG_ERR("rawpacket_queue failed !\n");
reasm_client_cancel(ps.ctrack);
ps.l7payload = L7P_UNKNOWN; // middle packet may be not L7P_TLS_CLIENT_HELLO
goto rediscover;
goto reasm_cancel_rediscover;
}
if (ReasmIsFull(&ps.ctrack->reasm_client))
{
@@ -1661,6 +1659,13 @@ pass:
pass_reasm_cancel:
reasm_client_cancel(ps.ctrack);
goto pass;
reasm_cancel_rediscover:
reasm_client_cancel(ps.ctrack);
rdata_payload = dis->data_payload;
rlen_payload = dis->len_payload;
ps.l7payload = L7P_UNKNOWN; // could be middle packet
goto rediscover;
}
// return : true - should continue, false - should stop with verdict