tpws: --tlsrec

This commit is contained in:
bol-van 2023-10-12 12:35:06 +03:00
parent 2456aae8ad
commit 6dc413d0c9
30 changed files with 3372 additions and 3493 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -645,6 +645,9 @@ tpws_check_domain_bypass()
tpws_curl_test_update $1 $3 $s $s2 && break tpws_curl_test_update $1 $3 $s $s2 && break
done done
done done
for s2 in '--tlsrec=sni' '--tlsrec=sni --split-pos=10' '--tlsrec=sni --split-pos=10 --disorder'; do
tpws_curl_test_update $1 $3 $s2 && [ "$FORCE" != 1 ] && break
done
fi fi
report_strategy $1 $3 tpws report_strategy $1 $3 tpws
} }

View File

@ -1,4 +1,4 @@
Поддерживаемые версии Поддерживаемые версии
--------------------- ---------------------
FreeBSD 11.x+ , OpenBSD 6.x+, частично MacOS Sierra+ FreeBSD 11.x+ , OpenBSD 6.x+, частично MacOS Sierra+

View File

@ -243,3 +243,7 @@ v50
DHT protocol support. DHT protocol support.
DPI desync mode 'tamper' for DHT. DPI desync mode 'tamper' for DHT.
HEX string support in addition to binary files. HEX string support in addition to binary files.
v51
tpws --tlsrec attack.

View File

@ -1,4 +1,4 @@
nftables - это технология, пришедшая на замену iptables. nftables - это технология, пришедшая на замену iptables.
В ней собрали все, что относилось к различным iptables. А их немало. iptables, ip6tables, ebtables, arptables, ipset. В ней собрали все, что относилось к различным iptables. А их немало. iptables, ip6tables, ebtables, arptables, ipset.
Весь код из разрозненных, но похожих компонент, собрали в одно целое с единым синтаксисом. Весь код из разрозненных, но похожих компонент, собрали в одно целое с единым синтаксисом.
Добавили различные конструкции языка, позволяющие писать правила более лаконично, не повторяя одни и те же команды с небольшими различиями. Добавили различные конструкции языка, позволяющие писать правила более лаконично, не повторяя одни и те же команды с небольшими различиями.

View File

@ -1,4 +1,4 @@
Специально для тех, кто хочет побыстрее начать, но не хочет слишком углубляться в простыню readme.txt. Специально для тех, кто хочет побыстрее начать, но не хочет слишком углубляться в простыню readme.txt.
Предупреждение : не пишите в issue вопросы типа "как скопировать файл", "как скачать", "как запустить", ... Предупреждение : не пишите в issue вопросы типа "как скопировать файл", "как скачать", "как запустить", ...
То есть все , что касается базовых навыков обращения с ОС linux. Эти вопросы буду закрывать сразу. То есть все , что касается базовых навыков обращения с ОС linux. Эти вопросы буду закрывать сразу.

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
zapret v.50 zapret v.51
English English
------- -------
@ -592,6 +592,8 @@ tpws - это transparent proxy.
--methodspace ; добавить пробел после метода : "GET /" => "GET /" --methodspace ; добавить пробел после метода : "GET /" => "GET /"
--methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /" --methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /"
--unixeol ; конвертировать 0D0A в 0A и использовать везде 0A --unixeol ; конвертировать 0D0A в 0A и использовать везде 0A
--tlsrec=sni ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI. Если SNI нет - отмена.
--tlsrec-pos=<pos> ; разбивка TLS ClientHello на 2 TLS records. режем на указанной позиции, если длина слишком мелкая - на позиции 1.
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются. --hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
; в файле должен быть хост на каждой строке. ; в файле должен быть хост на каждой строке.
; список читается 1 раз при старте и хранится в памяти в виде иерархической структуры для быстрого поиска. ; список читается 1 раз при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
@ -691,6 +693,19 @@ tpws полностью работает на асинхронных сокет
а потом полный запрос без сплита. На него может отреагировать DPI штатным образом. а потом полный запрос без сплита. На него может отреагировать DPI штатным образом.
--disorder является дополнительным флагом к любому сплиту. Сам по себе он не делает ничего. --disorder является дополнительным флагом к любому сплиту. Сам по себе он не делает ничего.
--tlsrec и --tlsrec-pos позволяют внутри одного tcp сегмента разрезать TLS ClientHello на 2 TLS records.
--tlsrec=sni режет между 1 и 2 символами hostname в SNI, делая невозможным бинарный поиск паттерна без анализа структуры данных.
В случае отсутствия SNI разбиение отменяется.
--tlsrec-pos режет на указанной позиции. Если длина блока данных TLS меньше указанной позиции, режем на позиции 1.
Параметр сочетается с --split-pos. В этом случае происходит сначала разделение на уровне TLS record layer, потом на уровне TCP.
Самая изорщенная атака --tslrec, --split-pos и --disorder вместе.
--tlsrec ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах без проблем
принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN или системы ddos-защиты.
Поэтому применение --tlsrec без ограничителей вряд ли целесообразно.
В РФ --tlsrec обычно не работает с TLS 1.2, потому что цензор парсит сертификат сервера из ServerHello.
Работает только с TLS 1.3, поскольку там эта информация шифруется.
Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного.
--skip-nodelay может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws. --skip-nodelay может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws.
Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения
подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз. подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.

