nfqws,tpws: snisld split

This commit is contained in:
bol-van 2024-11-09 15:53:28 +03:00
parent 6514b6f4c3
commit f0e68527ba
7 changed files with 131 additions and 48 deletions

View File

@ -1270,7 +1270,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
seg_len = dis->len_payload-split_pos; seg_len = dis->len_payload-split_pos;
} }
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(net32_add(dis->tcp->th_seq,split_pos),-dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps, 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), ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
seg, seg_len, pkt1, &pkt1_len)) seg, seg_len, pkt1, &pkt1_len))

View File

@ -927,7 +927,7 @@ static void exithelp(void)
" --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\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-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-http-req=method|host\t; split at specified logical part of plain http request\n"
" --dpi-desync-split-tls=sni|sniext\t\t; split at specified logical part of TLS ClientHello\n" " --dpi-desync-split-tls=sni|sniext|snisld\t; split at specified logical part of TLS ClientHello\n"
" --dpi-desync-split-seqovl=<int>\t\t; use sequence overlap before first sent original split segment\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-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" " --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n"
@ -983,6 +983,8 @@ bool parse_tlspos(const char *s, enum tlspos *pos)
*pos = tlspos_sni; *pos = tlspos_sni;
else if (!strcmp(s, "sniext")) else if (!strcmp(s, "sniext"))
*pos = tlspos_sniext; *pos = tlspos_sniext;
else if (!strcmp(s, "snisld"))
*pos = tlspos_snisld;
else else
return false; return false;
return true; return true;

View File

@ -7,6 +7,23 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <string.h> #include <string.h>
// find N level domain
static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t **p, size_t *len)
{
int i;
const uint8_t *p1,*p2;
for (i=1,p2=dom+dlen;i<level;i++)
{
for (p2--; p2>dom && *p2!='.'; p2--);
if (p2<=dom) return false;
}
for (p1=p2-1 ; p1>dom && *p1!='.'; p1--);
if (*p1=='.') p1++;
if (p) *p = p1;
if (len) *len = p2-p1;
return true;
}
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
const char *HttpMethod(const uint8_t *data, size_t len) const char *HttpMethod(const uint8_t *data, size_t len)
{ {
@ -116,17 +133,6 @@ bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_hos
{ {
return HttpExtractHeader(data, len, "\nHost:", host, len_host); return HttpExtractHeader(data, len, "\nHost:", host, len_host);
} }
const char *HttpFind2ndLevelDomain(const char *host)
{
const char *p=NULL;
if (*host)
{
for (p = host + strlen(host)-1; p>host && *p!='.'; p--);
if (*p=='.') for (p--; p>host && *p!='.'; p--);
if (*p=='.') p++;
}
return p;
}
// DPI redirects are global redirects to another domain // DPI redirects are global redirects to another domain
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host) bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host)
{ {
@ -157,10 +163,11 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
// somethinkg like : censor.net // somethinkg like : censor.net
// extract 2nd level domains // extract 2nd level domains
const char *dhost, *drhost;
if (!FindNLD((uint8_t*)host,strlen(host),2,(const uint8_t**)&dhost,NULL) || !FindNLD((uint8_t*)redirect_host,strlen(redirect_host),2,(const uint8_t**)&drhost,NULL))
return false;
const char *dhost = HttpFind2ndLevelDomain(host); // compare 2nd level domains
const char *drhost = HttpFind2ndLevelDomain(redirect_host);
return strcasecmp(dhost, drhost)!=0; 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(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
@ -305,15 +312,24 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK); return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK);
} }
static bool TLSAdvanceToHostInSNI(const uint8_t **ext, size_t *elen, size_t *slen)
{
// u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length
if (*elen < 5 || (*ext)[2] != 0) return false;
*slen = pntoh16(*ext + 3);
*ext += 5; *elen -= 5;
return *slen <= *elen;
}
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host) static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)
{ {
// u16 data+0 - name list length // u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name // u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length // u16 data+3 - server name length
if (elen < 5 || ext[2] != 0) return false; size_t slen;
size_t slen = pntoh16(ext + 3); if (!TLSAdvanceToHostInSNI(&ext,&elen,&slen))
ext += 5; elen -= 5; return false;
if (slen < elen) return false;
if (host && len_host) if (host && len_host)
{ {
if (slen >= len_host) slen = len_host - 1; if (slen >= len_host) slen = len_host - 1;
@ -338,22 +354,46 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false; if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host); 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(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
{ {
size_t elen; size_t elen;
const uint8_t *ext; const uint8_t *ext, *p;
switch(tpos_type) switch(tpos_type)
{ {
case tlspos_sni: case tlspos_sni:
case tlspos_sniext: case tlspos_sniext:
if (TLSFindExt(tls,sz,0,&ext,&elen,false)) if (TLSFindExt(tls,sz,0,&ext,&elen,false))
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1; return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
// fall through break;
case tlspos_snisld:
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p))
return p-tls;
break;
case tlspos_pos: case tlspos_pos:
return tpos_pos<sz ? tpos_pos : 0; break;
default: default:
return 0; return 0;
} }
return tpos_pos<sz ? tpos_pos : 0;
} }

View File

@ -35,7 +35,7 @@ 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 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 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); 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_pos }; 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); 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 IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);

