Overcoming blocking in TTK (Transtelecom) using pf

After reading the article by the respected ValdikSS, he decided to study the DPI of his provider and, with a favorable development of events, find ways to bypass it. Below are the results of my research.

So, the provider is TTK Ulyanovsk (formerly DARS Telecom), a PPPoE connection with an external IP for the duration of the session. Locking is done by wrapping locked subnets / hosts on your DPI. DNS is not a substitute.

% traceroute ya.ru
traceroute to ya.ru (87.250.250.242), 64 hops max, 40 byte packets
 1  bras.ulttk.ru (79.132.125.1)  0.899 ms  0.787 ms  0.817 ms
 2  ulk06.ulk26.transtelecom.net (217.150.41.98)  1.552 ms  1.536 ms  1.536 ms
 3  * * *
 4  Yandex-gw.transtelecom.net (188.43.3.213)  21.828 ms  15.955 ms  17.003 ms
% traceroute rutracker.org
traceroute to rutracker.org (195.82.146.214), 64 hops max, 40 byte packets
 1  bras.ulttk.ru (79.132.125.1)  1.778 ms  0.843 ms  0.751 ms
 2  ulk06.ulk26.transtelecom.net (217.150.41.98)  1.656 ms  1.698 ms  1.439 ms
 3  * * *
 4  188.43.0.18 (188.43.0.18)  16.589 ms
    BlackList-gw.transtelecom.net (188.43.30.130)  16.435 ms
    BL-gw.transtelecom.net (188.43.31.170)  16.377 ms
 5  Filter-gw.transtelecom.net (188.43.30.33)  17.006 ms  16.859 ms  17.080 ms
% traceroute kinozal.tv
traceroute: Warning: kinozal.tv has multiple addresses; using 104.24.107.53
traceroute to kinozal.tv (104.24.107.53), 64 hops max, 40 byte packets
 1  bras.ulttk.ru (79.132.125.1)  0.810 ms  0.809 ms  0.739 ms
 2  ulk06.ulk26.transtelecom.net (217.150.41.98)  1.653 ms  1.802 ms  1.736 ms
 3  * * *
 4  Filter-gw.transtelecom.net (188.43.30.34)  16.451 ms
    BL-gw.transtelecom.net (188.43.31.170)  16.405 ms  16.418 ms
 5  Filter-gw.transtelecom.net (188.43.30.33)  99.751 ms  78.541 ms  17.021 ms
 6  Cloudflare-msk-gw.transtelecom.net (188.43.3.65)  212.754 ms  107.803 ms  117.062 ms
 7  104.24.107.53 (104.24.107.53)  28.015 ms  17.216 ms  17.357 ms

Moreover, some sites are simply and unpretentiously blocked by IP (thanks at least they send RST). For example, the BT-announcements of Rutreker:

02:48:58.530078 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    ip109.176.ulttk.ru.22099 > bt.rutracker.org.http: Flags [S], cksum 0xd538 (correct), seq 3425095266, win 65535, options [mss 1452,nop,wscale 6,sackOK,TS val 1991139339 ecr 0], length 0
02:48:58.546482 IP (tos 0x0, ttl 61, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    bt.rutracker.org.http > ip109.176.ulttk.ru.22099: Flags [R.], cksum 0x13b9 (correct), seq 0, ack 3425095267, win 0, length 0

For the same sites where you need to show the blocking page, passive DPI works. Consider the example of Rutreker:

02:27:28.605860 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    ip109.176.ulttk.ru.27338 > rutracker.org.http: Flags [S], cksum 0xeafc (correct), seq 3703194126, win 65535, options [mss 1452,nop,wscale 6,sackOK,TS val 1989849414 ecr 0], length 0
02:27:28.646812 IP (tos 0x0, ttl 58, id 0, offset 0, flags [DF], proto TCP (6), length 48)
    rutracker.org.http > ip109.176.ulttk.ru.27338: Flags [S.], cksum 0x81d2 (correct), seq 2683499593, ack 3703194127, win 14600, options [mss 1400,nop,wscale 8], length 0
02:27:28.646913 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    ip109.176.ulttk.ru.27338 > rutracker.org.http: Flags [.], cksum 0xe266 (correct), seq 1, ack 1, win 1028, length 0
02:27:28.647281 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 117)
    ip109.176.ulttk.ru.27338 > rutracker.org.http: Flags [P.], cksum 0xb1d5 (correct), seq 1:78, ack 1, win 1028, length 77: HTTP, length: 77
        GET / HTTP/1.1
        Host: rutracker.org
        User-Agent: curl/7.56.0
        Accept: */*
02:27:28.663665 IP (tos 0x0, ttl 61, id 0, offset 0, flags [DF], proto TCP (6), length 286)
    rutracker.org.http > ip109.176.ulttk.ru.27338: Flags [FP.], cksum 0xf88c (correct), seq 1:247, ack 78, win 0, length 246: HTTP, length: 246
        HTTP/1.1 301 Moved Permanently
02:27:28.663724 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    ip109.176.ulttk.ru.27338 > rutracker.org.http: Flags [.], cksum 0xe126 (correct), seq 78, ack 248, win 1024, length 0
02:27:28.664047 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40)
    ip109.176.ulttk.ru.27338 > rutracker.org.http: Flags [F.], cksum 0xe121 (correct), seq 78, ack 248, win 1028, length 0
02:27:28.695429 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    ip109.176.ulttk.ru.42348 > dib-filtr-gw.transtelecom.net.http: Flags [S], cksum 0x0556 (correct), seq 2835326510, win 65535, options [mss 1452,nop,wscale 6,sackOK,TS val 1989849504 ecr 0], length 0

Let's look at this conclusion in more detail. From the moment 605860 to 646913 the client carries out a triple handshake with the “real” Rutreker. In 647281, the client sends an HTTP request to the Rutreker with a length of 77 bytes, but instead of the usual ACK, it receives from the "fake" Rutreker FPA with HTTP redirection of 246 bytes long. Moreover, mind you, with the correct ACK 78, but the SEQ sequence number on the left, within the framework of this TCP connection. Next, the client closes the connection and goes where they said. The ACK and HTTP response from Rutreker never arrive.

Basically, it’s clear why the provider needed this strange TCP with FPA flags: FIN to start the connection from the client side, PUSH to push this segment into the client’s TCP queue, and the correct ACK to make everything look beautiful and this “fake” TCP segment was dropped by the TCP \ IP stack of the client. But this FPA also killed him.

So, the main strategy is to drop TCP segments with FPA flags set, as if coming from addresses of blocked resources.

As a router in my home network, FreeBSD works on the old Atom, pf works as a packet filter. The main problem was that pf is a statefull firewall, i.e. after our first allowed outgoing SYN, a state record and all subsequent requests are entered into the firewall's state table in the Rutreker address and, most importantly, the answers within this TCP connection no longer pass through any firewall rules, they are all allowed.

Such behavior greatly increases the firewall performance and reduces the number of rules, but in our case it prevents effective filtering. Therefore, all requests / responses to blocked resources will be produced in stateless mode, i.e. without saving state. In pf, the rules are as follows:

WAN="ng0"
LAN="re0"
Blocked="{ заблок.IP1, заблок.IP2, заблок.IP3, и т.д. }"
...
# -UNBLOCK-
# for LAN hosts
pass in quick on $LAN proto tcp from $LAN:network to $Blocked no state
block drop out quick on $LAN proto tcp from $Blocked to $LAN:network flags FPA/FPA
pass out quick on $LAN proto tcp from $Blocked to $LAN:network no state
# -UNBLOCK-
# for me (router itself, for testing)
pass out quick on $WAN proto tcp from ($WAN) to $Blocked no state
block drop in quick on $WAN proto tcp from $Blocked to ($WAN) flags FPA/FPA
pass in quick on $WAN proto tcp from $Blocked to ($WAN) no state
...

The no state directive in the permitting rules prohibits state preservation, the first rule in the UNBLOCK block allows outgoing calls to a blocked resource, the second rule blocks “fake” incoming messages with FPA flags set, and the third one allows all other incoming ones. The quick directive in all rules stops the firewall from viewing all subsequent filtering rules when it matches this rule. Result (triple handshake skipped):

01:48:10.221343 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 117)
    ip109.176.ulttk.ru.42714 > rutracker.org.http: Flags [P.], cksum 0xccb6 (correct), seq 1:78, ack 1, win 1028, length 77: HTTP, length: 77
        GET / HTTP/1.1
        Host: rutracker.org
        User-Agent: curl/7.56.0
        Accept: */*
01:48:10.237686 IP (tos 0x0, ttl 61, id 0, offset 0, flags [DF], proto TCP (6), length 286)
    rutracker.org.http > ip109.176.ulttk.ru.42714: Flags [FP.], cksum 0x136e (correct), seq 1:247, ack 78, win 0, length 246: HTTP, length: 246
        HTTP/1.1 301 Moved Permanently
01:48:10.563977 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 117)
    ip109.176.ulttk.ru.42714 > rutracker.org.http: Flags [P.], cksum 0xccb6 (correct), seq 1:78, ack 1, win 1028, length 77: HTTP, length: 77
        GET / HTTP/1.1
        Host: rutracker.org
        User-Agent: curl/7.56.0
        Accept: */*
01:48:10.604853 IP (tos 0x0, ttl 58, id 42669, offset 0, flags [DF], proto TCP (6), length 40)
    rutracker.org.http > ip109.176.ulttk.ru.42714: Flags [.], cksum 0x00c5 (correct), seq 1, ack 78, win 58, length 0
01:48:10.605043 IP (tos 0x0, ttl 58, id 42670, offset 0, flags [DF], proto TCP (6), length 422)
    rutracker.org.http > ip109.176.ulttk.ru.42714: Flags [P.], cksum 0x9bc5 (correct), seq 1:383, ack 78, win 58, length 382: HTTP, length: 382
        HTTP/1.1 301 Moved Permanently
        Server: nginx
        Date: Thu, 26 Oct 2017 21:48:10 GMT
        Content-Type: text/html
        Content-Length: 178
        Location: http://rutracker.org/forum/index.php
        Connection: keep-alive

221343 - HTTP request to Rutreker
237686 - response from provider DPI with FPA flags (dump from the external interface of the router, so it is visible here; the client does not receive it)
563977 - the client did not wait for the response, repeats the request
604853 - the real Rutreker sends an ACK
605043 - the real Rootracker sends an HTTP response

PS The same can be done with the help of any modern firewall, pf is here only because I use it. An attentive reader could also notice that IP spoofing by the provider's DPI is elementarily detected by IP TTL (58 vs. 61). But pf does not know how to filter all fields of IP packets, only the main ones. When using other firewalls, IP TTL mismatch can be used as an additional sign of a “fake” packet, along with TCP FPA.

That's All Folks!

Also popular now: