nfqws: combine desync and tamper logic

This commit is contained in:
bol-van 2020-02-03 12:46:08 +03:00
parent b430221cd6
commit c2c8c83c03
16 changed files with 98 additions and 121 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -119,7 +119,7 @@ uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8
}
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
void tcp4_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr)
{
tcp->check = 0;
tcp->check = csum_tcpudp_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp, len));
@ -129,3 +129,10 @@ void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_ad
tcp->check = 0;
tcp->check = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp, len));
}
void tcp_fix_checksum(struct tcphdr *tcp,int len,const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr)
{
if (iphdr)
tcp4_fix_checksum(tcp, len, iphdr->saddr, iphdr->daddr);
else if (ip6hdr)
tcp6_fix_checksum(tcp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst);
}

View File

@ -12,5 +12,6 @@ uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8
uint16_t ip4_compute_csum(const void *buff, size_t len);
void ip4_fix_checksum(struct iphdr *ip);
void tcp_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr);
void tcp4_fix_checksum(struct tcphdr *tcp,int len, in_addr_t src_addr, in_addr_t dest_addr);
void tcp6_fix_checksum(struct tcphdr *tcp,int len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
void tcp_fix_checksum(struct tcphdr *tcp,int len,const struct iphdr *iphdr,const struct ip6_hdr *ip6hdr);

View File

@ -192,7 +192,7 @@ bool prepare_tcp_segment4(
fill_tcphdr(tcp,tcp_flags,seq,ack_seq,fooling,src->sin_port,dst->sin_port,wsize,timestamps);
memcpy((char*)tcp+sizeof(struct tcphdr)+tcpoptlen,data,len);
tcp_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,ip->saddr,ip->daddr);
tcp4_fix_checksum(tcp,sizeof(struct tcphdr)+tcpoptlen+len,ip->saddr,ip->daddr);
if (fooling & TCP_FOOL_BADSUM) tcp->check^=0xBEAF;
*buflen = pktlen;

View File

@ -1,5 +1,7 @@
#pragma once
#include "checksum.h"
#include <stdint.h>
#include <stdbool.h>
#include <netinet/ip.h>
@ -7,7 +9,7 @@
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "checksum.h"
// returns netorder value
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment);

View File

@ -58,9 +58,12 @@ void desync_init()
// result : true - drop original packet, false = dont drop
bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, uint8_t *data_payload, size_t len_payload)
packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload)
{
if (!!iphdr == !!ip6hdr) return false; // one and only one must be present
packet_process_result res=pass;
if (!!iphdr == !!ip6hdr) return res; // one and only one must be present
if (params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace) return res; // nothing to do. do not waste cpu
if (!tcphdr->syn && len_payload)
{
@ -69,8 +72,9 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
size_t fake_size;
char host[256];
bool bHaveHost=false;
bool bIsHttp;
if (IsHttp(data_payload,len_payload))
if (bIsHttp = IsHttp(data_payload,len_payload))
{
DLOG("packet contains HTTP request\n")
fake = (uint8_t*)fake_http_request;
@ -78,8 +82,8 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
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;
DLOG("not applying tampering to HTTP without Host:\n")
return res;
}
}
else if (IsTLSClientHello(data_payload,len_payload))
@ -92,16 +96,15 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
bHaveHost=TLSHelloExtractHost(data_payload,len_payload,host,sizeof(host));
if (params.desync_skip_nosni && !bHaveHost)
{
DLOG("not applying dpi-desync to TLS ClientHello without hostname in the SNI\n")
return false;
DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n")
return res;
}
}
}
else
{
if (!params.desync_any_proto) return false;
DLOG("applying dpi-desync to unknown protocol\n")
if (!params.desync_any_proto) return res;
DLOG("applying tampering to unknown protocol\n")
fake = zeropkt;
fake_size = 256;
}
@ -111,11 +114,42 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
DLOG("hostname: %s\n",host)
if (params.hostlist && !SearchHostList(params.hostlist,host,params.debug))
{
DLOG("not applying dpi-desync to this request\n")
return false;
DLOG("not applying tampering to this request\n")
return res;
}
uint8_t *phost;
if (bIsHttp && (params.hostcase || params.hostnospace) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\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);
res=modify;
}
uint8_t *pua;
if (params.hostnospace &&
(pua = (uint8_t*)memmem(data_payload, len_payload, "\r\nUser-Agent: ", 14)) &&
(pua = (uint8_t*)memmem(pua + 1, len_payload - (pua - data_payload) - 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 = ' ';
}
res=modify;
}
}
}
if (params.desync_mode==DESYNC_NONE) return res;
extract_endpoints(iphdr, ip6hdr, tcphdr, &src, &dst);
if (params.debug)
{
@ -150,7 +184,7 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
data_payload+split_pos, len_payload-split_pos, newdata, &newlen) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
{
return false;
return res;
}
}
@ -164,7 +198,7 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
zeropkt, split_pos, fakeseg, &fakeseg_len) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
{
return false;
return res;
}
}
@ -176,17 +210,17 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
data_payload, split_pos, newdata, &newlen) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
{
return false;
return res;
}
if (params.desync_mode==DESYNC_DISORDER)
{
DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos)
if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return false;
return res;
}
return true;
return drop;
}
break;
case DESYNC_SPLIT:
@ -205,7 +239,7 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
zeropkt, split_pos, fakeseg, &fakeseg_len) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
{
return false;
return res;
}
}
@ -216,14 +250,14 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
data_payload, split_pos, newdata, &newlen) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
{
return false;
return res;
}
if (params.desync_mode==DESYNC_SPLIT)
{
DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu\n",split_pos-1, split_pos)
if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, fakeseg, fakeseg_len))
return false;
return res;
}
if (split_pos<len_payload)
@ -235,11 +269,11 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
data_payload+split_pos, len_payload-split_pos, newdata, &newlen) ||
!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
{
return false;
return res;
}
}
return true;
return drop;
}
break;
@ -248,7 +282,7 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
ttl_fake,params.desync_tcp_fooling_mode,
fake, fake_size, newdata, &newlen))
{
return false;
return res;
}
break;
case DESYNC_RST:
@ -257,27 +291,29 @@ bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *ip
ttl_fake,params.desync_tcp_fooling_mode,
NULL, 0, newdata, &newlen))
{
return false;
return res;
}
break;
default:
return false;
return res;
}
if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, newdata, newlen))
return false;
return res;
if (params.desync_retrans)
DLOG("dropping packet to force retransmission. len=%zu len_payload=%zu\n", len_pkt, len_payload)
else
{
DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", len_pkt, len_payload)
// if original packet was tampered earlier it needs checksum fixed
if (res==modify) tcp_fix_checksum(tcphdr,len_tcp,iphdr,ip6hdr);
if (!rawsend((struct sockaddr *)&dst, params.desync_fwmark, data_pkt, len_pkt))
return false;
return res;
}
return true;
return drop;
}
return false;
return res;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "darkmagic.h"
#include "nfqws.h"
#include <stdint.h>
#include <stdbool.h>
@ -24,4 +25,4 @@ enum dpi_desync_mode {
};
void desync_init();
bool dpi_desync_packet(const uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, uint8_t *data_payload, size_t len_payload);
packet_process_result dpi_desync_packet(uint8_t *data_pkt, size_t len_pkt, struct iphdr *iphdr, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t len_tcp, uint8_t *data_payload, size_t len_payload);