View File

@ -1,4 +1,4 @@
Данный мануал пишется не как копипастная инструкция, а как помощь уже соображающему. Данный мануал пишется не как копипастная инструкция, а как помощь уже соображающему.
Если вы не знаете основ сетей, linux, openwrt, а пытаетесь что-то скопипастить отсюда без малейшего Если вы не знаете основ сетей, linux, openwrt, а пытаетесь что-то скопипастить отсюда без малейшего
понимания смысла, то маловероятно, что у вас что-то заработает. Не тратье свое время напрасно. понимания смысла, то маловероятно, что у вас что-то заработает. Не тратье свое время напрасно.
Цель - донести принципы как это настраивается вообще, а не указать какую буковку где вписать. Цель - донести принципы как это настраивается вообще, а не указать какую буковку где вписать.

View File

@ -31,6 +31,11 @@ bool set_hl(int fd, int hl);
bool set_ttl_hl(int fd, int ttl); bool set_ttl_hl(int fd, int ttl);
int get_so_error(int fd); int get_so_error(int fd);
// alignment-safe functions
static inline uint16_t pntoh16(const uint8_t *p) { static inline uint16_t pntoh16(const uint8_t *p) {
return ((uint16_t)p[0] << 8) | (uint16_t)p[1]; return ((uint16_t)p[0] << 8) | (uint16_t)p[1];
} }
static inline void phton16(uint8_t *p, uint16_t v) {
p[0] = (uint8_t)(v>>8);
p[1] = (uint8_t)v;
}

View File

@ -8,6 +8,7 @@
#include "strpool.h" #include "strpool.h"
enum splithttpreq { split_none = 0, split_method, split_host }; enum splithttpreq { split_none = 0, split_method, split_host };
enum tlsrec { tlsrec_none = 0, tlsrec_sni, tlsrec_pos };
enum bindll { unwanted=0, no, prefer, force }; enum bindll { unwanted=0, no, prefer, force };
#define MAX_BINDS 32 #define MAX_BINDS 32
@ -41,6 +42,8 @@ struct params_s
int hostpad; int hostpad;
char hostspell[4]; char hostspell[4];
enum splithttpreq split_http_req; enum splithttpreq split_http_req;
enum tlsrec tlsrec;
int tlsrec_pos;
bool split_any_protocol; bool split_any_protocol;
int split_pos; int split_pos;
bool disorder; bool disorder;

View File

