nfqws: multisplit

This commit is contained in:
bol-van 2024-11-11 18:38:23 +03:00
parent c1e670be23
commit a17e490851
9 changed files with 579 additions and 184 deletions

View File

@ -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)<len_payload) ? split_pos-reasm_offset : 0;
if (rsplit_pos)
{
if (split_pos==rsplit_pos)
DLOG("split pos %zu\n",split_pos);
else
{
if (split_pos)
DLOG("split pos was normalized to packet data offset : %zu -> %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)<len_payload) ? split_pos-reasm_offset : 0;
}
static void autottl_discover(t_ctrack *ctrack, bool bIpv6)
@ -618,6 +607,31 @@ static void autottl_discover(t_ctrack *ctrack, bool bIpv6)
}
}
static size_t resolve_split(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct split_pos *sp)
{
switch(l7proto)
{
case HTTP:
return HttpPos(sp->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;i<dp->split_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;
@ -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;i<multisplit_count;i++) DLOG("%zu ",multisplit_pos[i]);
DLOG("\n");
}
else
DLOG("all multisplit pos are outside of this packet\n");
}
}
else if (dp->desync_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)
{
@ -1147,24 +1197,42 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
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;i<multisplit_count;i++)
{
multisplit_pos[j]=pos_normalize(multisplit_pos[i],reasm_offset,dis->len_payload);
if (multisplit_pos[j]) j++;
}
multisplit_count=j;
if (params.debug)
{
if (multisplit_count)
{
DLOG("normalized multisplit pos: ");
for (i=0;i<multisplit_count;i++) DLOG("%zu ",multisplit_pos[i]);
DLOG("\n");
}
else
DLOG("all multisplit pos are outside of this packet\n");
}
}
enum dpi_desync_mode desync_mode = dp->desync_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,
@ -1437,6 +1548,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
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");

View File