View File

@ -7,6 +7,22 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <string.h> #include <string.h>
// find N level domain
static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t **p, size_t *len)
{
int i;
const uint8_t *p1,*p2;
for (i=1,p2=dom+dlen;i<level;i++)
{
for (p2--; p2>dom && *p2!='.'; p2--);
if (p2<=dom) return false;
}
for (p1=p2-1 ; p1>dom && *p1!='.'; p1--);
if (*p1=='.') p1++;
if (p) *p = p1;
if (len) *len = p2-p1;
return true;
}
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
const char *HttpMethod(const uint8_t *data, size_t len) const char *HttpMethod(const uint8_t *data, size_t len)
@ -116,17 +132,6 @@ bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_hos
{ {
return HttpExtractHeader(data, len, "\nHost:", host, len_host); return HttpExtractHeader(data, len, "\nHost:", host, len_host);
} }
const char *HttpFind2ndLevelDomain(const char *host)
{
const char *p=NULL;
if (*host)
{
for (p = host + strlen(host)-1; p>host && *p!='.'; p--);
if (*p=='.') for (p--; p>host && *p!='.'; p--);
if (*p=='.') p++;
}
return p;
}
// DPI redirects are global redirects to another domain // DPI redirects are global redirects to another domain
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host) bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host)
{ {
@ -157,10 +162,11 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
// somethinkg like : censor.net // somethinkg like : censor.net
// extract 2nd level domains // extract 2nd level domains
const char *dhost, *drhost;
if (!FindNLD((uint8_t*)host,strlen(host),2,(const uint8_t**)&dhost,NULL) || !FindNLD((uint8_t*)redirect_host,strlen(redirect_host),2,(const uint8_t**)&drhost,NULL))
return false;
const char *dhost = HttpFind2ndLevelDomain(host); // compare 2nd level domains
const char *drhost = HttpFind2ndLevelDomain(redirect_host);
return strcasecmp(dhost, drhost)!=0; 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(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
@ -295,15 +301,24 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has if (reclen<len) len=reclen; // correct len if it has more data than the first tls record has
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK); return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK);
} }
static bool TLSAdvanceToHostInSNI(const uint8_t **ext, size_t *elen, size_t *slen)
{
// u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length
if (*elen < 5 || (*ext)[2] != 0) return false;
*slen = pntoh16(*ext + 3);
*ext += 5; *elen -= 5;
return *slen <= *elen;
}
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host) static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)
{ {
// u16 data+0 - name list length // u16 data+0 - name list length
// u8 data+2 - server name type. 0=host_name // u8 data+2 - server name type. 0=host_name
// u16 data+3 - server name length // u16 data+3 - server name length
if (elen < 5 || ext[2] != 0) return false; size_t slen;
size_t slen = pntoh16(ext + 3); if (!TLSAdvanceToHostInSNI(&ext,&elen,&slen))
ext += 5; elen -= 5; return false;
if (slen < elen) return false;
if (host && len_host) if (host && len_host)
{ {
if (slen >= len_host) slen = len_host - 1; if (slen >= len_host) slen = len_host - 1;
@ -328,20 +343,44 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false; if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host); 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(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
{ {
size_t elen; size_t elen;
const uint8_t *ext; const uint8_t *ext, *p;
switch(tpos_type) switch(tpos_type)
{ {
case tlspos_sni: case tlspos_sni:
case tlspos_sniext: case tlspos_sniext:
if (TLSFindExt(tls,sz,0,&ext,&elen,false)) if (TLSFindExt(tls,sz,0,&ext,&elen,false))
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1; return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
// fall through break;
case tlspos_snisld:
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p))
return p-tls;
break;
case tlspos_pos: case tlspos_pos:
return tpos_pos<sz ? tpos_pos : 0; break;
default: default:
return 0; return 0;
} }
return tpos_pos<sz ? tpos_pos : 0;
} }

View File

@ -29,5 +29,5 @@ 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 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 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); 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_pos }; 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); size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);

