Loading nfq/desync.c +64 −32 Original line number Diff line number Diff line Loading @@ -1953,10 +1953,12 @@ 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]; 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)) { if (bFull) { bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len); bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false; Loading Loading @@ -2012,6 +2014,36 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint } } else { DLOG("QUIC initial contains CRYPTO without full 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,16384,16384,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)) { 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; } return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt); } if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict; } } else { // defrag failed if (!quic_reasm_cancel(ctrack,"QUIC initial defrag CRYPTO failed")) return verdict; Loading nfq/protocol.c +36 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading @@ -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); Loading @@ -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) Loading @@ -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=%d\n",*bFull); } return found; } Loading nfq/protocol.h +2 −1 Original line number Diff line number Diff line Loading @@ -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); Loading
nfq/desync.c +64 −32 Original line number Diff line number Diff line Loading @@ -1953,10 +1953,12 @@ 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]; 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)) { if (bFull) { bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len); bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false; Loading Loading @@ -2012,6 +2014,36 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint } } else { DLOG("QUIC initial contains CRYPTO without full 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,16384,16384,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)) { 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; } return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt); } if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict; } } else { // defrag failed if (!quic_reasm_cancel(ctrack,"QUIC initial defrag CRYPTO failed")) return verdict; Loading
nfq/protocol.c +36 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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); Loading @@ -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); Loading @@ -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) Loading @@ -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=%d\n",*bFull); } return found; } Loading
nfq/protocol.h +2 −1 Original line number Diff line number Diff line Loading @@ -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);