@ -32,6 +32,8 @@ enum dpi_desync_mode {
DESYNC_DISORDER2,
DESYNC_SPLIT,
DESYNC_SPLIT2,
DESYNC_MULTISPLIT,
DESYNC_MULTIDISORDER,
DESYNC_IPFRAG2,
DESYNC_HOPBYHOP,
DESYNC_DESTOPT,

View File

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

View File

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

View File

@ -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_count<splits_size ; (*split_count)++)
{
if ((e = strchr(p,',')))
{
c=*e;
*e=0;
}
if (!parse_split_pos(p,splits+*split_count)) return false;
if (e) *e++=c;
p = e;
}
if (p) return false; // too much splits
return true;
}
static void split_compat(struct desync_profile *dp)
{
// make it mostly compatible with old versions
int i;
dp->split_unknown.marker=PM_ABS;
dp->split_unknown.pos=2;
for (i=0;i<dp->split_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;i<dp->split_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;i<dp->split_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, &params.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;x<dp->split_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=[<mode0>,]<mode>[,<mode2>]\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=[<mode0>,]<mode>[,<mode2>]\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=<int|0xHEX>\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=<mode>[,<mode>]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n"
" --dpi-desync-repeats=<N>\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=<int>\t\t; use sequence overlap before first sent original split segment\n"
" --dpi-desync-split-seqovl-pattern=<filename>|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");
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();

View File

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

View File

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

View File

@ -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<sz) ? offset : 0;
}
size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
{
ssize_t offset;
switch(posmarker)
{
case PM_ABS:
offset = (pos<0) ? sz+pos : pos;
return CheckPos(sz,offset);
default:
return 0;
}
}
static size_t HostPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz, size_t offset_host, size_t len_host)
{
ssize_t offset;
const uint8_t *p;
size_t slen;
switch(posmarker)
{
case PM_HOST:
offset = offset_host+pos;
break;
case PM_HOST_END:
offset = offset_host+len_host+pos;
break;
case PM_HOST_SLD:
case PM_HOST_MIDSLD:
case PM_HOST_ENDSLD:
if (((offset_host+len_host)<=sz) && FindNLD(data+offset_host,len_host,2,&p,&slen))
offset = (posmarker==PM_HOST_SLD ? p-data : posmarker==PM_HOST_ENDSLD ? p-data+slen : slen==1 ? p+1-data : p+slen/2-data) + pos;
else
offset = 0;
break;
}
return CheckPos(sz,offset);
}
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
const char *HttpMethod(const uint8_t *data, size_t len)
{
@ -170,39 +252,49 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
// compare 2nd level domains
return strcasecmp(dhost, drhost)!=0;
}
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
{
const uint8_t *method, *host=NULL;
const uint8_t *method, *host=NULL, *p;
size_t offset_host,len_host;
ssize_t offset;
int i;
switch(tpos_type)
switch(posmarker)
{
case httpreqpos_method:
case PM_HTTP_METHOD:
// recognize some tpws pre-applied hacks
method=http;
method=data;
if (sz<10) break;
if (*method=='\n' || *method=='\r') method++;
if (*method=='\n' || *method=='\r') method++;
for (i=0;i<7;i++) if (*method>='A' && *method<='Z') method++;
if (i<3 || *method!=' ') break;
return method-http-1;
case httpreqpos_host:
if (HttpFindHostConst(&host,http,sz) && (host-http+7)<sz)
for (p=method,i=0;i<7;i++) if (*p>='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)<sz)
{
host+=5;
if (*host==' ') host++;
return host-http;
if (*host==' ' || *host=='\t') host++;
offset_host = host-data;
if (posmarker!=PM_HOST)
for (len_host=0; (offset_host+len_host)<sz && data[offset_host+len_host]!='\r' && data[offset_host+len_host]!='\n'; len_host++);
else
len_host = 0;
return HostPos(posmarker,pos,data,sz,offset_host,len_host);
}
break;
case httpreqpos_pos:
break;
default:
return 0;
return AnyProtoPos(posmarker,pos,data,sz);
}
return hpos_pos<sz ? hpos_pos : 0;
return 0;
}
uint16_t TLSRecordDataLen(const uint8_t *data)
{
return pntoh16(data + 3);
@ -355,45 +447,40 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
// find N level domain in SNI
static bool TLSHelloFindNLDInSNI(const uint8_t *ext, size_t elen, int level, const uint8_t **p, size_t *len)
{
size_t slen;
return TLSAdvanceToHostInSNI(&ext,&elen,&slen) && FindNLD(ext,slen,level,p,len);
}
// find the middle of second level domain (SLD) in SNI ext : www.sobaka.ru => 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:
return 0;
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<sz) ? offset : 0;
}
else
{
if (!TLSAdvanceToHostInSNI(&ext,&elen,&len_host))
return 0;
offset_host = ext-data;
return HostPos(posmarker,pos,data,sz,offset_host,len_host);
}
}
return 0;
default:
return AnyProtoPos(posmarker,pos,data,sz);
}
return tpos_pos<sz ? tpos_pos : 0;
}
@ -739,6 +826,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
return found;
}
/*
bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello)
{
if (bIsCryptoHello) *bIsCryptoHello=false;
@ -758,8 +846,9 @@ bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host
if (!IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len)) return false;
if (bIsCryptoHello) *bIsCryptoHello=true;
return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host, true);
return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host, NULL, true);
}
*/
bool IsQUICInitial(const uint8_t *data, size_t len)
{

View File

@ -7,6 +7,21 @@
#include "crypto/aes-gcm.h"
#include "helpers.h"
// pos markers
#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);
const char *posmarker_name(uint8_t posmarker);
size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
extern const char *http_methods[9];
const char *HttpMethod(const uint8_t *data, size_t len);
bool IsHttp(const uint8_t *data, size_t len);
@ -21,8 +36,6 @@ const char *HttpFind2ndLevelDomain(const char *host);
int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos };
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
@ -35,8 +48,6 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_snisld, tlspos_pos };
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);
bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);
bool IsDhtD1(const uint8_t *data, size_t len);
@ -56,4 +67,4 @@ 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);
bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);