Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-14 06:13:09 +00:00

44 Commits

Author SHA1 Message Date
bol-van
38454aabfa winws2: fix sandbox reinit error 2025-11-24 18:32:28 +03:00
bol-van
14503e0a57 winws2: set low integrity on writable folder 2025-11-24 18:27:18 +03:00
bol-van
ef421bad9c winws2: create writeable zapret2 dir in LocalLow and set WRITEABLE env 2025-11-24 17:33:17 +03:00
bol-van
e963b6f20b winws2: use cygwin_conv_path 2025-11-24 17:21:46 +03:00
bol-van
c52a3a2e75 winws2: define APPDATALOW env for LUA code for file storage 2025-11-24 17:08:04 +03:00
bol-van
b2f7fac102 fix missing update 2025-11-24 15:40:48 +03:00
bol-van
ea566720a5 nfqws: optimize logging to a file 2025-11-24 15:34:35 +03:00
bol-van
22c7ee257e Merge pull request #7 from Pavel4e5/typos
Fix typos in function descriptions
2025-11-24 13:55:06 +03:00
bol-van
a5a81424c8 winws2: enable sandbox even if nlm/ssid filter is present 2025-11-24 13:40:13 +03:00
bol-van
fba42f8a00 winws2: fix utf8 file names when setting mandatory level 2025-11-24 08:50:51 +03:00
bol-van
b4aff06c35 winws2: fix utf8 file names when setting mandatory level 2025-11-24 08:19:42 +03:00
bol-van
2b85262ee2 winws2: set low mandatory label on logs and autohostlist 2025-11-23 23:25:16 +03:00
bol-van
3b92197bb3 winws2: init lua after sandbox init 2025-11-23 21:03:39 +03:00
bol-van
9d49f35324 winws2: set low mandatory if possible 2025-11-23 20:52:33 +03:00
bol-van
ed4eb043a2 zapret-wgobfs: optimize key cache 2025-11-23 19:06:24 +03:00
bol-van
3a66f86621 zapret-wgobfs: optimize key cache 2025-11-23 19:02:47 +03:00
bol-van
c80efcc983 zapret-tests.lua: aes_ctr const tests 2025-11-23 16:09:11 +03:00
bol-van
c65b28c3f7 nfqws2: optimize aes-ctr gamma xor 2025-11-23 15:15:39 +03:00
Pavel4e5
6d74e6e873 Rename tcp_unflags_set to tcp_flags_unset 2025-11-23 16:16:39 +05:00
Pavel4e5
200ca70f82 Rename tcp_unflags_set to tcp_flags_unset 2025-11-23 16:14:24 +05:00
bol-van
f1d02aa81d zapret_antidpi: rst 2025-11-23 13:51:54 +03:00
bol-van
38ef16bcdf nfqws2: update crypto 2025-11-23 13:34:49 +03:00
bol-van
d0fe0f25ba nfqws2: aes_ctr luacall 2025-11-23 13:27:31 +03:00
bol-van
0ab1e19ce2 nfqws2: 'known' protocol and payload filter 2025-11-23 12:37:51 +03:00
bol-van
9a6353cb14 nfqws2: add mtproto detection 2025-11-23 12:22:16 +03:00
bol-van
0bc4ce03d2 update fake 2025-11-22 21:54:21 +03:00
bol-van
a1f28e4c4a lua: payload check for drop 2025-11-22 21:12:29 +03:00
bol-van
a7e6f07ae4 winws: remove unused var 2025-11-22 19:42:16 +03:00
bol-van
fd0a076504 winws: remove all privs from access token 2025-11-22 19:39:32 +03:00
bol-van
5603e1c760 winws: remove all privs from access token 2025-11-22 19:37:55 +03:00
bol-van
873262e49c build luajit without FFI 2025-11-22 16:00:44 +03:00
bol-van
99f64c9b16 nfqws2: remove package.loaded.debug 2025-11-22 15:34:35 +03:00
bol-van
9ab4ff2acb zapret-antidpi.lua: fix comment 2025-11-22 13:51:00 +03:00
bol-van
11ab7f7776 zapret-antidpi.lua: fix comment 2025-11-22 13:50:35 +03:00
bol-van
b972dadc72 zapret-antidpi.lua: fix comment 2025-11-22 13:49:19 +03:00
bol-van
02a5c28db4 zapret-antidpi.lua: fix comment 2025-11-22 13:46:19 +03:00
bol-van
792fc02268 zapret-antidpi.lua: fix comment 2025-11-22 13:45:01 +03:00
bol-van
402943034a zapret-antidpi.lua: add tcp_ts_up 2025-11-22 13:43:24 +03:00
bol-van
0f8c3f3fd8 zapret-antidpi.lua: fix comment 2025-11-22 13:41:26 +03:00
bol-van
89b6a65e16 Merge pull request #3 from Pavel4e5/dir_names
Typo in dir name
2025-11-22 12:54:22 +03:00
bol-van
94aded5d21 update docs 2025-11-22 12:48:34 +03:00
bol-van
d5c608d5d8 update docs 2025-11-22 12:39:46 +03:00
bol-van
be7854eefe nfqws2: fix erroneus error logging 2025-11-22 12:17:29 +03:00
Pavel4e5
b577d3cc22 Typo in dir name 2025-11-22 12:43:48 +05:00
23 changed files with 759 additions and 187 deletions

View File

@@ -131,7 +131,7 @@ jobs:
esac
(
cd luajit2-*
make BUILDMODE=static HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -s -flto=auto $CFLAGS" -j$(nproc)
make BUILDMODE=static XCFLAGS=-DLUAJIT_DISABLE_FFI HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -s -flto=auto $CFLAGS" -j$(nproc)
make install PREFIX= DESTDIR=$DEPS_DIR
)
LJIT=1
@@ -242,7 +242,7 @@ jobs:
esac
(
cd luajit2-*
make BUILDMODE=static HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -flto=auto $CFLAGS" -j$(nproc)
make BUILDMODE=static XCFLAGS=-DLUAJIT_DISABLE_FFI HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -flto=auto $CFLAGS" -j$(nproc)
make install PREFIX= DESTDIR=$DEPS_DIR
)
LJIT=1
@@ -319,7 +319,7 @@ jobs:
wget -qO- https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz
(
cd luajit2-*
make BUILDMODE=static HOST_CC=gcc CC=$CC CFLAGS="-Os -flto=auto $CFLAGS"
make BUILDMODE=static XCFLAGS=-DLUAJIT_DISABLE_FFI HOST_CC=gcc CC=$CC CFLAGS="-Os -flto=auto $CFLAGS"
make install PREFIX= DESTDIR=$DEPS_DIR
)
@@ -430,7 +430,7 @@ jobs:
wget -q https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz &&
tar -xzf v${LUAJIT_RELEASE}.tar.gz &&
rm -f v${LUAJIT_RELEASE}.tar.gz &&
make -C luajit2-${LUAJIT_RELEASE} BUILDMODE=static CFLAGS="-Os -s" &&
make -C luajit2-${LUAJIT_RELEASE} BUILDMODE=static XCFLAGS=-DLUAJIT_DISABLE_FFI CFLAGS="-Os -s" &&
make -C luajit2-${LUAJIT_RELEASE} install
- name: Build winws

20
docs/changes.txt Normal file
View File

@@ -0,0 +1,20 @@
v0.1.0
first public release
v0.1.1
* nfqws2: fixed crash on 32-bit platforms if debug is enabled
v0.1.2
* nfqws2: 'mtproto' protocol, 'mtproto_initial' payload
* nfqws2: 'known' protocol and payload filter
* nfqws2: 'aes_ctr' luacall
* zapret-antidpi: rst
* github actions: remove FFI from luajit
v0.1.4
* winws2: set low mandatory level in process token if possible : no --wlan-filter or --nlm-filter (no windivert reinit required)
* nfqws2: optimize debug logging to file

