tpws --disorder

This commit is contained in:
bol-van 2023-07-03 15:28:42 +03:00
parent 52af3b7906
commit 7f5fe99ad5
20 changed files with 156 additions and 51 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

@ -627,17 +627,23 @@ tpws_check_domain_bypass()
# $1 - test function
# $2 - encrypted test : 1/0
# $3 - domain
local s pos strategy sec="$2"
local s s2 pos strategy sec="$2"
if [ "$sec" = 0 ]; then
for s in '--hostcase' '--hostspell=hoSt' '--split-http-req=method' '--split-http-req=method --hostcase' '--split-http-req=host' '--split-http-req=host --hostcase' \
'--hostdot' '--hosttab' '--hostnospace' '--methodspace' '--methodeol' '--unixeol' \
'--hostpad=1024' '--hostpad=2048' '--hostpad=4096' '--hostpad=8192' '--hostpad=16384'; do
for s in '--hostcase' '--hostspell=hoSt' '--hostdot' '--hosttab' '--hostnospace' '--methodspace' '--methodeol' '--unixeol' \
'--hostpad=1024' '--hostpad=2048' '--hostpad=4096' '--hostpad=8192' '--hostpad=16384' ; do
tpws_curl_test_update $1 $3 $s
done
for s2 in '' '--disorder'; do
for s in '--split-http-req=method' '--split-http-req=method --hostcase' '--split-http-req=host' '--split-http-req=host --hostcase' ; do
tpws_curl_test_update $1 $3 $s $s2
done
done
else
for pos in 1 2 3 4 5 10 50 100; do
s="--split-pos=$pos"
tpws_curl_test_update $1 $3 $s && break
for s2 in '' '--disorder'; do
for pos in 1 2 3 4 5 10 50 100; do
s="--split-pos=$pos"
tpws_curl_test_update $1 $3 $s $s2 && break
done
done
fi
report_strategy $1 $3 tpws

View File

@ -529,6 +529,7 @@ tpws is transparent proxy.
--split-http-req=method|host ; split http request at specified logical position.
--split-pos=<numeric_offset> ; split at specified pos. split-http-req takes precedence over split-pos for http reqs.
--split-any-protocol ; split not only http and https
--disorder ; when splitting simulate sending second fragment first
--hostcase ; change Host: => host:
--hostspell ; exact spelling of "Host" header. must be 4 chars. default is "host"
--hostdot ; add "." after Host: name
@ -597,6 +598,12 @@ if tpws serves many clients it can cause trouble. also DoS attack is possible ag
if remote resolving causes trouble configure clients to use local name resolution and use
`--no-resolve` option on tpws side.
`--disorder` is an additional flag to any split option.
It tries to simulate `--disorder2` option of `nfqws` using standard socket API without the need of additional privileges.
This works fine in Linux and MacOS but unexpectedly in FreeBSD and OpenBSD
(system sends second fragment then the whole packet instead of the first fragment).
## Ways to get a list of blocked IP
nftables can't work with ipsets. Native nf sets require lots of RAM to load large ip lists with subnets and intervals.

View File

@ -574,6 +574,7 @@ tpws - это transparent proxy.
--split-http-req=method|host ; способ разделения http запросов на сегменты : около метода (GET,POST) или около заголовка Host
--split-pos=<offset> ; делить все посылы на сегменты в указанной позиции. единственная опция, работающая на не-http. при указании split-http-req он имеет преимущество на http.
--split-any-protocol ; применять split-pos к любым пакетам. по умолчанию - только к http и TLS ClientHello
--disorder ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса
--hostcase ; менять регистр заголовка "Host:". по умолчанию на "host:".
--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
--hostdot ; добавление точки после имени хоста : "Host: kinozal.tv."
@ -672,6 +673,17 @@ tpws полностью работает на асинхронных сокет
Если при этом критический размер padding около MTU, значит скорее всего DPI не выполняет реассемблинг пакетов, и лучше будет использовать обычные опции --split-…
Если все же реассемблинг выполняется, то критический размер будет около размера буфера DPI. Он может быть 4K или 8K, возможны и другие значения.
--disorder - это попытка симулировать режим disorder2 nfqws , используя особенности ОС по реализации stream сокетов.
Однако, в отличие от nfqws, здесь не требуются повышенные привилегии.
Реализовано это следующим образом. У сокета есть возможность выставить TTL. Все пакеты будут отправляться с ним.
Перед отправкой первого сегмента ставим TTL=1. Пакет будет дропнут на первом же роутере, он не дойдет ни до DPI, ни до сервера.
Затем возвращаем TTL в значение по умолчанию. ОС отсылает второй сегмент, и он уже доходит до сервера.
Сервер возвращает SACK, потому что не получил первый кусок, и ОС его отправляет повторно, но здесь уже мы ничего не делаем.
Этот режим работает как ожидается на Linux и MacOS. Однако, на FreeBSD и OpenBSD он работает не так хорошо.
Ядро этих ОС отсылает ретрансмиссию в виде полного пакета. Потому выходит, что до сервера идет сначала второй кусок,
а потом полный запрос без сплита. На него может отреагировать DPI штатным образом.
--disorder является дополнительным флагом к любому сплиту. Сам по себе он не делает ничего.
--skip-nodelay может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws.
Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения
подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.