View File

@ -1,5 +1,6 @@
#define _GNU_SOURCE
#include "nfqws.h"
#include "sec.h"
#include "desync.h"
#include "helpers.h"
@ -138,17 +139,6 @@ static inline bool tcp_synack_segment(const struct tcphdr *tcphdr)
tcphdr->syn == 1 &&
tcphdr->fin == 0;
}
static inline bool tcp_ack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
return tcphdr->urg == 0 &&
tcphdr->ack == 1 &&
tcphdr->rst == 0 &&
tcphdr->syn == 0 &&
tcphdr->fin == 0;
}
static void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize)
{
uint16_t winsize_old;
@ -157,77 +147,19 @@ static void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize)
DLOG("Window size change %u => %u\n", winsize_old, winsize)
}
// data/len points to data payload
static bool modify_tcp_packet(uint8_t *data, size_t len, struct tcphdr *tcphdr)
{
const char **method;
size_t method_len = 0;
uint8_t *phost, *pua;
bool bRet = false;
if (tcp_synack_segment(tcphdr))
if (tcp_synack_segment(tcphdr) && params.wsize)
{
if (params.wsize)
{
tcp_rewrite_winsize(tcphdr, (uint16_t)params.wsize);
bRet = true;
}
tcp_rewrite_winsize(tcphdr, (uint16_t)params.wsize);
return true;
}
else if ((params.hostcase || params.hostnospace) && IsHttp(data,len))
{
if (params.hostlist)
{
char host[256];
if (HttpExtractHost(data,len,host,sizeof(host)))
{
DLOG("hostname: %s\n",host)
if (!SearchHostList(params.hostlist,host,params.debug))
{
DLOG("not applying tampering to this request\n")
return false;
}
}
else
{
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;
}
}
}
return bRet;
return false;
}
typedef enum
{
pass = 0, modify, drop
} packet_process_result;
static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt, uint32_t *mark)
{
struct iphdr *iphdr = NULL;
@ -235,7 +167,7 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt
struct tcphdr *tcphdr = NULL;
size_t len = len_pkt, len_tcp;
uint8_t *data = data_pkt;
packet_process_result res = pass;
packet_process_result res = pass, res2;
uint8_t proto;
if (*mark & params.desync_fwmark) return res;
@ -266,18 +198,10 @@ static packet_process_result processPacketData(uint8_t *data_pkt, size_t len_pkt
if (modify_tcp_packet(data, len, tcphdr))
res = modify;
if (params.desync_mode!=DESYNC_NONE)
{
if (dpi_desync_packet(data_pkt, len_pkt, iphdr, ip6hdr, tcphdr, data, len))
res = drop;
}
if (res==modify)
{
if (iphdr)
tcp_fix_checksum(tcphdr, len_tcp, iphdr->saddr, iphdr->daddr);
else
tcp6_fix_checksum(tcphdr, len_tcp, &ip6hdr->ip6_src, &ip6hdr->ip6_dst);
}
res2 = dpi_desync_packet(data_pkt, len_pkt, iphdr, ip6hdr, tcphdr, len_tcp, data, len);
res = (res2==pass && res==modify) ? modify : res2;
if (res==modify) tcp_fix_checksum(tcphdr,len_tcp,iphdr,ip6hdr);
}
return res;
}
@ -320,7 +244,7 @@ static void exithelp()
" --pidfile=<filename>\t\t\t; write pid to file\n"
" --user=<username>\t\t\t; drop root privs\n"
" --uid=uid[:gid]\t\t\t; drop root privs\n"
" --wsize=<window_size>\t\t\t; set window size. 0 = do not modify\n"
" --wsize=<window_size>\t\t\t; set window size. 0 = do not modify. OBSOLETE !\n"
" --hostcase\t\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostnospace\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"

6
nfq/nfqws.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
typedef enum
{
pass = 0, modify, drop
} packet_process_result;