View File

@@ -16,9 +16,9 @@ download latest releast, unpack, cd to it's directory
make BUILDMODE=static CFLAGS="-Os"
make install
5) cd to %ZAPRET_BASE%/nfq
5) cd to %ZAPRET_BASE%/nfq2
cd C:/Users/user/Downloads/zapret2/nfq
cd C:/Users/user/Downloads/zapret2/nfq2
6) Compile nfqws2

View File

@@ -278,6 +278,93 @@ nfqws --dpi-desync=ipfrag2 --dpi-desync-ipfrag-pos-udp=8
nfqws2 --lua-desync=send:ipfrag:ipfrag_pos_udp=8 --lua-desync=drop
```
Рассмотрим теперь пример из zapret-win-bundle. Как `preset_example.cmd` был переписан в `preset2_example.cmd`.
Фильтр windivert поменялся только одним - больше нет параметров `--wf-tcp` и `--wf-udp`. Они разделены по направлениям in/out.
Для отлова UDP не перехватывается весь udp порт - используются пейлоад фильтры windivert. Тем самым во много раз экономятся ресурсы процессора,
вплоть до сотен раз. Когда попадет что-то на мощную выгрузку торрента, и она пойдет через winws, вы вполне можете словить загрузку целого ядра CPU
и вой кулеров вашего ноута. А так его не будет.
Для TCP так тоже можно было бы сделать, но не всегда. Во-первых, надо перехватывать SYN по порту, чтобы работал conntrack.
Но это решаемо. А что не решаемо - это перехват вторых частей kyber tls hello. Их невозможно опознать без связи с предыдущими фрагментами. Поэтому перехватывается весь порт.
Для HTTP вопрос решаемый, поскольку там нет реассемблирования запросов, но http сейчас стал настолько редким, что и смысла нет заморачиваться.
Везде расставлены фильтры профиля мультистратегии `--filter-l7`, фильтры по `--out-range` и по `--payload`.
Зачем ? В основном для сокращения вызовов LUA кода, который заведомо медленнее C кода.
Если пакет не попадет в профили с LUA - ни о каком вызове кода LUA речи быть не может.
Если пакет попал в профиль с LUA, то после первых 10 пакетов с данными наступает отсечение по верхней границе range. Все LUA инстансы входят в состояние instance cutoff,
соединение входит в состояние "lua cutoff" по направлению "out". Значит вызовов LUA не будет вообще. Не просто вызовов, а даже обращения к движку LUA
с какой-либо целью. Будет только C код, который посмотрит на признак "cutoff" и сразу же отпустит пакет.
Так же везде расставлены фильтры по payload type. Отчасти так же с целью сократить вызовы LUA даже в пределах первых 10 пакетов с данными.
С другой стороны, даже при совпадении протокола соединения (`--filter-l7`) может пробежать не интересующий нас пейлоад.
По умолчанию многие функции из `zapret-antidpi.lua` реагируют только на известные типы пейлоада, но не на конкретные, а на любые известные.
Если допустить малореальный, но гипотетически возможный сценарий, что в рамках протокола http будет отправлен блок данных с tls или фраза, похожая на сообщение из xmpp,
то тип пейлоада выскочит tls_client_hello или xmpp_stream, например. Лучше от этого сразу уберечься. Тем более что в других видах протоколов - xmpp, например, -
пейлоады могут проскакивать нескольких типов вполне ожидаемо. Но работать надо не по всем.
В фейке для TLS по умолчанию - fake_default_tls - однократно при старте меняется SNI с "www.microsoft.com" на случайный и рандомизируется поле "random" в TLS handshake.
Это делается простой строчкой LUA кода. Больше нет никаких специальных параметров *nfqws2* для модификации пейлоадов.
В профиле для youtube на лету меняется SNI на "www.google.com", копируется поле TLS "session id" с обрабатываемого в данный момент TLS handshake.
```
start "zapret: http,https,quic" /min "%~dp0winws.exe" ^
--wf-tcp=80,443 ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.discord_media.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.stun.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.wireguard.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.quic_initial_ietf.txt" ^
--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --new ^
--filter-tcp=443 --hostlist="%~dp0files\list-youtube.txt" --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=md5sig --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new ^
--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=midsld --dpi-desync-repeats=6 --dpi-desync-fooling=badseq,md5sig --new ^
--filter-l7=quic --hostlist="%~dp0files\list-youtube.txt" --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic="%~dp0files\quic_initial_www_google_com.bin" --new ^
--filter-l7=quic --dpi-desync=fake --dpi-desync-repeats=11 ^
--filter-l7=wireguard,stun,discord --dpi-desync=fake --dpi-desync-repeats=2
start "zapret: http,https,quic" /min "%~dp0winws2.exe" ^
--wf-tcp-out=80,443 ^
--lua-init=@"%~dp0lua\zapret-lib.lua" --lua-init=@"%~dp0lua\zapret-antidpi.lua" ^
--lua-init="fake_default_tls = tls_mod(fake_default_tls,'rnd,rndsni')" ^
--blob=quic_google:@"%~dp0files\quic_initial_www_google_com.bin" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.discord_media.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.stun.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.wireguard.txt" ^
--wf-raw-part=@"%~dp0windivert.filter\windivert_part.quic_initial_ietf.txt" ^
--filter-tcp=80 --filter-l7=http ^
--out-range=-d10 ^
--payload=http_req ^
--lua-desync=fake:blob=fake_default_http:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5 ^
--lua-desync=fakedsplit:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5 ^
--new ^
--filter-tcp=443 --filter-l7=tls --hostlist="%~dp0files\list-youtube.txt" ^
--out-range=-d10 ^
--payload=tls_client_hello ^
--lua-desync=fake:blob=fake_default_tls:tcp_md5:repeats=11:tls_mod=rnd,dupsid,sni=www.google.com ^
--lua-desync=multidisorder:pos=1,midsld ^
--new ^
--filter-tcp=443 --filter-l7=tls ^
--out-range=-d10 ^
--payload=tls_client_hello ^
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000:repeats=6 ^
--lua-desync=multidisorder:pos=midsld ^
--new ^
--filter-udp=443 --filter-l7=quic --hostlist="%~dp0files\list-youtube.txt" ^
--out-range=-d10 ^
--payload=quic_initial ^
--lua-desync=fake:blob=quic_google:repeats=11 ^
--new ^
--filter-udp=443 --filter-l7=quic ^
--out-range=-d10 ^
--payload=quic_initial ^
--lua-desync=fake:blob=fake_default_quic:repeats=11 ^
--new ^
--filter-l7=wireguard,stun,discord ^
--out-range=-d10 ^
--payload=wireguard_initiation,wireguard_cookie,stun_binding_req,discord_ip_discovery ^
--lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2
```
### Какие есть еще параметры
Как узнать какие есть еще функции и какие у них бывают параметры ? Смотрите `zapret-antidpi.lua`. Перед каждой функцией подробно описано какие параметры она берет.

View File

@@ -1,12 +1,12 @@
REGISTER sip:192.168.102.23 SIP/2.0
Via: SIP/2.0/UDP 192.168.102.250:51781;rport;branch=z9hG4bKPj3f42ea713eca49ec93f6cb69f6c38014
Max-Forwards: 70
From: <sip:233@192.168.102.23>;tag=ca565d7bd4e24a6d80c631d395ee117e
To: <sip:233@192.168.102.23>
Call-ID: a5e53e302b2d4a4d83c1455527c882c3
CSeq: 26538 REGISTER
REGISTER sip:192.168.1.1 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.2:42931;rport;branch=z9hG4bKPj3fd2e8713ffcd90c43f6ce69f6c98461
Max-Forwards: 50
From: <sip:703@192.168.1.1>;tag=ca565d7bd4e24a6d80c631d395ee117e
To: <sip:703@192.168.1.1>
Call-ID: dfec38302b8cea3d83c1452527c895c1
CSeq: 26139 REGISTER
User-Agent: MicroSIP/3.21.5
Contact: <sip:233@192.168.102.250:51781;ob>
Contact: <sip:703@192.168.1.2:42931;ob>
Expires: 300
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
Content-Length: 0

View File

@@ -25,7 +25,7 @@ standard fooling :
* ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
* ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_hopbyhop2 - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
* ip6_hopbyhop2[=hex] - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
* ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
@@ -35,9 +35,10 @@ standard fooling :
* tcp_ts=N - add N to timestamp value
* tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
* tcp_flags_set=<list> - set tcp flags in comma separated list
* tcp_unflags_set=<list> - unset tcp flags in comma separated list
* tcp_flags_unset=<list> - unset tcp flags in comma separated list
* tcp_ts_up - move timestamp tcp option to the top if present (workaround for badack without badseq fooling)
* fool - custom fooling function : fool_func(dis, fooling_options)
* fool=fool_function - custom fooling function : fool_func(dis, fooling_options)
standard reconstruct :
@@ -60,11 +61,11 @@ standard ip_id :
standard ipfrag :
* ipfrag - ipfrag function name. "ipfrag2" by default if empty
* ipfrag[=frag_function] - ipfrag function name. "ipfrag2" by default if empty
* ipfrag_disorder - send fragments from last to first
* ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
* ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
* ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
* ipfrag2 : ipfrag_disorder - send fragments from last to first
]]
@@ -82,17 +83,17 @@ function pktdebug(ctx, desync)
end
-- drop packet
-- standard args : direction
-- standard args : direction, payload
function drop(ctx, desync)
direction_cutoff_opposite(ctx, desync, "any")
if direction_check(desync, "any") then
if direction_check(desync, "any") and payload_check(desync,"all") then
DLOG("drop")
return VERDICT_DROP
end
end
-- nfqws1 : "--dup"
-- standard args : direction, fooling, ip_id, rawsend, reconstruct
-- standard args : direction, fooling, ip_id, ipfrag, rawsend, reconstruct
function send(ctx, desync)
direction_cutoff_opposite(ctx, desync, "any")
if direction_check(desync, "any") then
@@ -256,7 +257,7 @@ function synack(ctx, desync)
end
-- nfqws1 : "--wssize"
-- nfqws1 : "--wsize"
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
function wsize(ctx, desync)
@@ -273,7 +274,7 @@ function wsize(ctx, desync)
end
end
-- nfqws1 : "--wsize"
-- nfqws1 : "--wssize"
-- standard args : direction
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
@@ -322,6 +323,31 @@ function syndata(ctx, desync)
end
end
-- nfqws1 : "--dpi-desync=rst"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : rstack - send RST,ACK instead of RST
function rst(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if direction_check(desync, "any") and payload_check(desync) then
if replay_first(desync) then
local dis = deepcopy(desync.dis)
dis.payload = ""
dis.tcp.th_flags = TH_RST + (desync.arg.rstack and TH_ACK or 0)
apply_fooling(desync, dis)
apply_ip_id(desync, dis, nil, "none")
DLOG("rst")
-- it uses rawsend, reconstruct and ipfrag options
rawsend_dissect_ipfrag(dis, desync_opts(desync))
else
DLOG("rst: not acting on further replay pieces")
end
end
end
-- nfqws1 : "--dpi-desync=fake"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : blob=<blob> - fake payload

View File

@@ -423,7 +423,7 @@ end
-- tcp_ts=N - add N to timestamp value
-- tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
-- tcp_flags_set=<list> - set tcp flags in comma separated list
-- tcp_unflags_set=<list> - unset tcp flags in comma separated list
-- tcp_flags_unset=<list> - unset tcp flags in comma separated list
-- tcp_ts_up - move timestamp tcp option to the top if it's present. this allows linux not to accept badack segments without badseq. this is very strange discovery but it works.
-- fool - custom fooling function : fool_func(dis, fooling_options)
@@ -762,20 +762,19 @@ function direction_cutoff_opposite(ctx, desync, def)
end
end
-- check if desync payload type comply with payload type list in arg.payload
-- if arg.payload is not present - check if desync payload is not "empty" and not "unknown" (nfqws1 behavior without "--desync-any-protocol" option)
function payload_check(desync)
if desync.arg.payload and desync.arg.payload~="known" then
if not in_list(desync.arg.payload, "all") and not in_list(desync.arg.payload, desync.l7payload) then
DLOG("payload_check: payload '"..desync.l7payload.."' does not pass '"..desync.arg.payload.."' filter")
return false
end
else
if desync.l7payload=="empty" or desync.l7payload=="unknown" then
DLOG("payload_check: payload filter accepts only known protocols")
return false
end
-- if arg.payload is not present - check for known payload - not empty and not unknown (nfqws1 behavior without "--desync-any-protocol" option)
-- if arg.payload is prefixed with '~' - it means negation
function payload_check(desync, def)
local b
local argpl = desync.arg.payload or def or "known"
local neg = string.sub(argpl,1,1)=="~"
local pl = neg and string.sub(argpl,2) or argpl
b = neg ~= (in_list(pl, "all") or in_list(pl, desync.l7payload) or in_list(pl, "known") and desync.l7payload~="unknown" and desync.l7payload~="empty")
if not b then
DLOG("payload_check: payload '"..desync.l7payload.."' does not pass '"..argpl.."' filter")
end
return true
return b
end
-- return name of replay drop field in track.lua_state for the current desync function instance
@@ -978,3 +977,4 @@ function ipfrag2(dis, ipfrag_options)
return {dis1,dis2}
end

View File

@@ -18,7 +18,7 @@ end
function test_crypto()
test_run({test_random, test_aes, test_aes_gcm, test_hkdf, test_hash})
test_run({test_random, test_aes, test_aes_gcm, test_aes_ctr, test_hkdf, test_hash})
end
function test_random()
@@ -187,7 +187,68 @@ function test_aes_gcm()
end
end
function test_aes_ctr()
local clear_text="test message "..brandom_az09(math.random(10,50))
local iv, key, encrypted, decrypted
for key_size=16,32,8 do
iv = brandom(16)
key = brandom(key_size)
print()
print("* aes_ctr test key_size "..tostring(key_size))
print("clear text: "..clear_text);
print("* encrypting")
encrypted = aes_ctr(key, iv, clear_text)
print("encrypted: "..str_or_hex(encrypted))
print("* decrypting")
decrypted = aes_ctr(key, iv, encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==clear_text)
print("* decrypting with bad key")
decrypted = aes_ctr(bu8(u8(string.sub(key,1,1))+1)..string.sub(key,2), iv, encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("* decrypting with bad iv")
decrypted = aes_ctr(key, bu8(u8(string.sub(iv,1,1))+1)..string.sub(iv,2), encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
end
-- openssl enc -aes-256-ctr -d -in rnd.bin -out rnd_decrypted.bin -K c39383634d87eb3b6e56edf2c8c0ba99cc8cadf000fb2cd737e37947eecde5fd -iv d745164b233f10b93945526ffe94b87f
print("* aes_ctr const tests")
local data="\x9d\x9c\xa0\x78\x2e\x17\x84\xfc\x87\xc7\xf5\xdf\x5b\xb5\x71\xfd\xb9\xcb\xd2\x4d\xae\x2f\xf0\x19\xf3\xad\x79\xa8\x9a\xb4\xed\x28\x88\x3c\xe1\x78\x91\x23\x27\xd4\x8d\x94\xb3\xd0\x81\x88\xd2\x55\x95\x8a\x88\x70\x67\x99\x75\xb2\xee\x30\x0f\xe7\xc6\x32\x10"
local iv="\xd7\x45\x16\x4b\x23\x3f\x10\xb9\x39\x45\x52\x6f\xfe\x94\xb8\x7f"
local tests = {
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99\xcc\x8c\xad\xf0\x00\xfb\x2c\xd7\x37\xe3\x79\x47\xee\xcd\xe5\xfd",
result="\x8C\x2C\x15\x99\x83\x37\x33\xEE\xA1\x70\xA7\x4A\x44\x2E\x6F\x56\x22\x41\xE1\xFC\xC5\x84\x21\x1C\x16\xC6\xE9\x75\x22\x57\x55\x4A\x02\x04\xCE\xAD\xE9\x0A\x45\xAB\x4E\x38\xB8\xB2\x6F\x95\xDA\x46\x4F\x9E\xB1\xFF\xF4\x40\x8A\x57\x25\xD2\xF6\xB6\x93\x65\x75"
},
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99\xcc\x8c\xad\xf0\x00\xfb\x2c\xd7",
result="\xB0\x4C\xC9\xDB\x0C\xE5\x67\x51\x1D\x24\x3C\x15\x87\x1B\xF9\x62\x84\x8C\xD0\x57\x33\x93\xE0\x71\x91\x3A\x11\x26\xCA\x77\xA7\x54\xBD\xC6\x5E\x96\x60\x2C\x94\x0F\xBA\x3E\x79\xDC\x48\xA0\x22\x97\xA7\x77\x55\xC8\x14\xEA\xC2\xF5\xA0\x88\x6F\xE2\x44\x32\x68"
},
{
key="\xc3\x93\x83\x63\x4d\x87\xeb\x3b\x6e\x56\xed\xf2\xc8\xc0\xba\x99",
result="\xD9\xAC\xC7\x7D\xC8\xC9\xF1\x59\x9A\xDF\x15\xF3\x58\x61\xFD\x2B\x1D\x01\x9A\x5F\x04\x53\xA2\xA8\xFD\x52\xDC\x8A\xE9\x3B\x2E\x5E\x0D\x13\xCB\xBD\x16\xED\xC1\xF2\x0D\x68\x62\xB7\xD5\x0F\x8D\xD4\xEB\xA1\xC5\x75\xF2\x0B\x26\x75\x1D\x7E\x5A\x37\xA6\x8A\xCD"
}
}
for k,t in pairs(tests) do
local decrypted = aes_ctr(t.key, iv, data)
io.write("KEY SIZE "..(#t.key*8).." ")
print( decrypted==t.result and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==t.result)
end
end
function test_ub()
for k,f in pairs({{u8,bu8,0xFF,8}, {u16,bu16,0xFFFF,16}, {u24,bu24,0xFFFFFF,24}, {u32,bu32,0xFFFFFFFF,32}}) do

View File

@@ -11,15 +11,12 @@ function wgobfs(ctx, desync)
local padmin = desync.arg.padmin and tonumber(desync.arg.padmin) or 0
local padmax = desync.arg.padmax and tonumber(desync.arg.padmax) or 16
local function genkey()
-- cache key in lua_state of conntrack is present
if desync.track and desync.track.lua_state.wgobfs_key then
key = desync.track.lua_state.wgobfs_key
end
-- cache key in a global var bound to instance name
local key_cache_name = desync.func_instance.."_key"
key = _G[key_cache_name]
if not key then
key = hkdf("sha256", "wgobfs_salt", desync.arg.secret, nil, 16)
if desync.track then
desync.track.lua_state.wgobfs_key = key
end
_G[key_cache_name] = key
end
return key
end
@@ -53,7 +50,7 @@ function wgobfs(ctx, desync)
DLOG("wgobfs: encrypting '"..desync.l7payload.."'. size "..#desync.dis.payload)
local key = genkey()
-- in aes-gcm every message require it's own crypto secure random iv
-- encryption more than one message with the same iv is considered catastrophic failure
-- encrypting more than one message with the same iv is considered catastrophic failure
-- iv must be sent with encrypted message
local iv = bcryptorandom(12)
local encrypted, atag = aes_gcm(true, key, iv, bu16(#desync.dis.payload)..desync.dis.payload..brandom(math.random(padmin,padmax)), nil)

58
nfq2/crypto/aes-ctr.c Normal file
View File

@@ -0,0 +1,58 @@
#include "aes-ctr.h"
#include <string.h>
#define AES_BLOCKLEN 16
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
void aes_ctr_xcrypt_buffer(aes_context *ctx, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out)
{
uint8_t bi, buffer[AES_BLOCKLEN], ivc[AES_BLOCKLEN];
size_t i, l16 = length & ~0xF;
memcpy(ivc, iv, AES_BLOCKLEN);
for (i = 0; i < l16; i += 16)
{
memcpy(buffer, ivc, AES_BLOCKLEN);
aes_cipher(ctx, buffer, buffer);
// Increment ivc and handle overflow
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
// inc will owerflow
if (ivc[bi] == 255)
{
ivc[bi] = 0;
continue;
}
ivc[bi]++;;
break;
}
*((uint64_t*)(out + i)) = *((uint64_t*)(in + i)) ^ ((uint64_t*)buffer)[0];
*((uint64_t*)(out + i + 8)) = *((uint64_t*)(in + i + 8)) ^ ((uint64_t*)buffer)[1];
}
if (i<length)
{
memcpy(buffer, ivc, AES_BLOCKLEN);
aes_cipher(ctx, buffer, buffer);
for (bi=0 ; i < length; i++, bi++)
out[i] = in[i] ^ buffer[bi];
}
}
int aes_ctr_crypt(const uint8_t *key, unsigned int key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out)
{
int ret = 0;
aes_context ctx;
aes_init_keygen_tables();
if (!(ret = aes_setkey(&ctx, AES_ENCRYPT, key, key_len)))
aes_ctr_xcrypt_buffer(&ctx, iv, in, length, out);
return ret;
}

7
nfq2/crypto/aes-ctr.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include "aes.h"
void aes_ctr_xcrypt_buffer(aes_context *ctx, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out);
int aes_ctr_crypt(const uint8_t *key, unsigned int key_len, const uint8_t *iv, const uint8_t *in, size_t length, uint8_t *out);

View File

@@ -5,6 +5,8 @@ int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_
int ret = 0;
gcm_context ctx;
gcm_initialize();
if (!(ret = gcm_setkey(&ctx, key, (const uint)key_len)))
{
ret = gcm_crypt_and_tag(&ctx, mode, iv, iv_len, adata, adata_len, input, output, input_length, atag, atag_len);

View File

@@ -20,8 +20,14 @@
#include "nfqws.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h>
#include <wlanapi.h>
#include <netlistmgr.h>
#include <aclapi.h>
#include <wchar.h>
#include <KnownFolders.h>
#include <shlobj.h>
#ifndef ERROR_INVALID_IMAGE_HASH
#define ERROR_INVALID_IMAGE_HASH __MSABI_LONG(577)
@@ -88,26 +94,22 @@ uint8_t tcp_find_scale_factor(const struct tcphdr *tcp)
if (scale && scale[1]==3) return scale[2];
return SCALE_NONE;
}
bool tcp_has_fastopen(const struct tcphdr *tcp)
{
uint8_t *opt;
// new style RFC7413
opt = tcp_find_option((struct tcphdr*)tcp, TCP_KIND_FASTOPEN);
if (opt) return true;
// old style RFC6994
opt = tcp_find_option((struct tcphdr*)tcp, 254);
return opt && opt[1]>=4 && opt[2]==0xF9 && opt[3]==0x89;
}
uint16_t tcp_find_mss(const struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option((struct tcphdr *)tcp, TCP_KIND_MSS);
return (t && t[1]==4) ? *(uint16_t*)(t+2) : 0;
}
bool tcp_has_sack(struct tcphdr *tcp)
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
uint8_t *t = tcp_find_option(tcp, TCP_KIND_SACK_PERM);
return !!t;
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN));
}
bool tcp_syn_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN);
}
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport)
{
@@ -549,56 +551,6 @@ void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis)
}
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == (TH_ACK|TH_SYN));
}
bool tcp_syn_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_SYN);
}
bool tcp_ack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return ((tcphdr->th_flags & (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN)) == TH_ACK);
}
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor)
{
uint8_t *scale,scale_factor_old;
if (scale_factor!=SCALE_NONE)
{
scale = tcp_find_option(tcp,3); // tcp option 3 - scale factor
if (scale && scale[1]==3) // length should be 3
{
scale_factor_old=scale[2];
// do not allow increasing scale factor
if (scale_factor>=scale_factor_old)
DLOG("Scale factor %u unchanged\n", scale_factor_old);
else
{
scale[2]=scale_factor;
DLOG("Scale factor change %u => %u\n", scale_factor_old, scale_factor);
}
}
}
}
// scale_factor=SCALE_NONE - do not change
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor)
{
uint16_t winsize_old;
winsize_old = htons(tcp->th_win); // << scale_factor;
tcp->th_win = htons(winsize);
DLOG("Window size change %u => %u\n", winsize_old, winsize);
tcp_rewrite_wscale(tcp, scale_factor);
}
uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6)
{
return ip ? ip->ip_ttl : ip6 ? ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim : 0;
@@ -607,11 +559,204 @@ uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6)
#ifdef __CYGWIN__
uint32_t w_win32_error=0;
static BOOL RemoveTokenPrivs(void)
{
BOOL bRes = FALSE;
HANDLE hToken;
TOKEN_PRIVILEGES *privs;
DWORD k, dwSize;
LUID luid_SeChangeNotifyPrivilege;
if (LookupPrivilegeValue(NULL, SE_CHANGE_NOTIFY_NAME, &luid_SeChangeNotifyPrivilege))
{
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_PRIVILEGES, &hToken))
{
if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
if (privs = (PTOKEN_PRIVILEGES)malloc(dwSize))
{
if (GetTokenInformation(hToken, TokenPrivileges, privs, dwSize, &dwSize))
{
for (k = 0; k < privs->PrivilegeCount; k++)
{
if (memcmp(&privs->Privileges[k].Luid, &luid_SeChangeNotifyPrivilege, sizeof(LUID)))
privs->Privileges[k].Attributes = SE_PRIVILEGE_REMOVED;
}
}
bRes = AdjustTokenPrivileges(hToken, FALSE, privs, dwSize, NULL, NULL);
free(privs);
}
}
CloseHandle(hToken);
}
}
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
static SID_IDENTIFIER_AUTHORITY label_authority = SECURITY_MANDATORY_LABEL_AUTHORITY;
BOOL LowMandatoryLevel(void)
{
BOOL bRes = FALSE;
HANDLE hToken;
char buf1[32];
TOKEN_MANDATORY_LABEL label_low;
label_low.Label.Sid = (PSID)buf1;
InitializeSid(label_low.Label.Sid, &label_authority, 1);
label_low.Label.Attributes = 0;
*GetSidSubAuthority(label_low.Label.Sid, 0) = SECURITY_MANDATORY_LOW_RID;
// S-1-16-12288 : Mandatory Label\High Mandatory Level
// S-1-16-8192 : Mandatory Label\Medium Mandatory Level
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, &hToken))
{
bRes = SetTokenInformation(hToken, TokenIntegrityLevel, &label_low, sizeof(label_low));
CloseHandle(hToken);
}
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
BOOL SetMandatoryLabelFile(LPCSTR lpFileName, DWORD dwMandatoryLabelRID, DWORD dwAceFlags)
{
BOOL bRes=FALSE;
DWORD dwErr, dwFileAttributes;
char buf_label[16], buf_pacl[32];
PSID label = (PSID)buf_label;
PACL pacl = (PACL)buf_pacl;
LPWSTR lpFileNameW = NULL;
size_t szFileName;
szFileName = strlen(lpFileName);
if (!(lpFileNameW = (LPWSTR)LocalAlloc(LMEM_FIXED,(szFileName+1)*sizeof(WCHAR))))
goto err;
if (!MultiByteToWideChar(CP_UTF8, 0, lpFileName, -1, lpFileNameW, szFileName+1))
goto err;
if (!strncmp(lpFileName,"\\\\.\\",4))
dwFileAttributes = 0;
else
{
dwFileAttributes = GetFileAttributesW(lpFileNameW);
if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) goto err;
}
InitializeSid(label, &label_authority, 1);
*GetSidSubAuthority(label, 0) = dwMandatoryLabelRID;
if (InitializeAcl(pacl, sizeof(buf_pacl), ACL_REVISION) && AddMandatoryAce(pacl, (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? ACL_REVISION_DS : ACL_REVISION, dwAceFlags, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, label))
{
dwErr = SetNamedSecurityInfoW(lpFileNameW, SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pacl);
SetLastError(dwErr);
bRes = dwErr==ERROR_SUCCESS;
}
err:
if (!bRes) w_win32_error = GetLastError();
LocalFree(lpFileNameW);
return bRes;
}
BOOL SetMandatoryLabelFileW(LPCWSTR lpFileNameW, DWORD dwMandatoryLabelRID, DWORD dwAceFlags)
{
BOOL bRes=FALSE;
DWORD dwErr, dwFileAttributes;
char buf_label[16], buf_pacl[32];
PSID label = (PSID)buf_label;
PACL pacl = (PACL)buf_pacl;
if (!wcsncmp(lpFileNameW,L"\\\\.\\",4))
dwFileAttributes = 0;
else
{
dwFileAttributes = GetFileAttributesW(lpFileNameW);
if (dwFileAttributes == INVALID_FILE_ATTRIBUTES) goto err;
}
InitializeSid(label, &label_authority, 1);
*GetSidSubAuthority(label, 0) = dwMandatoryLabelRID;
if (InitializeAcl(pacl, sizeof(buf_pacl), ACL_REVISION) && AddMandatoryAce(pacl, (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? ACL_REVISION_DS : ACL_REVISION, dwAceFlags, SYSTEM_MANDATORY_LABEL_NO_WRITE_UP, label))
{
dwErr = SetNamedSecurityInfoW((LPWSTR)lpFileNameW, SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pacl);
SetLastError(dwErr);
bRes = dwErr==ERROR_SUCCESS;
}
err:
if (!bRes) w_win32_error = GetLastError();
return bRes;
}
bool ensure_file_access(const char *filename)
{
return SetMandatoryLabelFile(filename, SECURITY_MANDATORY_LOW_RID, 0);
}
static bool prepare_low_appdata()
{
bool b = false;
PWSTR pszPath = NULL;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow, 0, NULL, &pszPath);
if (SUCCEEDED(hr))
{
size_t l = cygwin_conv_path(CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, pszPath, NULL, 0);
char *buf = (char*)malloc(l+8);
if (buf)
{
if (!cygwin_conv_path(CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, pszPath, buf, l))
{
b = true;
setenv("APPDATALOW", buf, 1);
memcpy(buf+l-1,"/zapret2",9);
setenv("WRITEABLE", buf, 1);
mkdir(buf,0755);
l = wcslen(pszPath);
PWSTR pszPath2 = malloc((l+9)*sizeof(WCHAR));
if (pszPath2)
{
memcpy(pszPath2,pszPath,l*sizeof(WCHAR));
memcpy(pszPath2+l,L"\\zapret2",9*sizeof(WCHAR));
// ensure it's low and everything created inside is also low
SetMandatoryLabelFileW(pszPath2, SECURITY_MANDATORY_LOW_RID, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
free(pszPath2);
}
}
free(buf);
}
CoTaskMemFree(pszPath);
}
return b;
}
#define WINDIVERT_DEVICE_NAME "WinDivert"
static bool b_isandbox_set = false;
bool win_sandbox(void)
{
// there's no way to return privs
if (!b_isandbox_set)
{
if (!RemoveTokenPrivs())
return FALSE;
// set low mandatory label on windivert device to allow administrators with low label access the driver
if (logical_net_filter_present() && !SetMandatoryLabelFile("\\\\.\\" WINDIVERT_DEVICE_NAME, SECURITY_MANDATORY_LOW_RID, 0))
return FALSE;
prepare_low_appdata();
if (!LowMandatoryLevel())
return false;
// for LUA code to find where to store files
b_isandbox_set = true;
}
return true;
}
static HANDLE w_filter = NULL;
static OVERLAPPED ovl = { .hEvent = NULL };
static const struct str_list_head *wlan_filter_ssid = NULL, *nlm_filter_net = NULL;
static DWORD logical_net_filter_tick=0;
uint32_t w_win32_error=0;
INetworkListManager* pNetworkListManager=NULL;
static void guid2str(const GUID *guid, char *str)
@@ -699,6 +844,7 @@ bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_lis
win_dark_deinit();
if (LIST_EMPTY(ssid_filter)) ssid_filter=NULL;
if (LIST_EMPTY(nlm_filter)) nlm_filter=NULL;
if (nlm_filter)
{
if (SUCCEEDED(w_win32_error = CoInitialize(NULL)))
@@ -956,6 +1102,13 @@ bool logical_net_filter_match(void)
return wlan_filter_match(wlan_filter_ssid) && nlm_filter_match(nlm_filter_net);
}
bool logical_net_filter_present(void)
{
return (wlan_filter_ssid && !LIST_EMPTY(wlan_filter_ssid)) || (nlm_filter_net && !LIST_EMPTY(nlm_filter_net));
}
static bool logical_net_filter_match_rate_limited(void)
{
DWORD dwTick = GetTickCount() / 1000;
@@ -1045,14 +1198,17 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
return false;
}
usleep(0);
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, NULL, &ovl))
{
*len = recv_len;
return true;
}
for(;;)
{
w_win32_error = GetLastError();
switch(w_win32_error)
{
case ERROR_IO_PENDING:
@@ -1131,6 +1287,11 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const
#else // *nix
bool ensure_file_access(const char *filename)
{
return !chown(filename, params.uid, -1);
}
static int rawsend_sock4=-1, rawsend_sock6=-1;
static bool b_bind_fix4=false, b_bind_fix6=false;
static void rawsend_clean_sock(int *sock)

View File

@@ -83,17 +83,21 @@ uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
uint16_t tcp_find_mss(const struct tcphdr *tcp);
bool tcp_has_sack(struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
bool ip_has_df(const struct ip *ip);
bool ensure_file_access(const char *filename);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
bool win_sandbox(void);
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter);
bool win_dark_deinit(void);
bool logical_net_filter_present(void);
bool logical_net_filter_match(void);
bool nlm_list(bool bAll);
bool windivert_init(const char *filter);
@@ -156,13 +160,6 @@ struct dissect
};
void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
bool tcp_ack_segment(const struct tcphdr *tcphdr);
// scale_factor=SCALE_NONE - do not change
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor);
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor);
uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, const struct ip *ip, const struct ip6_hdr *ip6hdr);

View File

@@ -1162,7 +1162,6 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
}
process_retrans_fail(ctrack, IPPROTO_TCP, (struct sockaddr*)&src);
if (IsHttp(rdata_payload, rlen_payload))
{
DLOG("packet contains HTTP request\n");
@@ -1202,7 +1201,7 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_TLS;
if (ctrack) ctrack->l7proto = l7proto;
if (ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
if (bReqFull) TLSDebug(rdata_payload, rlen_payload);
@@ -1253,6 +1252,17 @@ static uint8_t dpi_desync_tcp_packet_play(unsigned int replay_piece, unsigned in
}
}
}
else if (ctrack && (ctrack->seq_last - ctrack->seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
{
DLOG("packet contains telegram mtproto2 initial\n");
// mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable.
l7payload = L7P_MTPROTO_INITIAL;
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_MTPROTO;
if (ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
}
else
{
t_protocol_probe testers[] = {

View File

@@ -417,26 +417,9 @@ bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size)
}
return true;
}
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset)
char hex_digit(uint8_t v)
{
size_t size;
if (offset%=patsize)
{
size = patsize-offset;
size = bufsize>size ? size : bufsize;
memcpy(buf,pattern+offset,size);
buf += size;
bufsize -= size;
}
while (bufsize)
{
size = bufsize>patsize ? patsize : bufsize;
memcpy(buf,pattern,size);
buf += size;
bufsize -= size;
}
return v<=9 ? '0'+v : (v<=0xF) ? v+'A'-0xA : '?';
}
int fprint_localtime(FILE *F)

View File

@@ -65,7 +65,7 @@ uint64_t pntoh64(const uint8_t *p);
void phton64(uint8_t *p, uint64_t v);
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size);
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset);
char hex_digit(uint8_t v);
int fprint_localtime(FILE *F);

View File

@@ -1,8 +1,14 @@
#include <time.h>
#include <fcntl.h>
#include "lua.h"
#include "params.h"
#include "helpers.h"
#include "conntrack.h"
#include "crypto/sha.h"
#include "crypto/aes-gcm.h"
#include "crypto/aes-ctr.h"
static void lua_check_argc(lua_State *L, const char *where, int argc)
{
@@ -442,7 +448,6 @@ static int luacall_aes_gcm(lua_State *L)
uint8_t *output = malloc(input_len);
if (!output) luaL_error(L, "out of memory");
gcm_initialize();
if (aes_gcm_crypt(bEncrypt, output, input, input_len, key, key_len, iv, iv_len, add, add_len, atag, sizeof(atag)))
{
lua_pushnil(L);
@@ -458,6 +463,38 @@ static int luacall_aes_gcm(lua_State *L)
LUA_STACK_GUARD_RETURN(L,2)
}
static int luacall_aes_ctr(lua_State *L)
{
// aes_ctr(key, iv, in) returns out
lua_check_argc(L,"aes_ctr",3);
LUA_STACK_GUARD_ENTER(L)
size_t key_len;
const uint8_t *key = (uint8_t*)luaL_checklstring(L,1,&key_len);
if (key_len!=16 && key_len!=24 && key_len!=32)
luaL_error(L, "aes_ctr: wrong key length %u. should be 16,24,32.", (unsigned)key_len);
size_t iv_len;
const uint8_t *iv = (uint8_t*)luaL_checklstring(L,2,&iv_len);
if (iv_len!=16)
luaL_error(L, "aes_ctr: wrong iv length %u. should be 16.", (unsigned)iv_len);
size_t input_len;
const uint8_t *input = (uint8_t*)luaL_checklstring(L,3,&input_len);
uint8_t *output = malloc(input_len);
if (!output) luaL_error(L, "out of memory");
if (aes_ctr_crypt(key, key_len, iv, input, input_len, output))
lua_pushnil(L);
else
lua_pushlstring(L,(const char*)output,input_len);
free(output);
LUA_STACK_GUARD_RETURN(L,1)
}
static int luacall_hkdf(lua_State *L)
{
// hkdf(hash_type, salt, ikm, info, okm_len) returns okm
@@ -2209,6 +2246,8 @@ void lua_shutdown()
if (params.L)
{
DLOG("LUA SHUTDOWN\n");
// conntrack holds lua state. must clear it before lua shoudown
ConntrackPoolDestroy(&params.conntrack);
lua_close(params.L);
params.L=NULL;
}
@@ -2293,6 +2332,20 @@ static bool lua_desync_functions_exist()
return true;
}
bool lua_test_init_script_files(void)
{
struct str_list *str;
LIST_FOREACH(str, &params.lua_init_scripts, next)
{
if (str->str[0]=='@' && !file_open_test(str->str+1, O_RDONLY))
{
DLOG_ERR("LUA file '%s' not accessible\n",str->str+1);
return false;
}
}
return true;
}
static bool lua_init_scripts(void)
{
struct str_list *str;
@@ -2322,27 +2375,39 @@ static bool lua_init_scripts(void)
static void lua_sec_harden(void)
{
LUA_STACK_GUARD_ENTER(params.L)
// remove unwanted functions. lua scripts are not intended to execute files
const struct
{
const char *global, *func;
const char *global, *field, *field2;
} bad[] = {
{"os","execute"},
{"io","popen"},
{"package","loadlib"},
{"debug", NULL}
{"os","execute",NULL},
{"io","popen",NULL},
{"package","loadlib",NULL},
{"debug", NULL, NULL},
{"package", "loaded", "debug"}
};
DLOG("LUA REMOVE:");
for (int i=0;i<sizeof(bad)/sizeof(*bad);i++)
{
if (bad[i].func)
if (bad[i].field)
{
lua_getglobal(params.L, bad[i].global);
lua_pushstring(params.L, bad[i].func);
if (bad[i].field2)
{
lua_getfield(params.L, -1, bad[i].field);
lua_pushstring(params.L, bad[i].field2);
DLOG(" %s.%s.%s", bad[i].global, bad[i].field, bad[i].field2);
}
else
{
lua_pushstring(params.L, bad[i].field);
DLOG(" %s.%s", bad[i].global, bad[i].field);
}
lua_pushnil(params.L);
lua_rawset(params.L, -3);
lua_pop(params.L,1);
DLOG(" %s.%s", bad[i].global, bad[i].func);
lua_pop(params.L,1 + !!bad[i].field2);
}
else
{
@@ -2352,10 +2417,14 @@ static void lua_sec_harden(void)
}
}
DLOG("\n");
LUA_STACK_GUARD_LEAVE(params.L,0)
}
static void lua_init_blobs(void)
{
LUA_STACK_GUARD_ENTER(params.L)
struct blob_item *blob;
// save some memory - destroy C blobs as they are not needed anymore
while ((blob = LIST_FIRST(&params.blobs)))
@@ -2366,10 +2435,14 @@ static void lua_init_blobs(void)
lua_setglobal(params.L, blob->name);
blob_destroy(blob);
}
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
static void lua_init_const(void)
{
LUA_STACK_GUARD_ENTER(params.L)
const struct
{
const char *name;
@@ -2470,10 +2543,14 @@ static void lua_init_const(void)
}
DLOG("\n");
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
static void lua_init_functions(void)
{
LUA_STACK_GUARD_ENTER(params.L)
const struct
{
const char *name;
@@ -2528,6 +2605,7 @@ static void lua_init_functions(void)
{"hash",luacall_hash},
{"aes",luacall_aes},
{"aes_gcm",luacall_aes_gcm},
{"aes_ctr",luacall_aes_ctr},
{"hkdf",luacall_hkdf},
// parsing
@@ -2575,6 +2653,8 @@ static void lua_init_functions(void)
};
for(int i=0;i<(sizeof(lfunc)/sizeof(*lfunc));i++)
lua_register(params.L,lfunc[i].name,lfunc[i].f);
LUA_STACK_GUARD_LEAVE(params.L, 0)
}
bool lua_init(void)

View File

@@ -28,6 +28,7 @@
#define LUA_STACK_GUARD_RETURN(L,N) LUA_STACK_GUARD_LEAVE(L,N); return N;
bool lua_test_init_script_files(void);
bool lua_init(void);
void lua_shutdown(void);
void lua_dlog_error(void);

View File

@@ -303,6 +303,8 @@ static int nfq_main(void)
print_id();
if (params.droproot && !test_list_files())
goto err;
if (!lua_test_init_script_files())
goto err;
sec_harden();
@@ -461,6 +463,8 @@ static int dvt_main(void)
print_id();
if (params.droproot && !test_list_files())
goto exiterr;
if (!lua_test_init_script_files())
goto exiterr;
if (!lua_init())
goto exiterr;
@@ -601,11 +605,6 @@ static int win_main()
return ERROR_TOO_MANY_OPEN_FILES; // code 4 = The system cannot open the file
}
if (!lua_init())
{
res=ERROR_INVALID_PARAMETER; goto ex;
}
if (!win_dark_init(&params.ssid_filter, &params.nlm_filter))
{
DLOG_ERR("win_dark_init failed. win32 error %u (0x%08X)\n", w_win32_error, w_win32_error);
@@ -635,6 +634,19 @@ static int win_main()
{
res=w_win32_error; goto ex;
}
if (!win_sandbox())
{
res=w_win32_error;
DLOG_ERR("Cannot init Windows sandbox\n");
goto ex;
}
// init LUA only here because of possible sandbox. no LUA code with high privs
if (!params.L && !lua_init())
{
res=ERROR_INVALID_PARAMETER; goto ex;
}
DLOG_CONDUP("windivert initialized. capture is started.\n");
@@ -2399,11 +2411,14 @@ int main(int argc, char **argv)
DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n", desync_profile_count);
#ifndef __CYGWIN__
if (params.debug_target == LOG_TARGET_FILE && params.droproot && chown(params.debug_logfile, params.uid, -1))
fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile);
if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
if (params.droproot)
#endif
{
if (params.debug_target == LOG_TARGET_FILE && !ensure_file_access(params.debug_logfile))
DLOG_ERR("could not make '%s' accessible. log file may not be writable after privilege drop\n", params.debug_logfile);
if (*params.hostlist_auto_debuglog && !ensure_file_access(params.hostlist_auto_debuglog))
DLOG_ERR("could not make '%s' accessible. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
}
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
dp = &dpl->dp;
@@ -2415,14 +2430,20 @@ int main(int argc, char **argv)
}
#ifndef __CYGWIN__
if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
if (params.droproot)
#endif
{
if (dp->hostlist_auto && ensure_file_access(dp->hostlist_auto->filename))
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
}
LuaDesyncDebug(dp);
}
if (!test_list_files())
exit_clean(1);
if (!lua_test_init_script_files())
exit_clean(1);
if (!LoadAllHostLists())
{

View File

@@ -88,21 +88,21 @@ const uint8_t fake_tls_clienthello_default[680] = {
const char * tld[6] = { "com","org","net","edu","gov","biz" };
int DLOG_FILE(FILE *F, const char *format, va_list args)
int DLOG_FILE_VA(FILE *F, const char *format, va_list args)
{
return vfprintf(F, format, args);
}
int DLOG_CON(const char *format, int syslog_priority, va_list args)
int DLOG_CON_VA(const char *format, int syslog_priority, va_list args)
{
return DLOG_FILE(syslog_priority==LOG_ERR ? stderr : stdout, format, args);
return DLOG_FILE_VA(syslog_priority==LOG_ERR ? stderr : stdout, format, args);
}
int DLOG_FILENAME(const char *filename, const char *format, va_list args)
int DLOG_FILENAME_VA(const char *filename, const char *format, va_list args)
{
int r;
FILE *F = fopen(filename,"at");
if (F)
{
r = DLOG_FILE(F, format, args);
r = DLOG_FILE_VA(F, format, args);
fclose(F);
}
else
@@ -118,6 +118,21 @@ static void syslog_log_function(int priority, const char *line)
{
syslog(priority,"%s",log_buf);
}
static int DLOG_FILENAME(const char *filename, const char *format, ...)
{
int r;
va_list args;
va_start(args, format);
r = DLOG_FILENAME_VA(filename, format, args);
va_end(args);
return r;
}
static void file_log_function(int priority, const char *line)
{
DLOG_FILENAME(params.debug_logfile,"%s",log_buf);
}
#ifdef __ANDROID__
static enum android_LogPriority syslog_priority_to_android(int priority)
{
@@ -163,7 +178,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
if (condup && !(params.debug && params.debug_target==LOG_TARGET_CONSOLE))
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
DLOG_CON_VA(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug)
@@ -171,10 +186,11 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
switch(params.debug_target)
{
case LOG_TARGET_CONSOLE:
r = DLOG_CON(format,syslog_priority,args);
r = DLOG_CON_VA(format,syslog_priority,args);
break;
case LOG_TARGET_FILE:
r = DLOG_FILENAME(params.debug_logfile,format,args);
log_buffered(file_log_function,syslog_priority,format,args);
r = 1;
break;
case LOG_TARGET_SYSLOG:
// skip newlines
@@ -271,10 +287,39 @@ void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
bcut = true;
}
if (!size) return;
for (k = 0; k < size; k++) DLOG("%02X ", data[k]);
DLOG(bcut ? "... : " : ": ");
for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.');
if (bcut) DLOG(" ...");
char *p, *buf = malloc(size*4 + 16);
if (buf)
{
p=buf;
for (k = 0; k < size; k++)
{
*p++ = hex_digit(data[k] >> 4);
*p++ = hex_digit(data[k] & 0xF);
*p++ = ' ';
}
if (bcut)
{
*p++='.';
*p++='.';
*p++='.';
*p++=' ';
}
*p++=':';
*p++=' ';
for (k = 0; k < size; k++)
*p++ = data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.';
if (bcut)
{
*p++=' ';
*p++='.';
*p++='.';
*p++='.';
}
*p = 0;
DLOG("%s", buf);
free(buf);
}
}
void dp_init(struct desync_profile *dp)
@@ -381,9 +426,7 @@ void cleanup_params(struct params_s *params)
#endif
ConntrackPoolDestroy(&params->conntrack);
dp_list_destroy(&params->desync_profiles);
hostlist_files_destroy(&params->hostlists);
ipset_files_destroy(&params->ipsets);
ipcacheDestroy(&params->ipcache);

View File

@@ -3,6 +3,9 @@
#include "protocol.h"
#include "helpers.h"
#include "params.h"
#include "crypto/sha.h"
#include "crypto/aes-gcm.h"
#include "crypto/aes-ctr.h"
#include <string.h>
#include <ctype.h>
@@ -26,7 +29,7 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t **
return true;
}
static const char *l7proto_name[] = {"all","unknown","http","tls","quic","wireguard","dht","discord","stun","xmpp","dns"};
static const char *l7proto_name[] = {"all","unknown","known","http","tls","quic","wireguard","dht","discord","stun","xmpp","dns","mtproto"};
const char *l7proto_str(t_l7proto l7)
{
if (l7>=L7_LAST) return NULL;
@@ -39,14 +42,16 @@ t_l7proto l7proto_from_name(const char *name)
}
bool l7_proto_match(t_l7proto l7proto, uint64_t filter_l7)
{
return !filter_l7 || (filter_l7 & (1<<l7proto));
return filter_l7==L7_ALL || (filter_l7 & (1<<l7proto)) || (filter_l7 & (1<<L7_KNOWN)) && l7proto>L7_KNOWN && l7proto<L7_LAST;
}
static const char *l7payload_name[] = {
"all","unknown","empty","http_req","http_reply","tls_client_hello","tls_server_hello","quic_initial",
"all","unknown","empty","known","http_req","http_reply","tls_client_hello","tls_server_hello","quic_initial",
"wireguard_initiation","wireguard_response","wireguard_cookie","wireguard_keepalive","wireguard_data",
"dht","discord_ip_discovery","stun_binding_req",
"xmpp_stream", "xmpp_starttls", "xmpp_proceed", "xmpp_features", "dns_query", "dns_response"};
"xmpp_stream", "xmpp_starttls", "xmpp_proceed", "xmpp_features",
"dns_query", "dns_response",
"mtproto_initial"};
t_l7payload l7payload_from_name(const char *name)
{
int idx = str_index(l7payload_name,sizeof(l7payload_name)/sizeof(*l7payload_name),name);
@@ -59,7 +64,7 @@ const char *l7payload_str(t_l7payload l7)
}
bool l7_payload_match(t_l7payload l7payload, uint64_t filter_l7p)
{
return !filter_l7p || (filter_l7p & (1<<l7payload));
return filter_l7p==L7P_ALL || (filter_l7p & (1<<l7payload)) || (filter_l7p & (1<<L7P_KNOWN)) && l7payload>L7P_KNOWN && l7payload<L7P_LAST;
}
@@ -720,7 +725,7 @@ bool TLSMod(const struct fake_tls_mod *tls_mod, const uint8_t *payload, size_t p
DLOG_ERR("cannot apply tls mod.tls structure invalid\n");
return false;
}
DLOG_ERR("tls extensions length offset : %zu\n", extlen_offset);
DLOG("tls extensions length offset : %zu\n", extlen_offset);
}
if (tls_mod->mod & (FAKE_TLS_MOD_RND_SNI | FAKE_TLS_MOD_SNI))
{
@@ -1146,7 +1151,7 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
pn_offset += tvb_get_varint(data + pn_offset, &payload_len);
if (payload_len<20 || (pn_offset + payload_len)>data_len) return false;
gcm_initialize(); // initialize aes keygen tables
aes_init_keygen_tables();
uint8_t sample_enc[16];
aes_context ctx;
@@ -1385,3 +1390,13 @@ bool IsStunBindingRequest(const uint8_t *data, size_t len)
ntohl(*(uint32_t*)(&data[4]))==0x2112A442 && // magic cookie
ntohs(*(uint16_t*)(&data[2]))==len-20;
}
bool IsMTProto(const uint8_t *data, size_t len)
{
if (len>=64)
{
uint8_t decrypt[64];
aes_ctr_crypt(data+8, 32, data+40, data, 64, decrypt);
return !memcmp(decrypt+56,"\xEF\xEF\xEF\xEF",4);
}
return false;
}

View File

@@ -3,13 +3,12 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "crypto/sha.h"
#include "crypto/aes-gcm.h"
#include "helpers.h"
typedef enum {
L7_ALL=0,
L7_UNKNOWN,
L7_KNOWN,
L7_HTTP,
L7_TLS,
L7_QUIC,
@@ -19,6 +18,7 @@ typedef enum {
L7_STUN,
L7_XMPP,
L7_DNS,
L7_MTPROTO,
L7_LAST, L7_INVALID=L7_LAST
} t_l7proto;
const char *l7proto_str(t_l7proto l7);
@@ -29,6 +29,7 @@ typedef enum {
L7P_ALL=0,
L7P_UNKNOWN,
L7P_EMPTY,
L7P_KNOWN,
L7P_HTTP_REQ,
L7P_HTTP_REPLY,
L7P_TLS_CLIENT_HELLO,
@@ -48,6 +49,7 @@ typedef enum {
L7P_XMPP_FEATURES,
L7P_DNS_QUERY,
L7P_DNS_RESPONSE,
L7P_MTPROTO_INITIAL,
L7P_LAST, L7P_INVALID=L7P_LAST
} t_l7payload;
t_l7payload l7payload_from_name(const char *name);
@@ -151,6 +153,7 @@ bool IsWireguardData(const uint8_t *data, size_t len);
bool IsDht(const uint8_t *data, size_t len);
bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len);
bool IsStunBindingRequest(const uint8_t *data, size_t len);
bool IsMTProto(const uint8_t *data, size_t len);
#define QUIC_MAX_CID_LENGTH 20
typedef struct quic_cid {