[Из песочницы] Преодоление блокировки в ТТК (Транстелеком) при помощи pf

habr.png

После прочтения статьи уважаемого ValdikSS решил изучить DPI своего провайдера и, при благоприятном развитии событий, найти пути его обхода. Ниже — результаты моих изысканий.

Итак, провайдер — ТТК Ульяновск (бывш. DARS Telecom), PPPoE-подключение с выделением внешнего IP на время сессии. Блокировка осуществляется заворачиванием заблокированных подсетей/хостов на свой DPI. DNS не подменяется.

% 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


Причем некоторые сайты просто и незатейливо заблокированы по IP (спасибо хоть RST присылают). Например, bt-анонсеры Рутрекера:

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


Для тех же сайтов, где нужно показать страничку блокировки работает пассивный DPI. Рассмотрим на примере Рутрекера:

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


Посмотрим на данный вывод подробнее. С момента 605860 до 646913 клиент осуществляет тройное рукопожатие с «настоящим» Рутрекером. В 647281 клиент посылает HTTP-запрос Рутрекеру длинной 77 байт, но вместо обычного ACK получает от «поддельного» Рутрекера FPA с HTTP-перенаправлением длинной 246 байт. Причем, заметьте, с корректным ACK 78, но левым, в рамках данного TCP-соединения, номером последовательности SEQ. Далее клиент закрывает соединение и переходит куда сказали. ACK и HTTP-ответ от Рутрекера так и не приходят.

В принципе, понятно зачем провайдеру потребовался этот странный TCP с флагами FPA: FIN для начала завершения соединения со стороны клиента, PUSH для проталкивания этого сегмента в TCP-очереди клиента, а корректный ACK чтоб всё выглядело красиво и этот «поддельный» TCP-сегмент не был отброшен TCP\IP-стеком клиента. Но этот FPA его же и погубил.

Итак, основная стратегия — отбрасывать TCP-сегменты с установленными флагами FPA, как-бы приходящие с адресов заблокированных ресурсов.

В качестве роутера в моей домашней сети трудится FreeBSD на старом Атоме, пакетным фильтром работает pf. Основной проблемой стало то, что pf — это statefull-фаервол, т.е. после первого же нашего разрешенного исходящего SYN в адрес Рутрекера в таблицу состояний фаервола заносится запись (state) и все последующие запросы и, что нам особенно важно, ответы в пределах данного TCP-соединения ни через какие правила фаервола более не проходят, они все разрешены.

Подобное поведение сильно увеличивает производительность брандмауэра и сокращает количество правил, но в нашем случае препятствует эффективной фильтрации. Поэтому все запросы/ответы к заблокированным ресурсам будем производить в stateless-режиме, т.е. без сохранения состояния. В pf правила получились такие:

# WAN thru PPPoE
WAN="ng0"
...
block in on $WAN
# -UNBLOCK-
pass out quick on $WAN proto tcp from ($WAN) to 195.82.146.214 no state
block drop in quick on $WAN proto tcp from 195.82.146.214 to ($WAN) flags FPA/FPA
pass in quick on $WAN proto tcp from 195.82.146.214 to ($WAN) no state
#
pass out keep state


Директива no state в разрешающих правилах запрещает сохранение состояния, первое правило в блоке UNBLOCK разрешает исходящие на заблокированный ресурс, второе правило блокирует «поддельные» входящие с установленными флагами FPA, третье — разрешает все остальные входящие. Директива quick во всех правилах прекращает просмотр фаерволом всех последующих правил фильтрации при совпадении с данным правилом. Результат (тройное рукопожатие пропущено):

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-запрос к Рутрекеру
237686 — ответ от провайдерского DPI с флагами FPA (дамп с внешнего интерфейса роутера, поэтому он тут виден; клиент его не получает)
563977 — клиент ответа так и не дождался, повторяет запрос
604853 — настоящий Рутрекер присылает ACK
605043 — настоящий Рутрекер присылает HTTP-ответ

P.S. То же самое можно проделать с помощью любого современного фаервола, pf здесь только потому, что я его использую. Так же внимательный читатель мог заметить, что IP-спуфинг, осуществляемый DPI провайдера, элементарно детектируется по IP TTL (58 против 61). Но pf не умеет фильтровать все поля IP-пакетов, только основные. При использовании других фаерволов несовпадение IP TTL можно использовать как дополнительный признак «поддельного» пакета, наряду с TCP FPA.

That’s All Folks!

© Habrahabr.ru