From a17e4908514f3f280d0d4a216f7d48b169d94ff8 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 11 Nov 2024 18:38:23 +0300 Subject: [PATCH] nfqws: multisplit --- nfq/desync.c | 275 +++++++++++++++++++++++++++++++++++-------------- nfq/desync.h | 2 + nfq/helpers.c | 21 ++++ nfq/helpers.h | 3 + nfq/nfqws.c | 232 ++++++++++++++++++++++++++++++++--------- nfq/params.c | 1 - nfq/params.h | 19 +++- nfq/protocol.c | 189 ++++++++++++++++++++++++--------- nfq/protocol.h | 21 +++- 9 files changed, 579 insertions(+), 184 deletions(-) diff --git a/nfq/desync.c b/nfq/desync.c index cf942e1..729ca29 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -97,11 +97,11 @@ bool desync_only_first_stage(enum dpi_desync_mode mode) } bool desync_valid_second_stage(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; + return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER; } bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode) { - return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2; + return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2; } bool desync_valid_second_stage_udp(enum dpi_desync_mode mode) { @@ -131,6 +131,10 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_SPLIT; else if (!strcmp(s,"split2")) return DESYNC_SPLIT2; + else if (!strcmp(s,"multisplit")) + return DESYNC_MULTISPLIT; + else if (!strcmp(s,"multidisorder")) + return DESYNC_MULTIDISORDER; else if (!strcmp(s,"ipfrag2")) return DESYNC_IPFRAG2; else if (!strcmp(s,"hopbyhop")) @@ -582,22 +586,7 @@ static bool replay_queue(struct rawpacket_tailhead *q); static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_payload) { - size_t rsplit_pos = split_pos; - // normalize split pos to current packet - split_pos=(split_pos>reasm_offset && (split_pos-reasm_offset) %zu\n",rsplit_pos,split_pos); - else - DLOG("split pos %zu is outside of this packet %zu-%zu\n",rsplit_pos,reasm_offset,reasm_offset+len_payload); - } - } - return split_pos; + return (split_pos>reasm_offset && (split_pos-reasm_offset)marker, sp->pos, data, sz); + case TLS: + return TLSPos(sp->marker, sp->pos, data, sz); + default: + return AnyProtoPos(sp->marker, sp->pos, data, sz); + } +} +static void resolve_multisplit(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct desync_profile *dp, size_t *pos, int *pos_count) +{ + int i,j; + for(i=j=0;isplit_count;i++) + { + pos[j] = resolve_split(data,sz,l7proto,dp->splits+i); + if (pos[j]) j++; + } + qsort_size_t(pos, j); + j=unique_size_t(pos, j); + *pos_count=j; +} + static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, struct dissect *dis) { uint8_t verdict=VERDICT_PASS; @@ -845,7 +859,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } // !replay - + if (!(dis->tcp->th_flags & TH_SYN) && dis->len_payload) { const uint8_t *fake; @@ -856,6 +870,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint const uint8_t *rdata_payload = dis->data_payload; size_t rlen_payload = dis->len_payload; size_t split_pos; + size_t multisplit_pos[MAX_SPLITS]; + int multisplit_count; + int i; t_l7proto l7proto = UNKNOWN; if (replay) @@ -1065,35 +1082,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - // desync profile may have changed after hostname was revealed - switch(l7proto) - { - case HTTP: - fake = dp->fake_http; - fake_size = dp->fake_http_size; - split_pos = HttpPos(dp->desync_split_http_req, dp->desync_split_pos, rdata_payload, rlen_payload); - break; - case TLS: - fake = dp->fake_tls; - fake_size = dp->fake_tls_size; - split_pos = TLSPos(dp->desync_split_tls, dp->desync_split_pos, rdata_payload, rlen_payload, 0); - break; - default: - fake = dp->fake_unknown; - fake_size = dp->fake_unknown_size; - split_pos=dp->desync_split_pos; - break; - } - - // we do not need reasm buffer anymore - reasm_orig_cancel(ctrack); - rdata_payload=NULL; - if (l7proto==UNKNOWN) { if (!dp->desync_any_proto) { DLOG("not applying tampering to unknown protocol\n"); + reasm_orig_cancel(ctrack); return verdict; } DLOG("applying tampering to unknown protocol\n"); @@ -1135,7 +1129,63 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - if (dp->desync_mode==DESYNC_NONE) return verdict; + if (dp->desync_mode==DESYNC_NONE) + { + reasm_orig_cancel(ctrack); + return verdict; + } + + const struct split_pos *spos; + switch(l7proto) + { + case HTTP: + fake = dp->fake_http; + fake_size = dp->fake_http_size; + spos = &dp->split_http; + break; + case TLS: + fake = dp->fake_tls; + fake_size = dp->fake_tls_size; + spos = &dp->split_tls; + break; + default: + fake = dp->fake_unknown; + fake_size = dp->fake_unknown_size; + spos = &dp->split_unknown; + break; + } + if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER) + { + split_pos=0; + resolve_multisplit(rdata_payload, rlen_payload, l7proto, dp, multisplit_pos, &multisplit_count); + if (params.debug) + { + if (multisplit_count) + { + DLOG("multisplit pos: "); + for (i=0;idesync_mode==DESYNC_SPLIT || dp->desync_mode==DESYNC_SPLIT2 || dp->desync_mode==DESYNC_DISORDER || dp->desync_mode==DESYNC_DISORDER2 || + dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2) + { + multisplit_count=0; + split_pos = resolve_split(rdata_payload, rlen_payload, l7proto, spos); + DLOG("regular split pos: %zu\n",split_pos); + } + else + { + multisplit_count=0; + split_pos = 0; + } + + // we do not need reasm buffer anymore + reasm_orig_cancel(ctrack); + rdata_payload=NULL; if (params.debug) { @@ -1144,27 +1194,45 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2)); DLOG("dpi desync src=%s dst=%s\n",s1,s2); } - + if (!split_pos || split_pos>rlen_payload) split_pos=1; split_pos=pos_normalize(split_pos,reasm_offset,dis->len_payload); + if (split_pos) + DLOG("normalized regular split pos : %zu\n",split_pos); + else + DLOG("regular split pos is outside of this packet\n"); + if (multisplit_count) + { + int j; + for (i=j=0;ilen_payload); + if (multisplit_pos[j]) j++; + } + multisplit_count=j; + if (params.debug) + { + if (multisplit_count) + { + DLOG("normalized multisplit pos: "); + for (i=0;idesync_mode; uint32_t fooling_orig = FOOL_NONE; - bool b; + bool bFake = false; pkt1_len = sizeof(pkt1); - b = false; - switch(desync_mode) + switch(dp->desync_mode) { case DESYNC_FAKE_KNOWN: - if (reasm_offset) - { - desync_mode = dp->desync_mode2; - break; - } + if (reasm_offset) break; if (l7proto==UNKNOWN) { DLOG("not applying fake because of unknown protocol\n"); - desync_mode = dp->desync_mode2; break; } case DESYNC_FAKE: @@ -1176,14 +1244,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { return verdict; } - DLOG("sending fake request : "); + DLOG("sending fake : "); hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); - b = true; + bFake = true; break; case DESYNC_RST: case DESYNC_RSTACK: if (reasm_offset) break; - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (dp->desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, NULL, 0, pkt1, &pkt1_len)) @@ -1191,15 +1259,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint return verdict; } DLOG("sending fake RST/RSTACK\n"); - b = true; + bFake = true; break; case DESYNC_HOPBYHOP: case DESYNC_DESTOPT: case DESYNC_IPFRAG1: - fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; - desync_mode = dp->desync_mode2; - if (dis->ip6 && (desync_mode==DESYNC_NONE || !desync_valid_second_stage_tcp(desync_mode) || - (!split_pos && (desync_mode==DESYNC_SPLIT || desync_mode==DESYNC_SPLIT2 || desync_mode==DESYNC_DISORDER || desync_mode==DESYNC_DISORDER2)))) + fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; + if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2) || + (!split_pos && (dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2)) || + (!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)))) { 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, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), @@ -1219,24 +1287,65 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint break; } - if (b) + if (bFake) { if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; - if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2)) - { - DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); - verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6); - if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) - return verdict; - return VERDICT_DROP; - } - desync_mode = dp->desync_mode2; } - pkt1_len = sizeof(pkt1); + enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2; switch(desync_mode) { + case DESYNC_MULTISPLIT: + if (multisplit_count) + { + size_t from,to; + for (i=0,from=0 ; i<=multisplit_count ; i++) + { + to = i==multisplit_count ? dis->len_payload : multisplit_pos[i]; + + pkt1_len = sizeof(pkt1); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, + net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack, + dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + fooling_orig,0,0, + dis->data_payload+from, to-from, pkt1, &pkt1_len)) + return verdict; + DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+1,from,to-1,to-from); + hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + return verdict; + + from = to; + } + return VERDICT_DROP; + } + break; + case DESYNC_MULTIDISORDER: + if (multisplit_count) + { + size_t from,to; + for (i=multisplit_count-1,to=dis->len_payload ; i>=-1 ; i--) + { + from = i>=0 ? multisplit_pos[i] : 0; + + pkt1_len = sizeof(pkt1); + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, + net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack, + dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), + fooling_orig,0,0, + dis->data_payload+from, to-from, pkt1, &pkt1_len)) + return verdict; + DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+2,from,to-1,to-from); + hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + return verdict; + + to = from; + } + return VERDICT_DROP; + } + break; case DESYNC_DISORDER: case DESYNC_DISORDER2: if (split_pos) @@ -1270,6 +1379,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = dis->len_payload-split_pos; } + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq , split_pos - dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, @@ -1357,6 +1467,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = split_pos; } + pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq,-dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, @@ -1436,7 +1547,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint default: break; } - + + if (bFake) + { + DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload); + verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6); + if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt)) + return verdict; + return VERDICT_DROP; + } } return verdict; @@ -1833,7 +1952,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_FAKE: if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len)) return verdict; - DLOG("sending fake request : "); + DLOG("sending fake : "); hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; @@ -1996,7 +2115,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[80]; str_tcphdr(s,sizeof(s),dis->tcp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("TCP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("TCP: len %zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } } else if (dis->udp) @@ -2004,7 +2123,7 @@ static void packet_debug(bool replay, const struct dissect *dis) char s[30]; str_udphdr(s,sizeof(s),dis->udp); DLOG(" %s\n",s); - if (dis->len_payload) { DLOG("UDP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } + if (dis->len_payload) { DLOG("UDP: len %zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); } } else DLOG("\n"); diff --git a/nfq/desync.h b/nfq/desync.h index 4aa42aa..4250f5c 100644 --- a/nfq/desync.h +++ b/nfq/desync.h @@ -32,6 +32,8 @@ enum dpi_desync_mode { DESYNC_DISORDER2, DESYNC_SPLIT, DESYNC_SPLIT2, + DESYNC_MULTISPLIT, + DESYNC_MULTIDISORDER, DESYNC_IPFRAG2, DESYNC_HOPBYHOP, DESYNC_DESTOPT, diff --git a/nfq/helpers.c b/nfq/helpers.c index 2d2d320..90c0452 100644 --- a/nfq/helpers.c +++ b/nfq/helpers.c @@ -11,6 +11,27 @@ #include "params.h" +int unique_size_t(size_t *pu, int ct) +{ + int i, j, u; + for (i = j = 0; j < ct; i++) + { + u = pu[j++]; + for (; j < ct && pu[j] == u; j++); + pu[i] = u; + } + return i; +} +static int cmp_size_t(const void * a, const void * b) +{ + return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b; +} +void qsort_size_t(size_t *array,size_t ct) +{ + qsort(array,ct,sizeof(*array),cmp_size_t); +} + + void rtrim(char *s) { if (s) diff --git a/nfq/helpers.h b/nfq/helpers.h index c06862e..58051a7 100644 --- a/nfq/helpers.h +++ b/nfq/helpers.h @@ -17,6 +17,9 @@ typedef union char _align[32]; // force 16-byte alignment for ip6_and int128 ops } sockaddr_in46; +int unique_size_t(size_t *pu, int ct); +void qsort_size_t(size_t *array,size_t ct); + void rtrim(char *s); void replace_char(char *s, char from, char to); char *strncasestr(const char *s,const char *find, size_t slen); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index e07054f..c9f18ed 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -673,10 +673,7 @@ static bool parse_l7_list(char *opt, uint32_t *l7) *l7 |= L7_PROTO_UNKNOWN; else return false; - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } return true; @@ -723,15 +720,173 @@ static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6) *ipv6 = true; else return false; - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } return true; } +static bool parse_httpreqpos(const char *s, struct split_pos *sp) +{ + if (!strcmp(s, "method")) + { + sp->marker = PM_HTTP_METHOD; + sp->pos=2; + } + else if (!strcmp(s, "host")) + { + sp->marker = PM_HOST; + sp->pos=1; + } + else + return false; + return true; +} +static bool parse_tlspos(const char *s, struct split_pos *sp) +{ + if (!strcmp(s, "sni")) + { + sp->marker = PM_HOST; + sp->pos=1; + } + else if (!strcmp(s, "sniext")) + { + sp->marker = PM_SNI_EXT; + sp->pos=0; + } + else if (!strcmp(s, "snisld")) + { + sp->marker = PM_HOST_MIDSLD; + sp->pos=1; + } + else + return false; + return true; +} + +static bool parse_int16(const char *p, int16_t *v) +{ + if (*p=='+' || *p=='-' || *p>='0' && *p<='9') + { + int i = atoi(p); + *v = (int16_t)i; + return *v==i; // check overflow + } + return false; +} +static bool parse_posmarker(const char *opt, uint8_t *posmarker) +{ + if (!strcmp(opt,"host")) + *posmarker = PM_HOST; + else if (!strcmp(opt,"endhost")) + *posmarker = PM_HOST_END; + else if (!strcmp(opt,"sld")) + *posmarker = PM_HOST_SLD; + else if (!strcmp(opt,"midsld")) + *posmarker = PM_HOST_MIDSLD; + else if (!strcmp(opt,"endsld")) + *posmarker = PM_HOST_ENDSLD; + else if (!strcmp(opt,"method")) + *posmarker = PM_HTTP_METHOD; + else if (!strcmp(opt,"sniext")) + *posmarker = PM_SNI_EXT; + else + return false; + return true; +} +static bool parse_split_pos(char *opt, struct split_pos *split) +{ + if (parse_int16(opt,&split->pos)) + { + split->marker = PM_ABS; + return !!split->pos; + } + else + { + char c,*p=opt; + bool b; + + for (; *opt && *opt!='+' && *opt!='-'; opt++); + c=*opt; *opt=0; + b=parse_posmarker(p,&split->marker); + *opt=c; + if (!b) return false; + if (*opt) + return parse_int16(opt,&split->pos); + else + split->pos = 0; + } + return true; +} +static bool parse_split_pos_list(char *opt, struct split_pos *splits, int splits_size, int *split_count) +{ + char c,*e,*p; + + for (p=opt, *split_count=0 ; p && *split_countsplit_unknown.marker=PM_ABS; + dp->split_unknown.pos=2; + for (i=0;isplit_count;i++) + { + if (dp->splits[i].marker==PM_ABS) + { + dp->split_unknown.pos=dp->splits[i].pos; + break; + } + } + if (SPLIT_POS_EMPTY(&dp->split_http)) + { + dp->split_http=dp->split_unknown; + for (i=0;isplit_count;i++) + if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_HTTP_METHOD) + { + dp->split_http = dp->splits[i]; + break; + } + } + if (SPLIT_POS_EMPTY(&dp->split_tls)) + { + dp->split_tls=dp->split_unknown; + for (i=0;isplit_count;i++) + if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_SNI_EXT) + { + dp->split_tls = dp->splits[i]; + break; + } + } +} +static void SplitDebug(void) +{ + struct desync_profile_list *dpl; + const struct desync_profile *dp; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + { + dp = &dpl->dp; + DLOG("profile %d split_http %s %d\n",dp->n,posmarker_name(dp->split_http.marker),dp->split_http.pos); + DLOG("profile %d split_tls %s %d\n",dp->n,posmarker_name(dp->split_tls.marker),dp->split_tls.pos); + DLOG("profile %d split_unknown %s %d\n",dp->n,posmarker_name(dp->split_unknown.marker),dp->split_unknown.pos); + for(int x=0;xsplit_count;x++) + DLOG("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos); + } +} + + #ifdef __CYGWIN__ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len) { @@ -757,10 +912,7 @@ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *bu if (n) strncat(buf," or ",len-strlen(buf)-1); strncat(buf, s1, len-strlen(buf)-1); - if (e) - { - *e++=c; - } + if (e) *e++=c; p = e; } strncat(buf, ")", len-strlen(buf)-1); @@ -912,7 +1064,9 @@ static void exithelp(void) " --hostspell\t\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostnospace\t\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" " --domcase\t\t\t\t\t; mix domain case : Host: TeSt.cOm\n" - " --dpi-desync=[,][,]\t; try to desync dpi state. modes : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen tamper\n" + " --dpi-desync=[,][,]\t; try to desync dpi state. modes :\n" + "\t\t\t\t\t\t; synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1\n" + "\t\t\t\t\t\t; disorder disorder2 split split2 multisplit multidisorder ipfrag2 udplen tamper\n" #ifdef __linux__ " --dpi-desync-fwmark=\t\t; override fwmark for desync packet. default = 0x%08X (%u)\n" #elif defined(SO_USER_COOKIE) @@ -925,9 +1079,9 @@ static void exithelp(void) " --dpi-desync-fooling=[,]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n" " --dpi-desync-repeats=\t\t\t; send every desync packet N times\n" " --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n" - " --dpi-desync-split-pos=<1..%u>\t\t; data payload split position\n" - " --dpi-desync-split-http-req=method|host\t; split at specified logical part of plain http request\n" - " --dpi-desync-split-tls=sni|sniext|snisld\t; split at specified logical part of TLS ClientHello\n" + " --dpi-desync-split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions. markers: method,host,endhost,sld,endsld,midsld,sniext\n" + "\t\t\t\t\t\t; full list is only used by multisplit and multidisorder\n" + "\t\t\t\t\t\t; single split takes first l7-protocol-compatible parameter if present, first abs value otherwise\n" " --dpi-desync-split-seqovl=\t\t; use sequence overlap before first sent original split segment\n" " --dpi-desync-split-seqovl-pattern=|0xHEX ; pattern for the fake part of overlap\n" " --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n" @@ -953,7 +1107,6 @@ static void exithelp(void) DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT, #endif AUTOTTL_DEFAULT_DELTA,AUTOTTL_DEFAULT_MIN,AUTOTTL_DEFAULT_MAX, - DPI_DESYNC_MAX_FAKE_LEN, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT, BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT, @@ -967,29 +1120,6 @@ static void exithelp_clean(void) exithelp(); } -bool parse_httpreqpos(const char *s, enum httpreqpos *pos) -{ - if (!strcmp(s, "method")) - *pos = httpreqpos_method; - else if (!strcmp(s, "host")) - *pos = httpreqpos_host; - else - return false; - return true; -} -bool parse_tlspos(const char *s, enum tlspos *pos) -{ - if (!strcmp(s, "sni")) - *pos = tlspos_sni; - else if (!strcmp(s, "sniext")) - *pos = tlspos_sniext; - else if (!strcmp(s, "snisld")) - *pos = tlspos_snisld; - else - return false; - return true; -} - #ifndef __OpenBSD__ // no static to not allow optimizer to inline this func (save stack) void config_from_file(const char *filename) @@ -1447,21 +1577,27 @@ int main(int argc, char **argv) dp->desync_skip_nosni = !optarg || atoi(optarg); break; case 23: /* dpi-desync-split-pos */ - if (sscanf(optarg,"%u",&dp->desync_split_pos)<1 || dp->desync_split_pos<1) { - DLOG_ERR("dpi-desync-split-pos is not valid\n"); - exit_clean(1); + int ct; + if (!parse_split_pos_list(optarg,dp->splits+dp->split_count,MAX_SPLITS-dp->split_count,&ct)) + { + DLOG_ERR("could not parse split pos list or too much positions (before parsing - %u, max - %u) : %s\n",dp->split_count,MAX_SPLITS,optarg); + exit_clean(1); + } + dp->split_count += ct; } break; case 24: /* dpi-desync-split-http-req */ - if (!parse_httpreqpos(optarg, &dp->desync_split_http_req)) + // obsolete arg + if (!parse_httpreqpos(optarg, &dp->split_http)) { DLOG_ERR("Invalid argument for dpi-desync-split-http-req\n"); exit_clean(1); } break; case 25: /* dpi-desync-split-tls */ - if (!parse_tlspos(optarg, &dp->desync_split_tls)) + // obsolete arg + if (!parse_tlspos(optarg, &dp->split_tls)) { DLOG_ERR("Invalid argument for dpi-desync-split-tls\n"); exit_clean(1); @@ -1919,8 +2055,7 @@ int main(int argc, char **argv) DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",dp->n,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max); if (AUTOTTL_ENABLED(dp->desync_autottl6)) DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max); - if (dp->desync_split_tls==tlspos_none && dp->desync_split_pos) dp->desync_split_tls=tlspos_pos; - if (dp->desync_split_http_req==httpreqpos_none && dp->desync_split_pos) dp->desync_split_http_req=httpreqpos_pos; + split_compat(dp); } if (!LoadAllHostLists()) @@ -1937,6 +2072,9 @@ int main(int argc, char **argv) DLOG("\nlists summary:\n"); HostlistsDebug(); IpsetsDebug(); + + DLOG("\nsplits summary:\n"); + SplitDebug(); DLOG("\n"); if (daemon) daemonize(); diff --git a/nfq/params.c b/nfq/params.c index 3cece72..8a7702c 100644 --- a/nfq/params.c +++ b/nfq/params.c @@ -169,7 +169,6 @@ struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head) memcpy(entry->dp.hostspell, "host", 4); // default hostspell entry->dp.desync_skip_nosni = true; - entry->dp.desync_split_pos = 2; entry->dp.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT; entry->dp.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT; entry->dp.desync_repeats = 1; diff --git a/nfq/params.h b/nfq/params.h index 7af45c6..4fff9dc 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -40,6 +40,14 @@ enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG }; +struct split_pos +{ + int16_t pos; + uint8_t marker; +}; +#define SPLIT_POS_EMPTY(sp) ((sp)->marker==PM_ABS && (sp)->pos==0) +#define MAX_SPLITS 64 + struct desync_profile { int n; // number of the profile @@ -53,9 +61,14 @@ struct desync_profile char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2; bool desync_retrans,desync_skip_nosni,desync_any_proto; - unsigned int desync_repeats,desync_split_pos,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; - enum httpreqpos desync_split_http_req; - enum tlspos desync_split_tls; + unsigned int desync_repeats,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp; + + // multisplit + struct split_pos splits[MAX_SPLITS]; + int split_count; + // single split pos cache + struct split_pos split_http,split_tls,split_unknown; + char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int desync_start, desync_cutoff; uint8_t desync_ttl, desync_ttl6; diff --git a/nfq/protocol.c b/nfq/protocol.c index bec8362..1283e2c 100644 --- a/nfq/protocol.c +++ b/nfq/protocol.c @@ -24,6 +24,88 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t ** return true; } + +#define PM_ABS 0 +#define PM_HOST 1 +#define PM_HOST_END 2 +#define PM_HOST_SLD 3 +#define PM_HOST_MIDSLD 4 +#define PM_HOST_ENDSLD 5 +#define PM_HTTP_METHOD 6 +#define PM_SNI_EXT 7 +bool IsHostMarker(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + return true; + default: + return false; + } +} +const char *posmarker_name(uint8_t posmarker) +{ + switch(posmarker) + { + case PM_ABS: return "abs"; + case PM_HOST: return "host"; + case PM_HOST_END: return "endhost"; + case PM_HOST_SLD: return "sld"; + case PM_HOST_MIDSLD: return "midsld"; + case PM_HOST_ENDSLD: return "endsld"; + case PM_HTTP_METHOD: return "method"; + case PM_SNI_EXT: return "sniext"; + default: return "?"; + } +} + +static size_t CheckPos(size_t sz, ssize_t offset) +{ + return (offset>=0 && offset='A' && *method<='Z') method++; - if (i<3 || *method!=' ') break; - return method-http-1; - case httpreqpos_host: - if (HttpFindHostConst(&host,http,sz) && (host-http+7)='A' && *p<='Z') p++; + if (i<3 || *p!=' ') break; + return CheckPos(sz,method-data+pos); + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + if (HttpFindHostConst(&host,data,sz) && (host-data+7) aka.ru -// return false if SNI ext is bad or SLD is not found -static bool TLSHelloFindMiddleOfSLDInSNI(const uint8_t *ext, size_t elen, const uint8_t **p) -{ - size_t len; - if (!TLSHelloFindNLDInSNI(ext,elen,2,p,&len)) - return false; - // in case of one letter SLD (x.com) we split at '.' to prevent appearance of the whole SLD - *p = (len==1) ? *p+1 : *p+len/2; - return true; -} -size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type) +size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz) { size_t elen; const uint8_t *ext, *p; - switch(tpos_type) + size_t offset_host,len_host; + ssize_t offset; + + switch(posmarker) { - case tlspos_sni: - case tlspos_sniext: - if (TLSFindExt(tls,sz,0,&ext,&elen,false)) - return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1; - break; - case tlspos_snisld: - if (TLSFindExt(tls,sz,0,&ext,&elen,false)) - if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p)) - return p-tls; - break; - case tlspos_pos: - break; - default: + case PM_HOST: + case PM_HOST_END: + case PM_HOST_SLD: + case PM_HOST_MIDSLD: + case PM_HOST_ENDSLD: + case PM_SNI_EXT: + if (TLSFindExt(data,sz,0,&ext,&elen,false)) + { + if (posmarker==PM_SNI_EXT) + { + offset = ext-data+1+pos; + return (offset>=0 && offset