nfqws: allow hopbyhop with 2nd phase desync

This commit is contained in:
bol-van 2022-02-02 15:18:35 +03:00
parent f0a9246fd9
commit 707e86bd90
15 changed files with 98 additions and 21 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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
}

View File

@ -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

View File

@ -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.

View File

@ -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(

View File

@ -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(

View File

@ -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_pos<len_payload)
{
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 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_tcp<len_tcp) ? params.desync_ipfrag_pos_tcp : 24;
uint32_t ident = ip ? ip->ip_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<len_transport) ? params.desync_ipfrag_pos_udp : sizeof(struct udphdr);
@ -721,7 +751,20 @@ packet_process_result dpi_desync_udp_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)