@ -4,11 +4,12 @@
#include "params.h" #include "params.h"
#include "hostlist.h" #include "hostlist.h"
#include "protocol.h" #include "protocol.h"
#include "helpers.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
// pHost points to "Host: ..." // pHost points to "Host: ..."
bool find_host(char **pHost,char *buf,size_t bs) bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs)
{ {
if (!*pHost) if (!*pHost)
{ {
@ -23,14 +24,15 @@ bool find_host(char **pHost,char *buf,size_t bs)
} }
static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL }; static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos) // segment buffer has at least 5 extra bytes to extend data block
void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos)
{ {
char *p, *pp, *pHost = NULL; uint8_t *p, *pp, *pHost = NULL;
size_t method_len = 0, pos; size_t method_len = 0, pos;
const char **method; const char **method;
bool bIsHttp = false, bBypass = false; bool bIsHttp = false, bBypass = false;
char bRemovedHostSpace = 0; char bRemovedHostSpace = 0;
char Host[128]; char *pc, Host[128];
*split_pos=0; *split_pos=0;
@ -57,7 +59,7 @@ void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,si
memcpy(Host, p, pp - p); memcpy(Host, p, pp - p);
Host[pp - p] = '\0'; Host[pp - p] = '\0';
VPRINT("Requested Host is : %s", Host) VPRINT("Requested Host is : %s", Host)
for(p = Host; *p; p++) *p=tolower(*p); for(pc = Host; *pc; pc++) *pc=tolower(*pc);
bBypass = !HostlistCheck(params.hostlist, params.hostlist_exclude, Host); bBypass = !HostlistCheck(params.hostlist, params.hostlist_exclude, Host);
} }
if (!bBypass) if (!bBypass)
@ -208,13 +210,14 @@ void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,si
{ {
VPRINT("Not acting on this request") VPRINT("Not acting on this request")
} }
return;
} }
else if (params.split_pos && params.split_pos < *size)
{ if (IsTLSClientHello(segment,*size))
// split-pos is the only parameter applicable to non-http block (may be https ?)
if (IsTLSClientHello((uint8_t*)segment,*size))
{ {
char host[256]; char host[256];
size_t tpos=0,elen;
const uint8_t *ext;
VPRINT("packet contains TLS ClientHello") VPRINT("packet contains TLS ClientHello")
// we need host only if hostlist is present // we need host only if hostlist is present
@ -227,9 +230,42 @@ void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,si
return; return;
} }
} }
switch(params.tlsrec)
{
case tlsrec_sni:
if (TLSFindExt(segment,*size,0,&ext,&elen))
tpos = ext-segment+1; // between typical 1st and 2nd char of hostname
break;
case tlsrec_pos:
tpos = params.tlsrec_pos;
break;
default:
break;
}
if (tpos)
{
// construct 2 TLS records from one
uint16_t l = pntoh16(segment+3); // length
if (l>=2)
{
// length is checked in IsTLSClientHello and cannot exceed buffer size
if (tpos>=l) tpos=1;
VPRINT("making 2 TLS records at pos %zu",tpos)
memmove(segment+5+tpos+5,segment+5+tpos,l-tpos);
segment[5+tpos] = segment[0];
segment[5+tpos+1] = segment[1];
segment[5+tpos+2] = segment[2];
phton16(segment+5+tpos+3,l-tpos);
phton16(segment+3,tpos);
*size += 5;
}
}
if (params.split_pos < *size)
*split_pos = params.split_pos;
return;
}
if (params.split_any_protocol && params.split_pos < *size)
*split_pos = params.split_pos; *split_pos = params.split_pos;
} }
else if (params.split_any_protocol)
*split_pos = params.split_pos;
}
}

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
bool find_host(char **pHost,char *buf,size_t bs); bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs);
void modify_tcp_segment(char *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos); void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos);

View File

