mirror of
https://github.com/bol-van/zapret.git
synced 2025-08-10 01:02:03 +03:00
Compare commits
7 Commits
85f2b37c88
...
v70.5
Author | SHA1 | Date | |
---|---|---|---|
|
a2ffa3455d | ||
|
60b97dbed0 | ||
|
e56e4f5f35 | ||
|
5305ea83c8 | ||
|
14b3dd459b | ||
|
66fda2c33d | ||
|
77df43b9cb |
@@ -470,3 +470,4 @@ tpws: detect WSL 1 and warn about non-working options
|
||||
v70.5
|
||||
|
||||
nfqws: multiple --dpi-desync-fake-xxx
|
||||
nfqws: support of inter-packet fragmented QUIC CRYPTO
|
||||
|
Binary file not shown.
Binary file not shown.
112
nfq/desync.c
112
nfq/desync.c
@@ -66,6 +66,9 @@ const uint8_t fake_tls_clienthello_default[648] = {
|
||||
#define PKTDATA_MAXDUMP 32
|
||||
#define IP_MAXDUMP 80
|
||||
|
||||
#define TCP_MAX_REASM 16384
|
||||
#define UDP_MAX_REASM 16384
|
||||
|
||||
bool desync_valid_zero_stage(enum dpi_desync_mode mode)
|
||||
{
|
||||
return mode==DESYNC_SYNACK || mode==DESYNC_SYNDATA;
|
||||
@@ -609,7 +612,7 @@ static uint16_t IP4_IP_ID_FIX(const struct ip *ip)
|
||||
static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache, uint8_t fake_tls_mod, const uint8_t *fake_data, size_t fake_data_size, const uint8_t *payload, size_t payload_len, uint8_t *fake_mod)
|
||||
{
|
||||
bool b=false;
|
||||
if (IsTLSClientHello(fake_data,fake_data_size,false))
|
||||
if (modcache) // it's filled only if it's TLS
|
||||
{
|
||||
if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
|
||||
{
|
||||
@@ -954,7 +957,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
!(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
|
||||
{
|
||||
// do not reconstruct unexpected large payload (they are feeding garbage ?)
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),16384,dis->data_payload,dis->len_payload))
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),TCP_MAX_REASM,dis->data_payload,dis->len_payload))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
@@ -1300,7 +1303,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
switch(l7proto)
|
||||
{
|
||||
case TLS:
|
||||
if ((fake_item->size <= sizeof(fake_data_buf)) &&
|
||||
if ((fake_item->size <= sizeof(fake_data_buf)) &&
|
||||
runtime_tls_mod(n,(struct fake_tls_mod_cache *)fake_item->extra, dp->fake_tls_mod, fake_item->data, fake_item->size, rdata_payload, rlen_payload, fake_data_buf))
|
||||
{
|
||||
fake_data = fake_data_buf;
|
||||
@@ -1359,6 +1362,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
(!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER))))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
rdata_payload=NULL;
|
||||
|
||||
pkt1_len = sizeof(pkt1);
|
||||
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
|
||||
@@ -1952,29 +1956,82 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
return verdict; // cannot be first packet
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t defrag[16384];
|
||||
uint8_t defrag[UDP_MAX_REASM];
|
||||
size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
|
||||
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len))
|
||||
bool bFull;
|
||||
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len,&bFull))
|
||||
{
|
||||
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
||||
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
||||
|
||||
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
||||
|
||||
if (ctrack)
|
||||
if (bFull)
|
||||
{
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
DLOG("QUIC initial contains CRYPTO with full fragment coverage\n");
|
||||
|
||||
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
||||
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
||||
|
||||
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
||||
|
||||
if (ctrack)
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
|
||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||
{
|
||||
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG_ERR("rawpacket_queue failed !\n");
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
if (bReqFull)
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
}
|
||||
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsHello)
|
||||
{
|
||||
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
||||
if (!bHaveHost && dp->desync_skip_nosni)
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
else
|
||||
{
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DLOG("QUIC initial contains CRYPTO with partial fragment coverage\n");
|
||||
if (ctrack)
|
||||
{
|
||||
if (ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||
{
|
||||
@@ -1986,28 +2043,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
reasm_orig_cancel(ctrack);
|
||||
return verdict;
|
||||
}
|
||||
if (bReqFull)
|
||||
{
|
||||
replay_queue(&ctrack->delayed);
|
||||
reasm_orig_fin(ctrack);
|
||||
}
|
||||
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsHello)
|
||||
{
|
||||
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
||||
if (!bHaveHost && dp->desync_skip_nosni)
|
||||
{
|
||||
reasm_orig_cancel(ctrack);
|
||||
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
||||
return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
||||
if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2026,7 +2064,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
||||
{
|
||||
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
|
||||
|
||||
reasm_orig_cancel(ctrack);
|
||||
|
||||
if (IsWireguardHandshakeInitiation(dis->data_payload,dis->len_payload))
|
||||
|
@@ -844,7 +844,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
|
||||
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
|
||||
}
|
||||
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
|
||||
struct range64
|
||||
{
|
||||
uint64_t offset,len;
|
||||
};
|
||||
#define MAX_DEFRAG_PIECES 128
|
||||
static int cmp_range64(const void * a, const void * b)
|
||||
{
|
||||
return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
|
||||
}
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
|
||||
{
|
||||
// Crypto frame can be split into multiple chunks
|
||||
// chromium randomly splits it and pads with zero/one bytes to force support the standard
|
||||
@@ -853,13 +862,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
if (*defrag_len<10) return false;
|
||||
uint8_t *defrag_data = defrag+10;
|
||||
size_t defrag_data_len = *defrag_len-10;
|
||||
|
||||
uint8_t ft;
|
||||
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
|
||||
bool found=false;
|
||||
struct range64 ranges[MAX_DEFRAG_PIECES];
|
||||
int i,range=0;
|
||||
|
||||
while(pos<clean_len)
|
||||
{
|
||||
// frame type
|
||||
ft = clean[pos];
|
||||
pos++;
|
||||
if (ft>1) // 00 - padding, 01 - ping
|
||||
@@ -867,6 +878,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
if (ft!=6) return false; // dont want to know all possible frame type formats
|
||||
|
||||
if (pos>=clean_len) return false;
|
||||
if (range>=MAX_DEFRAG_PIECES) return false;
|
||||
|
||||
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
|
||||
pos += tvb_get_varint(clean+pos, &offset);
|
||||
@@ -875,7 +887,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
pos += tvb_get_varint(clean+pos, &sz);
|
||||
if ((pos+sz)>clean_len) return false;
|
||||
|
||||
if ((offset+sz)>defrag_data_len) return false;
|
||||
if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
|
||||
if (zeropos < offset)
|
||||
// make sure no uninitialized gaps exist in case of not full fragment coverage
|
||||
memset(defrag_data+zeropos,0,offset-zeropos);
|
||||
@@ -886,6 +898,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
|
||||
found=true;
|
||||
pos+=sz;
|
||||
|
||||
ranges[range].offset = offset;
|
||||
ranges[range].len = sz;
|
||||
range++;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
@@ -897,6 +913,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
||||
phton64(defrag+2,szmax);
|
||||
defrag[2] |= 0xC0; // 64 bit value
|
||||
*defrag_len = (size_t)(szmax+10);
|
||||
|
||||
qsort(ranges, range, sizeof(*ranges), cmp_range64);
|
||||
|
||||
//for(i=0 ; i<range ; i++)
|
||||
// printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);
|
||||
|
||||
for(i=0,offset=0,*bFull=true ; i<range ; i++)
|
||||
{
|
||||
if (ranges[i].offset!=offset)
|
||||
{
|
||||
*bFull = false;
|
||||
break;
|
||||
}
|
||||
offset += ranges[i].len;
|
||||
}
|
||||
|
||||
//printf("bFull=%u\n",*bFull);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@@ -87,5 +87,6 @@ uint8_t QUICDraftVersion(uint32_t version);
|
||||
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
|
||||
|
||||
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
|
||||
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
|
||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
|
||||
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
|
||||
|
Reference in New Issue
Block a user