View File

@ -172,11 +172,27 @@ bool is_private6(const struct sockaddr_in6* a)
int set_keepalive(int fd)
bool set_keepalive(int fd)
{
int yes=1;
return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int))!=-1;
}
bool set_ttl(int fd, int ttl)
{
return setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))!=-1;
}
bool set_hl(int fd, int hl)
{
return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hl, sizeof(hl))!=-1;
}
bool set_ttl_hl(int fd, int ttl)
{
bool b1,b2;
// try to set both but one may fail if family is wrong
b1=set_ttl(fd, ttl);
b2=set_hl(fd, ttl);
return b1 || b2;
}
int get_so_error(int fd)
{
// getsockopt(SO_ERROR) clears error

View File

@ -25,7 +25,10 @@ bool is_localnet(const struct sockaddr *a);
bool is_linklocal(const struct sockaddr_in6* a);
bool is_private6(const struct sockaddr_in6* a);
int set_keepalive(int fd);
bool set_keepalive(int fd);
bool set_ttl(int fd, int ttl);
bool set_hl(int fd, int hl);
bool set_ttl_hl(int fd, int ttl);
int get_so_error(int fd);
static inline uint16_t pntoh16(const uint8_t *p) {

View File

@ -43,6 +43,8 @@ struct params_s
enum splithttpreq split_http_req;
bool split_any_protocol;
int split_pos;
bool disorder;
int ttl_default;
char pidfile[256];

View File

@ -103,6 +103,18 @@ static bool is_interface_online(const char *ifname)
close(sock);
return !!(ifr.ifr_flags & IFF_UP);
}
static int get_default_ttl()
{
int sock,ttl=0;
socklen_t optlen=sizeof(ttl);
if ((sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP))!=-1)
{
getsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, &optlen);
close(sock);
}
return ttl;
}
static void exithelp()
@ -147,6 +159,11 @@ static void exithelp()
" --split-http-req=method|host\t; split at specified logical part of plain http request\n"
" --split-pos=<numeric_offset>\t; split at specified pos. split-http-req takes precedence for http.\n"
" --split-any-protocol\t\t; split not only http and https\n"
#if defined(BSD) && !defined(__APPLE__)
" --disorder\t\t\t; when splitting simulate sending second fragment first (BSD sends entire message instead of first fragment, this is not good)\n"
#else
" --disorder\t\t\t; when splitting simulate sending second fragment first\n"
#endif
" --hostcase\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostdot\t\t\t; add \".\" after Host: name\n"
@ -204,6 +221,19 @@ static void checkbind_clean()
}
void save_default_ttl()
{
if (!params.ttl_default)
{
params.ttl_default = get_default_ttl();
if (!params.ttl_default)
{
fprintf(stderr, "could not get default ttl\n");
exit_clean(1);
}
}
}
void parse_params(int argc, char *argv[])
{
int option_index = 0;
@ -253,23 +283,24 @@ void parse_params(int argc, char *argv[])
{ "split-http-req",required_argument,0,0 },// optidx=23
{ "split-pos",required_argument,0,0 },// optidx=24
{ "split-any-protocol",optional_argument,0,0},// optidx=25
{ "methodspace",no_argument,0,0 },// optidx=26
{ "methodeol",no_argument,0,0 },// optidx=27
{ "hosttab",no_argument,0,0 },// optidx=28
{ "unixeol",no_argument,0,0 },// optidx=29
{ "hostlist",required_argument,0,0 },// optidx=30
{ "hostlist-exclude",required_argument,0,0 },// optidx=31
{ "pidfile",required_argument,0,0 },// optidx=32
{ "debug",optional_argument,0,0 },// optidx=33
{ "local-rcvbuf",required_argument,0,0 },// optidx=34
{ "local-sndbuf",required_argument,0,0 },// optidx=35
{ "remote-rcvbuf",required_argument,0,0 },// optidx=36
{ "remote-sndbuf",required_argument,0,0 },// optidx=37
{ "socks",no_argument,0,0 },// optidx=38
{ "no-resolve",no_argument,0,0 },// optidx=39
{ "skip-nodelay",no_argument,0,0 },// optidx=40
{ "disorder",no_argument,0,0 },// optidx=26
{ "methodspace",no_argument,0,0 },// optidx=27
{ "methodeol",no_argument,0,0 },// optidx=28
{ "hosttab",no_argument,0,0 },// optidx=29
{ "unixeol",no_argument,0,0 },// optidx=30
{ "hostlist",required_argument,0,0 },// optidx=31
{ "hostlist-exclude",required_argument,0,0 },// optidx=32
{ "pidfile",required_argument,0,0 },// optidx=33
{ "debug",optional_argument,0,0 },// optidx=34
{ "local-rcvbuf",required_argument,0,0 },// optidx=35
{ "local-sndbuf",required_argument,0,0 },// optidx=36
{ "remote-rcvbuf",required_argument,0,0 },// optidx=37
{ "remote-sndbuf",required_argument,0,0 },// optidx=38
{ "socks",no_argument,0,0 },// optidx=39
{ "no-resolve",no_argument,0,0 },// optidx=40
{ "skip-nodelay",no_argument,0,0 },// optidx=41
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
{ "enable-pf",no_argument,0,0 },// optidx=41
{ "enable-pf",no_argument,0,0 },// optidx=42
#endif
{ NULL,0,NULL,0 }
};
@ -453,23 +484,27 @@ void parse_params(int argc, char *argv[])
case 25: /* split-any-protocol */
params.split_any_protocol = true;
break;
case 26: /* methodspace */
case 26: /* disorder */
params.disorder = true;
save_default_ttl();
break;
case 27: /* methodspace */
params.methodspace = true;
params.tamper = true;
break;
case 27: /* methodeol */
case 28: /* methodeol */
params.methodeol = true;
params.tamper = true;
break;
case 28: /* hosttab */
case 29: /* hosttab */
params.hosttab = true;
params.tamper = true;
break;
case 29: /* unixeol */
case 30: /* unixeol */
params.unixeol = true;
params.tamper = true;
break;
case 30: /* hostlist */
case 31: /* hostlist */
if (!strlist_add(&params.hostlist_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
@ -477,7 +512,7 @@ void parse_params(int argc, char *argv[])
}
params.tamper = true;
break;
case 31: /* hostlist-exclude */
case 32: /* hostlist-exclude */
if (!strlist_add(&params.hostlist_exclude_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
@ -485,36 +520,36 @@ void parse_params(int argc, char *argv[])
}
params.tamper = true;
break;
case 32: /* pidfile */
case 33: /* pidfile */
strncpy(params.pidfile,optarg,sizeof(params.pidfile));
params.pidfile[sizeof(params.pidfile)-1]='\0';
break;
case 33:
case 34:
params.debug = optarg ? atoi(optarg) : 1;
break;
case 34: /* local-rcvbuf */
case 35: /* local-rcvbuf */
params.local_rcvbuf = atoi(optarg)/2;
break;
case 35: /* local-sndbuf */
case 36: /* local-sndbuf */
params.local_sndbuf = atoi(optarg)/2;
break;
case 36: /* remote-rcvbuf */
case 37: /* remote-rcvbuf */
params.remote_rcvbuf = atoi(optarg)/2;
break;
case 37: /* remote-sndbuf */
case 38: /* remote-sndbuf */
params.remote_sndbuf = atoi(optarg)/2;
break;
case 38: /* socks */
case 39: /* socks */
params.proxy_type = CONN_TYPE_SOCKS;
break;
case 39: /* no-resolve */
case 40: /* no-resolve */
params.no_resolve = true;
break;
case 40: /* skip-nodelay */
case 41: /* skip-nodelay */
params.skip_nodelay = true;
break;
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
case 41: /* enable-pf */
case 42: /* enable-pf */
params.pf_enable = true;
break;
#endif
@ -849,7 +884,7 @@ int main(int argc, char *argv[])
fprintf(stderr,"could not initialize redirector !!!\n");
goto exiterr;
}
for(i=0;i<=params.binds_last;i++)
{
if (params.debug)
@ -862,6 +897,7 @@ int main(int argc, char *argv[])
perror("socket");
goto exiterr;
}
#ifndef __OpenBSD__
// in OpenBSD always IPV6_ONLY for wildcard sockets
if ((list[i].salisten.ss_family == AF_INET6) && setsockopt(listen_fd[i], IPPROTO_IPV6, IPV6_V6ONLY, &list[i].ipv6_only, sizeof(int)) == -1)

View File

@ -111,8 +111,29 @@ static bool proxy_remote_conn_ack(tproxy_conn_t *conn, int sock_err)
}
ssize_t send_with_ttl(int fd, const void *buf, size_t len, int flags, int ttl)
{
ssize_t wr;
static bool send_buffer_create(send_buffer_t *sb, char *data, size_t len)
if (ttl)
{
DBGPRINT("send_with_ttl %d fd=%d",ttl,fd);
if (!set_ttl_hl(fd, ttl))
fprintf(stderr,"could not set ttl %d to fd=%d\n",ttl,fd);
}
wr = send(fd, buf, len, flags);
if (ttl)
{
int e=errno;
if (!set_ttl_hl(fd, params.ttl_default))
fprintf(stderr,"could not set ttl %d to fd=%d\n",params.ttl_default,fd);
errno=e;
}
return wr;
}
static bool send_buffer_create(send_buffer_t *sb, char *data, size_t len, int ttl)
{
if (sb->data)
{
@ -128,6 +149,7 @@ static bool send_buffer_create(send_buffer_t *sb, char *data, size_t len)
if (data) memcpy(sb->data,data,len);
sb->len = len;
sb->pos = 0;
sb->ttl = ttl;
return true;
}
static void send_buffer_free(send_buffer_t *sb)
@ -162,7 +184,7 @@ static ssize_t send_buffer_send(send_buffer_t *sb, int fd)
{
ssize_t wr;
wr = send(fd, sb->data + sb->pos, sb->len - sb->pos, 0);
wr = send_with_ttl(fd, sb->data + sb->pos, sb->len - sb->pos, 0, sb->ttl);
DBGPRINT("send_buffer_send len=%zu pos=%zu wr=%zd err=%d",sb->len,sb->pos,wr,errno)
if (wr>0)
{
@ -236,16 +258,16 @@ 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)
static ssize_t send_or_buffer(send_buffer_t *sb, int fd, char *buf, size_t len, int ttl)
{
ssize_t wr=0;
if (len)
{
wr = send(fd, buf, len, 0);
wr = send_with_ttl(fd, buf, len, 0, ttl);
if (wr<0 && errno==EAGAIN) wr=0;
if (wr>=0 && wr<len)
{
if (!send_buffer_create(sb, buf+wr, len-wr))
if (!send_buffer_create(sb, buf+wr, len-wr, ttl))
wr=-1;
}
}
@ -967,19 +989,19 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
if (split_pos)
{
VPRINT("Splitting at pos %zu", split_pos)
wr = send_or_buffer(conn->partner->wr_buf, conn->partner->fd, buf, split_pos);
wr = send_or_buffer(conn->partner->wr_buf, conn->partner->fd, buf, split_pos, params.disorder ? 1 : 0);
DBGPRINT("send_or_buffer(1) fd=%d wr=%zd err=%d",conn->partner->fd,wr,errno)
if (wr >= 0)
{
conn->partner->twr += wr;
wr = send_or_buffer(conn->partner->wr_buf + 1, conn->partner->fd, buf + split_pos, bs - split_pos);
wr = send_or_buffer(conn->partner->wr_buf + 1, conn->partner->fd, buf + split_pos, bs - split_pos, 0);
DBGPRINT("send_or_buffer(2) fd=%d wr=%zd err=%d",conn->partner->fd,wr,errno)
if (wr>0) conn->partner->twr += wr;
}
}
else
{
wr = send_or_buffer(conn->partner->wr_buf, conn->partner->fd, buf, bs);
wr = send_or_buffer(conn->partner->wr_buf, conn->partner->fd, buf, bs, 0);
DBGPRINT("send_or_buffer(3) fd=%d wr=%zd err=%d",conn->partner->fd,wr,errno)
if (wr>0) conn->partner->twr += wr;
}
@ -1039,7 +1061,7 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number)
DBGPRINT("read_all_and_buffer(%d) numbytes=%d",buffer_number,numbytes)
if (numbytes>0)
{
if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes))
if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes, 0))
{
ssize_t rd = recv(conn->fd, conn->partner->wr_buf[buffer_number].data, numbytes, MSG_DONTWAIT);
if (rd>0)

View File

@ -29,6 +29,7 @@ struct send_buffer
{
char *data;
size_t len,pos;
int ttl;
};
typedef struct send_buffer send_buffer_t;