diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index abb7a5a..2ab321a 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/aarch64/tpws b/binaries/aarch64/tpws index 75bad7e..2bd251e 100755 Binary files a/binaries/aarch64/tpws and b/binaries/aarch64/tpws differ diff --git a/binaries/armhf/nfqws b/binaries/armhf/nfqws index 9e27233..96fbb7b 100755 Binary files a/binaries/armhf/nfqws and b/binaries/armhf/nfqws differ diff --git a/binaries/armhf/tpws b/binaries/armhf/tpws index b882edc..73726ed 100755 Binary files a/binaries/armhf/tpws and b/binaries/armhf/tpws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index 324a9e0..79d3a82 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-lsb/tpws b/binaries/mips32r1-lsb/tpws index 4cd4b98..6316318 100755 Binary files a/binaries/mips32r1-lsb/tpws and b/binaries/mips32r1-lsb/tpws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 80b05f3..d92b077 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips32r1-msb/tpws b/binaries/mips32r1-msb/tpws index 6b9382e..3ebabd2 100755 Binary files a/binaries/mips32r1-msb/tpws and b/binaries/mips32r1-msb/tpws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index e7ddce9..20bacca 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/mips64r2-msb/tpws b/binaries/mips64r2-msb/tpws index 7c3a9b9..efc02ff 100755 Binary files a/binaries/mips64r2-msb/tpws and b/binaries/mips64r2-msb/tpws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index 79ee035..ce03b29 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/ppc/tpws b/binaries/ppc/tpws index 5143787..82790d8 100755 Binary files a/binaries/ppc/tpws and b/binaries/ppc/tpws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index ee6ebcf..a2ec476 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86/tpws b/binaries/x86/tpws index 44457db..829e735 100755 Binary files a/binaries/x86/tpws and b/binaries/x86/tpws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index 75f985d..cc64236 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/binaries/x86_64/tpws b/binaries/x86_64/tpws index c7dd1ac..4b797ae 100755 Binary files a/binaries/x86_64/tpws and b/binaries/x86_64/tpws differ diff --git a/docs/readme.eng.txt b/docs/readme.eng.txt index c3ab773..6f8fe27 100644 --- a/docs/readme.eng.txt +++ b/docs/readme.eng.txt @@ -135,7 +135,7 @@ It takes the following parameters: --dpi-desync-skip-nosni=0|1 ; 1(default)=do not apply desync to requests without hostname in the SNI --dpi-desync-split-pos=<1..1500> ; (for disorder only) split TCP packet at specified position --dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet - --hostlist= ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply) + --hostlist= ; apply fooling only to the listed hosts (one host per line, subdomains auto apply) The manipulation parameters can be combined in any way. @@ -189,7 +189,7 @@ Split mode is very similar to disorder but without segment reordering : 4. 2nd segment Mode 'split2' disables sending of fake segments. It can be used as a faster alternative to --wsize. -In disorder2 and split2 modes no fake packets are sent, so no fooling options are required. +In disorder2 and split2 modes no fake packets are sent, so ttl and fooling options are not required. There are DPIs that analyze responses from the server, particularly the certificate from the ServerHello that contain domain name(s). The ClientHello delivery confirmation is an ACK packet from the server @@ -204,7 +204,6 @@ doing something about it is hardly possible without the help of the server. The best solution is to enable TLS 1.3 support on the server. TLS 1.3 sends the server certificate in encrypted form. This is recommendation to all admins of blocked sites. Enable TLS 1.3. You will give more opportunities to overcome DPI. -Hostlist is applicable only to desync attack. It does not work for other options. Hosts are extracted from plain http request Host: header and SNI of ClientHelllo TLS message. Subdomains are applied automatically. gzip lists are supported. diff --git a/docs/readme.txt b/docs/readme.txt index 2d579e5..42a33bb 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -166,7 +166,7 @@ nfqws --dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI --dpi-desync-split-pos=<1..1500> ; (только для disorder) разбивать пакет на указанной позиции --dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных - --hostlist= ; применять dpi-desync только к хостам из листа + --hostlist= ; применять дурение только к хостам из листа Параметры манипуляции могут сочетаться в любых комбинациях. @@ -224,7 +224,7 @@ nfqws Режим split2 отключает отправку поддельных частей. Он может быть использован как более быстрая альтернатива --wsize. -disorder2 и split2 не предполагают отсылку фейк пакетов, поэтому опции дурения неактуальны. +disorder2 и split2 не предполагают отсылку фейк пакетов, поэтому опции ttl и fooling неактуальны. Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены. Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1. @@ -239,7 +239,6 @@ DPI может отстать от потока, если ClientHello его у Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде. Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI. -hostlist относится только к атаке desync. он не работает для других параметров. при попытке запустить nfqws с hostlist и без dpi-desync будет ошибка. Хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello. Субдомены учитываются автоматически. Поддерживаются листы gzip. diff --git a/nfq/desync.c b/nfq/desync.c index c95bc47..563e24c 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -76,6 +76,11 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip fake = (uint8_t*)fake_http_request; fake_size = sizeof(fake_http_request); if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host)); + if (params.hostlist && !bHaveHost) + { + DLOG("not applying dpi-desync to HTTP without Host:\n") + return false; + } } else if (IsTLSClientHello(data_payload,len_payload)) { diff --git a/nfq/helpers.c b/nfq/helpers.c index 6299a79..32e535e 100644 --- a/nfq/helpers.c +++ b/nfq/helpers.c @@ -1,29 +1,9 @@ +#define _GNU_SOURCE + #include "helpers.h" -#include #include - -const uint8_t *find_bin_const(const uint8_t *data, size_t len, const void *blk, size_t blk_len) -{ - while (len >= blk_len) - { - if (!memcmp(data, blk, blk_len)) - return data; - data++; - len--; - } - return NULL; -} -uint8_t *find_bin(uint8_t *data, size_t len, const void *blk, size_t blk_len) -{ - while (len >= blk_len) - { - if (!memcmp(data, blk, blk_len)) - return data; - data++; - len--; - } - return NULL; -} +#include +#include void print_sockaddr(const struct sockaddr *sa) { @@ -42,3 +22,24 @@ void print_sockaddr(const struct sockaddr *sa) printf("UNKNOWN_FAMILY_%d", sa->sa_family); } } + +char *strncasestr(const char *s,const char *find, size_t slen) +{ + char c, sc; + size_t len; + + if ((c = *find++) != '\0') + { + len = strlen(find); + do + { + do + { + if (slen-- < 1 || (sc = *s++) == '\0') return NULL; + } while (toupper(c) != toupper(sc)); + if (len > slen) return NULL; + } while (strncasecmp(s, find, len) != 0); + s--; + } + return (char *)s; +} diff --git a/nfq/helpers.h b/nfq/helpers.h index 0dd62e3..1fef189 100644 --- a/nfq/helpers.h +++ b/nfq/helpers.h @@ -1,10 +1,7 @@ #pragma once -#include -#include -#include #include +#include -const uint8_t *find_bin_const(const uint8_t *data, size_t len, const void *blk, size_t blk_len); -uint8_t *find_bin(uint8_t *data, size_t len, const void *blk, size_t blk_len); void print_sockaddr(const struct sockaddr *sa); +char *strncasestr(const char *s,const char *find, size_t slen); diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 09b49f1..53b115e 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -5,6 +5,7 @@ #include "helpers.h" #include "checksum.h" #include "params.h" +#include "protocol.h" #include "hostlist.h" #include @@ -165,34 +166,57 @@ static bool modify_tcp_packet(uint8_t *data, size_t len, struct tcphdr *tcphdr) uint8_t *phost, *pua; bool bRet = false; - if (params.wsize && tcp_synack_segment(tcphdr)) + if (tcp_synack_segment(tcphdr)) { - tcp_rewrite_winsize(tcphdr, (uint16_t)params.wsize); - bRet = true; - } - - if ((params.hostcase || params.hostnospace) && (phost = find_bin(data, len, "\r\nHost: ", 8))) - { - if (params.hostcase) + if (params.wsize) { - DLOG("modifying Host: => %c%c%c%c:\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3]) - memcpy(phost + 2, params.hostspell, 4); + tcp_rewrite_winsize(tcphdr, (uint16_t)params.wsize); bRet = true; } - if (params.hostnospace && (pua = find_bin(data, len, "\r\nUser-Agent: ", 14)) && (pua = find_bin(pua + 1, len - (pua - data) - 1, "\r\n", 2))) + } + else if ((params.hostcase || params.hostnospace) && IsHttp(data,len)) + { + if (params.hostlist) { - DLOG("removing space after Host: and adding it to User-Agent:\n") - if (pua > phost) + char host[256]; + if (HttpExtractHost(data,len,host,sizeof(host))) { - memmove(phost + 7, phost + 8, pua - phost - 8); - phost[pua - phost - 1] = ' '; + DLOG("hostname: %s\n",host) + if (!SearchHostList(params.hostlist,host,params.debug)) + { + DLOG("not applying tampering to this request\n") + return false; + } } else { - memmove(pua + 1, pua, phost - pua + 7); - *pua = ' '; + DLOG("could not extract host from http request. not applying tampering\n") + return false; + } + } + if (phost = (uint8_t*)memmem(data, len, "\r\nHost: ", 8)) + { + if (params.hostcase) + { + DLOG("modifying Host: => %c%c%c%c:\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3]) + memcpy(phost + 2, params.hostspell, 4); + bRet = true; + } + if (params.hostnospace && (pua = (uint8_t*)memmem(data, len, "\r\nUser-Agent: ", 14)) && (pua = (uint8_t*)memmem(pua + 1, len - (pua - data) - 1, "\r\n", 2))) + { + DLOG("removing space after Host: and adding it to User-Agent:\n") + if (pua > phost) + { + memmove(phost + 7, phost + 8, pua - phost - 8); + phost[pua - phost - 1] = ' '; + } + else + { + memmove(pua + 1, pua, phost - pua + 7); + *pua = ' '; + } + bRet = true; } - bRet = true; } } return bRet; @@ -531,12 +555,6 @@ int main(int argc, char **argv) } } - if (params.desync_mode==DESYNC_NONE && params.hostlist) - { - fprintf(stderr, "hostlist is applicable only to dpi-desync\n"); - exit_clean(1); - } - if (daemon) daemonize(); h = NULL; diff --git a/nfq/protocol.c b/nfq/protocol.c index b0625c2..b8f0335 100644 --- a/nfq/protocol.c +++ b/nfq/protocol.c @@ -1,8 +1,11 @@ +#define _GNU_SOURCE + #include "protocol.h" #include "helpers.h" #include #include #include +#include const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; bool IsHttp(const char *data, size_t len) @@ -21,7 +24,7 @@ bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_hos { const uint8_t *p, *s, *e=data+len; - p = find_bin_const(data, len, "\nHost:", 6); + p = (uint8_t*)strncasestr((char*)data, "\nHost:", len); if (!p) return false; p+=6; while(p #include -char *find_bin(void *data, size_t len, const void *blk, size_t blk_len) -{ - while (len >= blk_len) - { - if (!memcmp(data, blk, blk_len)) - return data; - data = (char*)data + 1; - len--; - } - return NULL; -} - // pHost points to "Host: ..." bool find_host(char **pHost,char *buf,size_t bs) { if (!*pHost) { - *pHost = find_bin(buf, bs, "\nHost:", 6); + *pHost = memmem(buf, bs, "\nHost:", 6); if (*pHost) { (*pHost)++; @@ -74,7 +64,7 @@ void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,si if (params.unixeol) { p = pp = segment; - while (p = find_bin(p, segment + *size - p, "\r\n", 2)) + while (p = memmem(p, segment + *size - p, "\r\n", 2)) { *p = '\n'; p++; memmove(p, p + 1, segment + *size - p - 1); diff --git a/tpws/tamper.h b/tpws/tamper.h index 09fde2e..6cc49bc 100644 --- a/tpws/tamper.h +++ b/tpws/tamper.h @@ -3,6 +3,5 @@ #include #include -char *find_bin(void *data, size_t len, const void *blk, size_t blk_len); bool find_host(char **pHost,char *buf,size_t bs); void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos); diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index 71bc65d..3868378 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -98,7 +98,6 @@ static int get_so_error(int fd) socklen_t optlen = sizeof(errn); if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &errn, &optlen) == -1) errn=errno; - return errn; } static bool proxy_remote_conn_ack(tproxy_conn_t *conn, int sock_err)