View File

@ -183,7 +183,7 @@ static void exithelp(void)
" --hostlist-auto-debug=<logfile>\t; debug auto hostlist positives\n" " --hostlist-auto-debug=<logfile>\t; debug auto hostlist positives\n"
"\nTAMPER:\n" "\nTAMPER:\n"
" --split-http-req=method|host\t\t; split at specified logical part of plain http request\n" " --split-http-req=method|host\t\t; split at specified logical part of plain http request\n"
" --split-tls=sni|sniext\t\t\t; split at specified logical part of TLS ClientHello\n" " --split-tls=sni|sniext|snisld\t\t; split at specified logical part of TLS ClientHello\n"
" --split-pos=<numeric_offset>\t\t; split at specified pos. split-http-req or split-tls take precedence for http.\n" " --split-pos=<numeric_offset>\t\t; split at specified pos. split-http-req or split-tls take precedence for http.\n"
" --split-any-protocol\t\t\t; split not only http and https\n" " --split-any-protocol\t\t\t; split not only http and https\n"
#if defined(BSD) && !defined(__APPLE__) #if defined(BSD) && !defined(__APPLE__)
@ -203,7 +203,7 @@ static void exithelp(void)
" --methodspace\t\t\t\t; add extra space after method\n" " --methodspace\t\t\t\t; add extra space after method\n"
" --methodeol\t\t\t\t; add end-of-line before method\n" " --methodeol\t\t\t\t; add end-of-line before method\n"
" --unixeol\t\t\t\t; replace 0D0A to 0A\n" " --unixeol\t\t\t\t; replace 0D0A to 0A\n"
" --tlsrec=sni|sniext\t\t\t; make 2 TLS records. split at specified logical part. don't split if SNI is not present\n" " --tlsrec=sni|sniext|snisld\t\t; make 2 TLS records. split at specified logical part. don't split if SNI is not present\n"
" --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n" " --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n"
#ifdef __linux__ #ifdef __linux__
" --mss=<int>\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n" " --mss=<int>\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n"
@ -292,6 +292,8 @@ bool parse_tlspos(const char *s, enum tlspos *pos)
*pos = tlspos_sni; *pos = tlspos_sni;
else if (!strcmp(s, "sniext")) else if (!strcmp(s, "sniext"))
*pos = tlspos_sniext; *pos = tlspos_sniext;
else if (!strcmp(s, "snisld"))
*pos = tlspos_snisld;
else else
return false; return false;
return true; return true;