diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index 04de08b..90aaf2a 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 05c96c7..182bb72 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/freebsd-x64/dvtws b/binaries/freebsd-x64/dvtws index 3a27572..5e4cfe7 100755 Binary files a/binaries/freebsd-x64/dvtws and b/binaries/freebsd-x64/dvtws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index 6aad964..e9c13a2 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index f1b7c23..c4d1d1a 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index 0301675..a150a4c 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index fa648a0..8cc0de8 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index 4eb25eb..74240f9 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index 609f3fa..d711589 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/blockcheck.sh b/blockcheck.sh index f31dfe3..f533fc6 100755 --- a/blockcheck.sh +++ b/blockcheck.sh @@ -575,7 +575,6 @@ pktws_check_domain_bypass() pktws_curl_test_update $1 $3 --dpi-desync=$desync $e done } - [ "$IPV" = 6 ] && pktws_curl_test_update $1 $3 --dpi-desync=hopbyhop $e for desync in $tests; do s="--dpi-desync=$desync" for ttl in $ttls; do @@ -588,13 +587,22 @@ pktws_check_domain_bypass() echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.' done done + [ "$IPV" = 6 ] && { + for desync in hopbyhop hopbyhop,split2 hopbyhop,disorder2; do + pktws_curl_test_update $1 $3 --dpi-desync=$desync $e + done + } # do not do wssize test for http. it's useless [ "$sec" = 1 ] || break done [ "$IPV" = 4 -o -n "$IP6_DEFRAG_DISABLE" ] && { for frag in 24 32 40 64 80 104; do - pktws_curl_test_update $1 $3 --dpi-desync=ipfrag2 --dpi-desync-ipfrag-pos-tcp=$frag + tests="ipfrag2" + [ "$IPV" = 6 ] && tests="$tests hopbyhop,ipfrag2" + for desync in $tests; do + pktws_curl_test_update $1 $3 --dpi-desync=$desync --dpi-desync-ipfrag-pos-tcp=$frag + done done } diff --git a/docs/readme.eng.md b/docs/readme.eng.md index 83774bb..9595e58 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -245,7 +245,9 @@ Extra header increases packet size and can't be applied to the maximum size pack If it's not possible to send modified packet original one will be sent. The idea here is that DPI sees 0 in the next header field of the main ipv6 header and does not walk through the extension header chain until transport header is found. -`hopbyhop` mode cannot be used with second phase modes. +`hopbyhop` mode can be used with any second phase mode. +For example, `hopbyhop,split2` means split original tcp packet into 2 pieces and add hop-by-hop header to both. +With `hopbyhop,ipfrag2` header sequence will be : `ipv6,hop-by-hop,fragment,tcp/udp`. There are DPIs that analyze responses from the server, particularly the certificate from the ServerHello that contain domain name(s). The ClientHello delivery confirmation is an ACK packet from the server diff --git a/docs/readme.txt b/docs/readme.txt index a223b73..b0c2470 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -301,7 +301,9 @@ disorder2 и split2 не предполагают отсылку фейк пак Расчет идет на то, что DPI увидит 0 в поле next header основного заголовка ipv6 и не будет скакать по extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет без анализа. Возможно, какие-то DPI на это купятся. -hopbyhop исключает применение режимов 2-й фазы. +Может сочетаться с любыми режимами 2-й фазы. +Например, "hopbyhop,split2" означает разбить tcp пакет на 2 сегмента, в каждый из них добавить hop-by-hop. +При "hopbyhop,ipfrag2" последовательность хедеров будет : ipv6,hop-by-hop,fragment,tcp/udp. Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены. Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1. diff --git a/nfq/darkmagic.c b/nfq/darkmagic.c index 7e58fb6..a7f6be9 100644 --- a/nfq/darkmagic.c +++ b/nfq/darkmagic.c @@ -16,6 +16,10 @@ uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment) { return htonl(ntohl(netorder_value)+cpuorder_increment); } +uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment) +{ + return htons(ntohs(netorder_value)+cpuorder_increment); +} uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind) { @@ -346,7 +350,23 @@ bool prepare_udp_segment( false; } - +bool ip6_insert_hopbyhop(uint8_t *data_pkt, size_t len_pkt, uint8_t *buf, size_t *buflen) +{ + if ((len_pkt+8)<=*buflen && len_pkt>=sizeof(struct ip6_hdr)) + { + struct ip6_hdr *ip6 = (struct ip6_hdr *)buf; + struct ip6_hbh *hbh = (struct ip6_hbh*)(ip6+1); + *ip6 = *(struct ip6_hdr*)data_pkt; + memset(hbh,0,8); + memcpy((uint8_t*)hbh+8, data_pkt+sizeof(struct ip6_hdr), len_pkt-sizeof(struct ip6_hdr)); + hbh->ip6h_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; + ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = 0; + ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = net16_add(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen, 8); + *buflen = len_pkt + 8; + return true; + } + return false; +} // split ipv4 packet into 2 fragments at data payload position frag_pos bool ip_frag4( diff --git a/nfq/darkmagic.h b/nfq/darkmagic.h index 8ed768b..abb068a 100644 --- a/nfq/darkmagic.h +++ b/nfq/darkmagic.h @@ -13,6 +13,7 @@ // returns netorder value uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment); +uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment); #define FOOL_NONE 0x00 #define FOOL_MD5SIG 0x01 @@ -85,6 +86,7 @@ bool prepare_udp_segment( const void *data, uint16_t len, uint8_t *buf, size_t *buflen); +bool ip6_insert_hopbyhop(uint8_t *data_pkt, size_t len_pkt, uint8_t *buf, size_t *buflen); // ipv4: ident==-1 - copy ip_id from original ipv4 packet bool ip_frag4( diff --git a/nfq/desync.c b/nfq/desync.c index ad59089..f8bebf4 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -51,7 +51,7 @@ const uint8_t fake_tls_clienthello_default[517] = { }; #define PKTDATA_MAXDUMP 32 -#define IP_MAXDUMP 64 +#define IP_MAXDUMP 80 static uint8_t zeropkt[DPI_DESYNC_MAX_FAKE_LEN]; @@ -71,7 +71,7 @@ bool desync_valid_first_stage(enum dpi_desync_mode mode) } bool desync_only_first_stage(enum dpi_desync_mode mode) { - return mode==DESYNC_HOPBYHOP; + return false; } bool desync_valid_second_stage(enum dpi_desync_mode mode) { @@ -356,6 +356,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s } enum dpi_desync_mode desync_mode = params.desync_mode; + uint8_t fooling_orig = FOOL_NONE; bool b; pkt1_len = sizeof(pkt1); @@ -385,7 +386,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s b = true; break; case DESYNC_HOPBYHOP: - if (ip6hdr) + if (ip6hdr && params.desync_mode2==DESYNC_NONE) { if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, ttl_orig,FOOL_HOPBYHOP,0,0, @@ -399,7 +400,8 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s // this mode is final, no other options available return drop; } - return res; + fooling_orig = FOOL_HOPBYHOP; + desync_mode = params.desync_mode2; } if (b) @@ -444,7 +446,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s if (split_posth_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload+split_pos, len_payload-split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) @@ -470,7 +472,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload, split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -510,7 +512,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload, split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos) @@ -530,7 +532,7 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s { pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,FOOL_NONE,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, data_payload+split_pos, len_payload-split_pos, pkt1, &pkt1_len)) return res; DLOG("sending 2nd tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos) @@ -546,9 +548,15 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s { #ifdef __FreeBSD__ // FreeBSD tend to pass ipv6 frames with wrong checksum - if (ip6hdr) - tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr); + if (res==modify || ip6hdr) + #else + // if original packet was tampered earlier it needs checksum fixed + if (res==modify) #endif + tcp_fix_checksum(tcphdr,len_tcp,ip,ip6hdr); + + uint8_t pkt3[DPI_DESYNC_MAX_FAKE_LEN+100], *pkt_orig; + size_t pkt_orig_len; size_t ipfrag_pos = (params.desync_ipfrag_pos_tcp && params.desync_ipfrag_pos_tcpip_id ? ip->ip_id : htons(1+random()%0xFFFF) : htonl(1+random()&0xFFFFFFFF); @@ -556,7 +564,20 @@ packet_process_result dpi_desync_tcp_packet(uint8_t *data_pkt, size_t len_pkt, s pkt1_len = sizeof(pkt1); pkt2_len = sizeof(pkt2); - if (!ip_frag(data_pkt, len_pkt, ipfrag_pos, ident, pkt1, &pkt1_len, pkt2, &pkt2_len)) + if (ip6hdr && fooling_orig==FOOL_HOPBYHOP) + { + pkt_orig_len = sizeof(pkt3); + if (!ip6_insert_hopbyhop(data_pkt, len_pkt, pkt3, &pkt_orig_len)) + return res; + pkt_orig = pkt3; + } + else + { + pkt_orig = data_pkt; + pkt_orig_len = len_pkt; + } + + if (!ip_frag(pkt_orig, pkt_orig_len, ipfrag_pos, ident, pkt1, &pkt1_len, pkt2, &pkt2_len)) return res; DLOG("sending 1st ip fragment 0-%zu len=%zu : ", ipfrag_pos-1, ipfrag_pos) @@ -633,6 +654,7 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s DLOG("applying tampering to unknown protocol\n") enum dpi_desync_mode desync_mode = params.desync_mode; + uint8_t fooling_orig = FOOL_NONE; ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; if (ip6hdr) ttl_fake = params.desync_ttl6 ? params.desync_ttl6 : ttl_orig; @@ -653,6 +675,7 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s pkt1_len = sizeof(pkt1); b = false; + switch(desync_mode) { case DESYNC_FAKE: @@ -663,7 +686,7 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s b = true; break; case DESYNC_HOPBYHOP: - if (ip6hdr) + if (ip6hdr && params.desync_mode2==DESYNC_NONE) { if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,FOOL_HOPBYHOP, @@ -677,7 +700,8 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s // this mode is final, no other options available return drop; } - return res; + fooling_orig = FOOL_HOPBYHOP; + desync_mode = params.desync_mode2; } if (b) @@ -709,9 +733,15 @@ packet_process_result dpi_desync_udp_packet(uint8_t *data_pkt, size_t len_pkt, s #ifdef __FreeBSD__ // FreeBSD tend to pass ipv6 frames with wrong checksum - if (ip6hdr) - udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); + if (res==modify || ip6hdr) + #else + // if original packet was tampered earlier it needs checksum fixed + if (res==modify) #endif + udp_fix_checksum(udphdr,sizeof(struct udphdr)+len_payload,ip,ip6hdr); + + uint8_t pkt3[DPI_DESYNC_MAX_FAKE_LEN+100], *pkt_orig; + size_t pkt_orig_len; size_t len_transport = len_payload + sizeof(struct udphdr); size_t ipfrag_pos = (params.desync_ipfrag_pos_udp && params.desync_ipfrag_pos_udp