@ -174,6 +174,8 @@ static void exithelp()
" --methodspace\t\t\t; add extra space after method\n" " --methodspace\t\t\t; add extra space after method\n"
" --methodeol\t\t\t; add end-of-line before method\n" " --methodeol\t\t\t; add end-of-line before method\n"
" --unixeol\t\t\t; replace 0D0A to 0A\n" " --unixeol\t\t\t; replace 0D0A to 0A\n"
" --tlsrec=sni\t\t\t; make 2 TLS records. split at SNI. don't split if SNI is not present\n"
" --tlsrec-pos=<pos>\t\t; make 2 TLS records. split at specified pos\n"
); );
exit(1); exit(1);
} }
@ -288,19 +290,21 @@ void parse_params(int argc, char *argv[])
{ "methodeol",no_argument,0,0 },// optidx=28 { "methodeol",no_argument,0,0 },// optidx=28
{ "hosttab",no_argument,0,0 },// optidx=29 { "hosttab",no_argument,0,0 },// optidx=29
{ "unixeol",no_argument,0,0 },// optidx=30 { "unixeol",no_argument,0,0 },// optidx=30
{ "hostlist",required_argument,0,0 },// optidx=31 { "tlsrec",required_argument,0,0 },// optidx=31
{ "hostlist-exclude",required_argument,0,0 },// optidx=32 { "tlsrec-pos",required_argument,0,0 },// optidx=32
{ "pidfile",required_argument,0,0 },// optidx=33 { "hostlist",required_argument,0,0 },// optidx=33
{ "debug",optional_argument,0,0 },// optidx=34 { "hostlist-exclude",required_argument,0,0 },// optidx=34
{ "local-rcvbuf",required_argument,0,0 },// optidx=35 { "pidfile",required_argument,0,0 },// optidx=35
{ "local-sndbuf",required_argument,0,0 },// optidx=36 { "debug",optional_argument,0,0 },// optidx=36
{ "remote-rcvbuf",required_argument,0,0 },// optidx=37 { "local-rcvbuf",required_argument,0,0 },// optidx=37
{ "remote-sndbuf",required_argument,0,0 },// optidx=38 { "local-sndbuf",required_argument,0,0 },// optidx=38
{ "socks",no_argument,0,0 },// optidx=39 { "remote-rcvbuf",required_argument,0,0 },// optidx=39
{ "no-resolve",no_argument,0,0 },// optidx=40 { "remote-sndbuf",required_argument,0,0 },// optidx=40
{ "skip-nodelay",no_argument,0,0 },// optidx=41 { "socks",no_argument,0,0 },// optidx=41
{ "no-resolve",no_argument,0,0 },// optidx=42
{ "skip-nodelay",no_argument,0,0 },// optidx=43
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__) #if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
{ "enable-pf",no_argument,0,0 },// optidx=42 { "enable-pf",no_argument,0,0 },// optidx=44
#endif #endif
{ NULL,0,NULL,0 } { NULL,0,NULL,0 }
}; };
@ -472,7 +476,7 @@ void parse_params(int argc, char *argv[])
break; break;
case 24: /* split-pos */ case 24: /* split-pos */
i = atoi(optarg); i = atoi(optarg);
if (i) if (i>0)
params.split_pos = i; params.split_pos = i;
else else
{ {
@ -504,7 +508,27 @@ void parse_params(int argc, char *argv[])
params.unixeol = true; params.unixeol = true;
params.tamper = true; params.tamper = true;
break; break;
case 31: /* hostlist */ case 31: /* tlsrec */
if (!strcmp(optarg, "sni"))
params.tlsrec = tlsrec_sni;
else
{
fprintf(stderr, "Invalid argument for tlsrec\n");
exit_clean(1);
}
params.tamper = true;
break;
case 32: /* tlsrec-pos */
if ((params.tlsrec_pos = atoi(optarg))>0)
params.tlsrec = tlsrec_pos;
else
{
fprintf(stderr, "Invalid argument for tlsrec-pos\n");
exit_clean(1);
}
params.tamper = true;
break;
case 33: /* hostlist */
if (!strlist_add(&params.hostlist_files, optarg)) if (!strlist_add(&params.hostlist_files, optarg))
{ {
fprintf(stderr, "strlist_add failed\n"); fprintf(stderr, "strlist_add failed\n");
@ -512,7 +536,7 @@ void parse_params(int argc, char *argv[])
} }
params.tamper = true; params.tamper = true;
break; break;
case 32: /* hostlist-exclude */ case 34: /* hostlist-exclude */
if (!strlist_add(&params.hostlist_exclude_files, optarg)) if (!strlist_add(&params.hostlist_exclude_files, optarg))
{ {
fprintf(stderr, "strlist_add failed\n"); fprintf(stderr, "strlist_add failed\n");
@ -520,36 +544,36 @@ void parse_params(int argc, char *argv[])
} }
params.tamper = true; params.tamper = true;
break; break;
case 33: /* pidfile */ case 35: /* pidfile */
strncpy(params.pidfile,optarg,sizeof(params.pidfile)); strncpy(params.pidfile,optarg,sizeof(params.pidfile));
params.pidfile[sizeof(params.pidfile)-1]='\0'; params.pidfile[sizeof(params.pidfile)-1]='\0';
break; break;
case 34: case 36:
params.debug = optarg ? atoi(optarg) : 1; params.debug = optarg ? atoi(optarg) : 1;
break; break;
case 35: /* local-rcvbuf */ case 37: /* local-rcvbuf */
params.local_rcvbuf = atoi(optarg)/2; params.local_rcvbuf = atoi(optarg)/2;
break; break;
case 36: /* local-sndbuf */ case 38: /* local-sndbuf */
params.local_sndbuf = atoi(optarg)/2; params.local_sndbuf = atoi(optarg)/2;
break; break;
case 37: /* remote-rcvbuf */ case 39: /* remote-rcvbuf */
params.remote_rcvbuf = atoi(optarg)/2; params.remote_rcvbuf = atoi(optarg)/2;
break; break;
case 38: /* remote-sndbuf */ case 40: /* remote-sndbuf */
params.remote_sndbuf = atoi(optarg)/2; params.remote_sndbuf = atoi(optarg)/2;
break; break;
case 39: /* socks */ case 41: /* socks */
params.proxy_type = CONN_TYPE_SOCKS; params.proxy_type = CONN_TYPE_SOCKS;
break; break;
case 40: /* no-resolve */ case 42: /* no-resolve */
params.no_resolve = true; params.no_resolve = true;
break; break;
case 41: /* skip-nodelay */ case 43: /* skip-nodelay */
params.skip_nodelay = true; params.skip_nodelay = true;
break; break;
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__) #if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
case 42: /* enable-pf */ case 44: /* enable-pf */
params.pf_enable = true; params.pf_enable = true;
break; break;
#endif #endif

View File

@ -133,7 +133,7 @@ ssize_t send_with_ttl(int fd, const void *buf, size_t len, int flags, int ttl)
} }
static bool send_buffer_create(send_buffer_t *sb, char *data, size_t len, int ttl) static bool send_buffer_create(send_buffer_t *sb, const void *data, size_t len, int ttl)
{ {
if (sb->data) if (sb->data)
{ {
@ -258,7 +258,7 @@ static bool conn_has_unsent_pair(tproxy_conn_t *conn)
} }
static ssize_t send_or_buffer(send_buffer_t *sb, int fd, char *buf, size_t len, int ttl) static ssize_t send_or_buffer(send_buffer_t *sb, int fd, const void *buf, size_t len, int ttl)
{ {
ssize_t wr=0; ssize_t wr=0;
if (len) if (len)
@ -919,7 +919,7 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
if (!conn_partner_alive(conn)) if (!conn_partner_alive(conn))
{ {
// throw it to a black hole // throw it to a black hole
char waste[65070]; uint8_t waste[65070];
ssize_t trd=0; ssize_t trd=0;
while((rd=recv(conn->fd, waste, sizeof(waste), MSG_DONTWAIT))>0 && trd<MAX_WASTE) while((rd=recv(conn->fd, waste, sizeof(waste), MSG_DONTWAIT))>0 && trd<MAX_WASTE)
@ -969,7 +969,7 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
#endif #endif
{ {
// incoming data from local leg // incoming data from local leg
char buf[RD_BLOCK_SIZE + 4]; uint8_t buf[RD_BLOCK_SIZE + 5];
rd = recv(conn->fd, buf, RD_BLOCK_SIZE, MSG_DONTWAIT); rd = recv(conn->fd, buf, RD_BLOCK_SIZE, MSG_DONTWAIT);
DBGPRINT("recv fd=%d rd=%zd err=%d",conn->fd, rd,errno) DBGPRINT("recv fd=%d rd=%zd err=%d",conn->fd, rd,errno)