From 044801efb95acdd00f17d8ef0707fee64eb5b3c4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 15 Aug 2024 02:31:48 +0300 Subject: [PATCH] Add support for bruteforce mode of parsing SNI from Client Hello. --- README.md | 2 ++ args.c | 19 +++++++++++++++ config.h | 5 +++- mangle.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- types.h | 14 +++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bc18c7a..184415d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ Available flags: - `--fk-winsize=` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization. +- `--sni-detection={parse|brute}` Specifies how to detect SNI. Parse will normally detect it by parsing the Client Hello message. Brute will go through the entire message and check possibility of SNI occurrence. Please note, that when `--sni-domains` option is not all brute will be O(nm) time complexity where n stands for length of the message and m is number of domains. Defaults to parse. + - `--seg2delay=` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet. - `--silent` Disables verbose mode. diff --git a/args.c b/args.c index 46677e6..a723ae7 100644 --- a/args.c +++ b/args.c @@ -19,6 +19,8 @@ struct config_t config = { .fake_sni = 1, .fake_sni_seq_len = 1, + .sni_detection = SNI_DETECTION_PARSE, + #ifdef SEG2_DELAY .seg2_delay = SEG2_DELAY, #else @@ -56,6 +58,7 @@ struct config_t config = { #define OPT_FK_WINSIZE 14 #define OPT_TRACE 15 #define OPT_QUIC_DROP 16 +#define OPT_SNI_DETECTION 17 #define OPT_SEG2DELAY 5 #define OPT_THREADS 6 #define OPT_SILENT 7 @@ -77,6 +80,7 @@ static struct option long_opt[] = { {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"quic-drop", 0, 0, OPT_QUIC_DROP}, + {"sni-detection", 1, 0, OPT_SNI_DETECTION}, {"seg2delay", 1, 0, OPT_SEG2DELAY}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, @@ -126,6 +130,7 @@ void print_usage(const char *argv0) { printf("\t--frag-sni-faked={0|1}\n"); printf("\t--fk-winsize=\n"); printf("\t--quic-drop\n"); + printf("\t--sni-detection={parse|brute}\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); printf("\t--silent\n"); @@ -166,6 +171,16 @@ int parse_args(int argc, char *argv[]) { config.domains_str = optarg; config.domains_strlen = strlen(config.domains_str); + break; + case OPT_SNI_DETECTION: + if (strcmp(optarg, "parse") == 0) { + config.sni_detection = SNI_DETECTION_PARSE; + } else if (strcmp(optarg, "brute") == 0) { + config.sni_detection = SNI_DETECTION_BRUTE; + } else { + goto invalid_opt; + } + break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { @@ -346,6 +361,10 @@ void print_welcome() { printf("All QUIC packets will be dropped\n"); } + if (config.sni_detection == SNI_DETECTION_BRUTE) { + printf("Server Name Extension will be parsed in the bruteforce mode\n"); + } + if (config.all_domains) { printf("All Client Hello will be targetted by youtubeUnblock!\n"); } diff --git a/config.h b/config.h index 2769176..98ca573 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,9 @@ struct config_t { #define VERBOSE_TRACE 2 int verbose; int quic_drop; +#define SNI_DETECTION_PARSE 0 +#define SNI_DETECTION_BRUTE 1 + int sni_detection; /* In milliseconds */ unsigned int seg2_delay; const char *domains_str; @@ -89,7 +92,7 @@ extern struct config_t config; // The Maximum Transmission Unit size for rawsocket // Larger packets will be fragmented. Applicable for Chrome's kyber. -#define AVAILABLE_MTU 1384 +#define AVAILABLE_MTU 1500 #define DEFAULT_QUEUE_NUM 537 diff --git a/mangle.c b/mangle.c index 0d7c60d..5f50bf9 100644 --- a/mangle.c +++ b/mangle.c @@ -428,7 +428,25 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, return 0; } +void z_function(const char *str, int *zbuf, size_t len) { + zbuf[0] = len; + ssize_t lh = 0, rh = 1; + for (ssize_t i = 1; i < len; i++) { + zbuf[i] = 0; + if (i < rh) { + zbuf[i] = min(zbuf[i - lh], rh - i); + } + + while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) + zbuf[i]++; + + if (i + zbuf[i] > rh) { + lh = i; + rh = i + zbuf[i]; + } + } +} #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 @@ -463,12 +481,16 @@ struct tls_verdict analyze_tls_data( uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); const uint8_t *message_length_ptr = msgData + 3; + if (tls_vmajor != 0x03) goto nextMessage; - if (i + 5 + message_length > dlen) break; + if (i + 5 > dlen) break; if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; + if (config.sni_detection == SNI_DETECTION_BRUTE) { + goto brute; + } const uint8_t *handshakeProto = msgData + 5; @@ -506,7 +528,7 @@ struct tls_verdict analyze_tls_data( const uint8_t *extensionsPtr = msgPtr; const uint8_t *extensions_end = extensionsPtr + extensionsLen; - if (extensions_end > data_end) break; + if (extensions_end > data_end) extensions_end = data_end; while (extensionsPtr < extensions_end) { const uint8_t *extensionPtr = extensionsPtr; @@ -590,8 +612,54 @@ nextMessage: out: return vrd; + +brute: + if (config.all_domains) { + vrd.target_sni = 1; + vrd.sni_len = 0; + vrd.sni_offset = dlen / 2; + goto out; + } + + unsigned int j = 0; + for (unsigned int i = 0; i <= config.domains_strlen; i++) { + if ( i > j && + (i == config.domains_strlen || + config.domains_str[i] == '\0' || + config.domains_str[i] == ',' || + config.domains_str[i] == '\n' )) { + + uint8_t buf[MAX_PACKET_SIZE]; + int zbuf[MAX_PACKET_SIZE]; + unsigned int domain_len = (i - j); + const char *domain_startp = config.domains_str + j; + + if (domain_len + dlen + 1> MAX_PACKET_SIZE) continue; + + memcpy(buf, domain_startp, domain_len); + memcpy(buf + domain_len, "#", 1); + memcpy(buf + domain_len + 1, data, dlen); + + z_function((char *)buf, zbuf, domain_len + 1 + dlen); + + for (unsigned int k = 0; k < dlen; k++) { + if (zbuf[k] == domain_len) { + vrd.target_sni = 1; + vrd.sni_len = domain_len; + vrd.sni_offset = (k - domain_len - 1); + goto out; + } + } + + + j = i + 1; + } + } + + goto out; } + int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, uint8_t *buf, uint32_t *buflen) { diff --git a/types.h b/types.h index 07599fe..edf9505 100644 --- a/types.h +++ b/types.h @@ -44,4 +44,18 @@ typedef __i64 int64_t; #include // IWYU pragma: export #endif +#define max(a,b)__extension__\ +({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ +}) + +#define min(a,b)__extension__\ +({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; \ +}) + #endif /* TYPES_H */