Automatic TTL value picker for --set-ttl Fake Packet mode

This is a per-connection (per-destination) automatic TTL adjusting feature.
Basically a --set-ttl mode where you don't need to set specific TTL value.

It works as follows:
 1. All incoming SYN/ACKs (the response to client's SYN) are intercepted
 2. TTL value is extracted from SYN/ACK
 3. New TTL is calculated with the simple formula:
    128 > extracted_ttl > 64: // Server is running Windows
      fakepacket_ttl = 128 - extracted_ttl - decrement
    64 > extracted_ttl > 34: // Server is running Linux/FreeBSD/other
      fakepacket_ttl = 64 - extracted_ttl - decrement
 4. Fake packet is sent

To comply with the multi-path multi-hop server connections
where 1 hop dispersion is not rare, decrement should be at least of
value "2", which is the default.

The patch does not process "too strange" TTL values (bigger than 128,
less than 34).
This commit is contained in:
ValdikSS 2021-12-25 11:13:50 +03:00
parent bbb6af89fe
commit 21ff80b43c
4 changed files with 330 additions and 14 deletions

View File

@ -1,3 +1,5 @@
#ifndef _DNSREDIR_H
#define _DNSREDIR_H
#include <stdint.h>
typedef struct conntrack_info {
@ -34,3 +36,4 @@ int dns_handle_outgoing(const uint32_t srcip[4], const uint16_t srcport,
void flush_dns_cache();
int dns_is_dns_packet(const char *packet_data, const UINT packet_dataLen, const int outgoing);
#endif

View File

@ -16,6 +16,7 @@
#include "utils/repl_str.h"
#include "service.h"
#include "dnsredir.h"
#include "ttltrack.h"
#include "blackwhitelist.h"
#include "fakepackets.h"
@ -94,6 +95,22 @@ WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pA
} \
} while (0)
#define TCP_HANDLE_OUTGOING_ADJUST_TTL() do { \
if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \
ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
&tcp_conn_info, 0)) \
|| \
(packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \
ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
&tcp_conn_info, 1))) \
{ \
ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, do_auto_ttl); \
if (do_tcp_verb) { \
printf("Connection TTL = %d, Fake TTL = %d\n", tcp_conn_info.ttl, ttl_of_fake_packet); \
} \
} \
} while (0)
static int running_from_service = 0;
static HANDLE filters[MAX_FILTERS];
static int filter_num = 0;
@ -124,6 +141,7 @@ static struct option long_options[] = {
{"blacklist", required_argument, 0, 'b' },
{"ip-id", required_argument, 0, 'i' },
{"set-ttl", required_argument, 0, '$' },
{"auto-ttl", optional_argument, 0, '+' },
{"wrong-chksum",no_argument, 0, '%' },
{"wrong-seq", no_argument, 0, ')' },
{"native-frag", no_argument, 0, '*' },
@ -442,6 +460,7 @@ int main(int argc, char *argv[]) {
PWINDIVERT_TCPHDR ppTcpHdr;
PWINDIVERT_UDPHDR ppUdpHdr;
conntrack_info_t dns_conn_info;
tcp_conntrack_info_t tcp_conn_info;
int do_passivedpi = 0, do_fragment_http = 0,
do_fragment_http_persistent = 0,
@ -451,8 +470,9 @@ int main(int argc, char *argv[]) {
do_http_allports = 0,
do_host_mixedcase = 0,
do_dnsv4_redirect = 0, do_dnsv6_redirect = 0,
do_dns_verb = 0, do_blacklist = 0,
do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0,
do_fake_packet = 0,
do_auto_ttl = 0,
do_wrong_chksum = 0,
do_wrong_seq = 0,
do_native_frag = 0, do_reverse_frag = 0;
@ -651,6 +671,7 @@ int main(int argc, char *argv[]) {
break;
case 'v':
do_dns_verb = 1;
do_tcp_verb = 1;
break;
case 'b':
do_blacklist = 1;
@ -663,6 +684,15 @@ int main(int argc, char *argv[]) {
do_fake_packet = 1;
ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!");
break;
case '+':
do_fake_packet = 1;
do_auto_ttl = 2;
if (optarg) {
do_auto_ttl = atoub(optarg, "Set Auto TTL parameter error!");
} else if (argv[optind] && argv[optind][0] != '-') {
do_auto_ttl = atoub(argv[optind], "Set Auto TTL parameter error!");
}
break;
case '%':
do_fake_packet = 1;
do_wrong_chksum = 1;
@ -705,7 +735,8 @@ int main(int argc, char *argv[]) {
" supplied text file. This option can be supplied multiple times.\n"
" --set-ttl [value] activate Fake Request Mode and send it with supplied TTL value.\n"
" DANGEROUS! May break websites in unexpected ways. Use with care.\n"
" Could be combined with --wrong-chksum.\n"
" --auto-ttl [decttl] activate Fake Request Mode, automatically detect TTL and decrease\n"
" it from standard 64 or 128 by decttl (128/64 - TTL - 2 by default).\n"
" --wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum.\n"
" May not work in a VM or with some routers, but is safer than set-ttl.\n"
" Could be combined with --set-ttl\n"
@ -736,7 +767,7 @@ int main(int argc, char *argv[]) {
"hoSt: %d\nHost no space: %d\nAdditional space: %d\n"
"Mix Host: %d\nHTTP AllPorts: %d\nHTTP Persistent Nowait: %d\n"
"DNS redirect: %d\nDNSv6 redirect: %d\n"
"Fake requests, TTL: %hu\nFake requests, wrong checksum: %d\n",
"Fake requests, TTL: %hu (auto: %hu)\nFake requests, wrong checksum: %d\n"
"Fake requests, wrong SEQ/ACK: %d\n",
do_passivedpi, (do_fragment_http ? http_fragment_size : 0),
(do_fragment_http_persistent ? http_fragment_size : 0),
@ -744,8 +775,8 @@ int main(int argc, char *argv[]) {
do_native_frag, do_reverse_frag,
do_host, do_host_removespace, do_additional_space, do_host_mixedcase,
do_http_allports, do_fragment_http_persistent_nowait, do_dnsv4_redirect,
do_dnsv6_redirect, ttl_of_fake_packet, do_wrong_chksum,
do_wrong_seq
do_dnsv6_redirect, ttl_of_fake_packet, do_auto_ttl,
do_wrong_chksum, do_wrong_seq
);
if (do_fragment_http && http_fragment_size > 2) {
@ -871,6 +902,9 @@ int main(int argc, char *argv[]) {
{
if (packet_dataLen >=2 && memcmp(packet_data, "\x16\x03", 2) == 0) {
if (do_fake_packet) {
if (do_auto_ttl) {
TCP_HANDLE_OUTGOING_ADJUST_TTL();
}
send_fake_https_request(w_filter, &addr, packet, packetLen, packet_v6,
ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq);
}
@ -906,7 +940,10 @@ int main(int argc, char *argv[]) {
should_recalc_checksum = 1;
}
if (do_fake_packet)
if (do_fake_packet) {
if (do_auto_ttl) {
TCP_HANDLE_OUTGOING_ADJUST_TTL();
}
send_fake_http_request(w_filter, &addr, packet, packetLen, packet_v6,
ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq);
}
@ -1022,20 +1059,36 @@ int main(int argc, char *argv[]) {
else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) {
/* If we got INBOUND SYN+ACK packet */
if (addr.Direction == WINDIVERT_DIRECTION_INBOUND &&
ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1 &&
!do_native_frag) {
ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) {
//printf("Changing Window Size!\n");
/*
* Window Size is changed even if do_fragment_http_persistent
* is enabled as there could be non-HTTP data on port 80
*/
if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {
change_window_size(ppTcpHdr, http_fragment_size);
should_recalc_checksum = 1;
if (do_fake_packet && do_auto_ttl) {
if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr,
ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
0, ppIpHdr->TTL))
||
(packet_v6 && tcp_handle_incoming(&ppIpV6Hdr->SrcAddr, &ppIpV6Hdr->DstAddr,
ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
1, ppIpV6Hdr->HopLimit))))
{
if (do_tcp_verb)
puts("[TCP WARN] Can't add TCP connection record.");
}
}
else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {
change_window_size(ppTcpHdr, https_fragment_size);
should_recalc_checksum = 1;
if (!do_native_frag) {
if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {
change_window_size(ppTcpHdr, http_fragment_size);
should_recalc_checksum = 1;
}
else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {
change_window_size(ppTcpHdr, https_fragment_size);
should_recalc_checksum = 1;
}
}
}
}

235
src/ttltrack.c Normal file
View File

@ -0,0 +1,235 @@
/**
* TCP (TTL) Connection Tracker for GoodbyeDPI
*
* Monitors SYN/ACK only, to extract the TTL value of the remote server.
*
*/
#include <windows.h>
#include <time.h>
#include <stdio.h>
#include "goodbyedpi.h"
#include "ttltrack.h"
#include "utils/uthash.h"
/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */
#define TCP_CONNRECORD_KEY_LEN 37
#define TCP_CLEANUP_INTERVAL_SEC 30
/* HACK!
* uthash uses strlen() for HASH_FIND_STR.
* We have null bytes in our key, so we can't use strlen()
* And since it's always TCP_CONNRECORD_KEY_LEN bytes long,
* we don't need to use any string function to determine length.
*/
#undef uthash_strlen
#define uthash_strlen(s) TCP_CONNRECORD_KEY_LEN
typedef struct tcp_connrecord {
/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */
char key[TCP_CONNRECORD_KEY_LEN];
time_t time; /* time when this record was added */
uint16_t ttl;
UT_hash_handle hh; /* makes this structure hashable */
} tcp_connrecord_t;
static time_t last_cleanup = 0;
static tcp_connrecord_t *conntrack = NULL;
inline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4],
const uint32_t dstip[4], const uint16_t srcport, const uint16_t dstport)
{
int offset = 0;
if (is_ipv6) {
*(uint8_t*)(key) = '6';
offset += sizeof(uint8_t);
ipv6_copy_addr((uint32_t*)(key + offset), srcip);
offset += sizeof(uint32_t) * 4;
ipv6_copy_addr((uint32_t*)(key + offset), dstip);
offset += sizeof(uint32_t) * 4;
}
else {
*(uint8_t*)(key) = '4';
offset += sizeof(uint8_t);
ipv4_copy_addr((uint32_t*)(key + offset), srcip);
offset += sizeof(uint32_t) * 4;
ipv4_copy_addr((uint32_t*)(key + offset), dstip);
offset += sizeof(uint32_t) * 4;
}
*(uint16_t*)(key + offset) = srcport;
offset += sizeof(srcport);
*(uint16_t*)(key + offset) = dstport;
offset += sizeof(dstport);
}
inline static void fill_data_from_key(uint8_t *is_ipv6, uint32_t srcip[4], uint32_t dstip[4],
uint16_t *srcport, uint16_t *dstport, const char *key)
{
int offset = 0;
if (key[0] == '6') {
*is_ipv6 = 1;
offset += sizeof(uint8_t);
ipv6_copy_addr(srcip, (uint32_t*)(key + offset));
offset += sizeof(uint32_t) * 4;
ipv6_copy_addr(dstip, (uint32_t*)(key + offset));
offset += sizeof(uint32_t) * 4;
}
else {
*is_ipv6 = 0;
offset += sizeof(uint8_t);
ipv4_copy_addr(srcip, (uint32_t*)(key + offset));
offset += sizeof(uint32_t) * 4;
ipv4_copy_addr(dstip, (uint32_t*)(key + offset));
offset += sizeof(uint32_t) * 4;
}
*srcport = *(uint16_t*)(key + offset);
offset += sizeof(*srcport);
*dstport = *(uint16_t*)(key + offset);
offset += sizeof(*dstport);
}
inline static void construct_key(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
char *key, const uint8_t is_ipv6)
{
debug("Construct key enter\n");
if (key) {
debug("Constructing key\n");
fill_key_data(key, is_ipv6, srcip, dstip, srcport, dstport);
}
debug("Construct key end\n");
}
inline static void deconstruct_key(const char *key, const tcp_connrecord_t *connrecord,
tcp_conntrack_info_t *conn_info)
{
debug("Deconstruct key enter\n");
if (key && conn_info) {
debug("Deconstructing key\n");
fill_data_from_key(&conn_info->is_ipv6,
conn_info->srcip, conn_info->dstip,
&conn_info->srcport, &conn_info->dstport,
key);
conn_info->ttl = connrecord->ttl;
}
debug("Deconstruct key end\n");
}
static int check_get_tcp_conntrack_key(const char *key, tcp_connrecord_t **connrecord) {
tcp_connrecord_t *tmp_connrecord = NULL;
if (!conntrack) return FALSE;
HASH_FIND_STR(conntrack, key, tmp_connrecord);
if (tmp_connrecord) {
if (connrecord)
*connrecord = tmp_connrecord;
debug("check_get_tcp_conntrack_key found key\n");
return TRUE;
}
debug("check_get_tcp_conntrack_key key not found\n");
return FALSE;
}
static int add_tcp_conntrack(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
const uint8_t is_ipv6, const uint8_t ttl
)
{
if (!(srcip && srcport && dstip && dstport))
return FALSE;
tcp_connrecord_t *tmp_connrecord = malloc(sizeof(tcp_connrecord_t));
construct_key(srcip, dstip, srcport, dstport, tmp_connrecord->key, is_ipv6);
if (!check_get_tcp_conntrack_key(tmp_connrecord->key, NULL)) {
tmp_connrecord->time = time(NULL);
tmp_connrecord->ttl = ttl;
HASH_ADD_STR(conntrack, key, tmp_connrecord);
debug("Added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));
return TRUE;
}
debug("Not added TCP conntrack %u:%hu - %u:%hu\n", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));
free(tmp_connrecord);
return FALSE;
}
static void tcp_cleanup() {
tcp_connrecord_t *tmp_connrecord, *tmp_connrecord2 = NULL;
if (last_cleanup == 0) {
last_cleanup = time(NULL);
return;
}
if (difftime(time(NULL), last_cleanup) >= TCP_CLEANUP_INTERVAL_SEC) {
last_cleanup = time(NULL);
HASH_ITER(hh, conntrack, tmp_connrecord, tmp_connrecord2) {
if (difftime(last_cleanup, tmp_connrecord->time) >= TCP_CLEANUP_INTERVAL_SEC) {
HASH_DEL(conntrack, tmp_connrecord);
free(tmp_connrecord);
}
}
}
}
int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
const uint8_t is_ipv6, const uint8_t ttl)
{
tcp_cleanup();
debug("trying to add TCP srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport));
return add_tcp_conntrack(srcip, dstip, srcport, dstport, is_ipv6, ttl);
debug("____tcp_handle_incoming FALSE: srcport = %hu, dstport = %hu\n", ntohs(srcport), ntohs(dstport));
return FALSE;
}
int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
tcp_conntrack_info_t *conn_info,
const uint8_t is_ipv6)
{
char key[TCP_CONNRECORD_KEY_LEN];
tcp_connrecord_t *tmp_connrecord = NULL;
if (!conn_info)
return FALSE;
tcp_cleanup();
construct_key(dstip, srcip, dstport, srcport, key, is_ipv6);
if (check_get_tcp_conntrack_key(key, &tmp_connrecord) && tmp_connrecord) {
/* Connection exists in conntrack, moving on */
deconstruct_key(key, tmp_connrecord, conn_info);
HASH_DEL(conntrack, tmp_connrecord);
free(tmp_connrecord);
debug("____tcp_handle_outgoing TRUE: srcport = %hu\n", ntohs(srcport));
return TRUE;
}
debug("____tcp_handle_outgoing FALSE: srcport = %hu\n", ntohs(srcport));
return FALSE;
}
int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for) {
uint8_t ttl_of_fake_packet = 0;
if (ttl > 64 && ttl < 128) {
ttl_of_fake_packet = 128 - ttl - decrease_for;
}
else if (ttl > 34 && ttl < 64) {
ttl_of_fake_packet = 64 - ttl - decrease_for;
}
else {
ttl_of_fake_packet = 0;
}
return ttl_of_fake_packet;
}

25
src/ttltrack.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _TTLTRACK_H
#define _TTLTRACK_H
#include <stdint.h>
#include "dnsredir.h"
typedef struct tcp_conntrack_info {
uint8_t is_ipv6;
uint8_t ttl;
uint32_t srcip[4];
uint16_t srcport;
uint32_t dstip[4];
uint16_t dstport;
} tcp_conntrack_info_t;
int tcp_handle_incoming(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
const uint8_t is_ipv6, const uint8_t ttl);
int tcp_handle_outgoing(const uint32_t srcip[4], const uint32_t dstip[4],
const uint16_t srcport, const uint16_t dstport,
tcp_conntrack_info_t *conn_info,
const uint8_t is_ipv6);
int tcp_get_auto_ttl(const uint8_t ttl, const uint8_t decrease_for);
#endif