После прочтения статьи уважаемого ValdikSS решил изучить DPI своего провайдера и, при благоприятном развитии событий, найти пути его обхода. Ниже — результаты моих изысканий.
Итак, провайдер — ТТК Ульяновск (бывш. DARS Telecom), PPPoE-подключение с выделением внешнего IP на время сессии. Блокировка осуществляется заворачиванием заблокированных подсетей/хостов на свой DPI. DNS не подменяется.
Причем некоторые сайты просто и незатейливо заблокированы по IP (спасибо хоть RST присылают). Например, bt-анонсеры Рутрекера:
Для тех же сайтов, где нужно показать страничку блокировки работает пассивный DPI. Рассмотрим на примере Рутрекера:
Посмотрим на данный вывод подробнее. С момента 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 правила получились такие:
Директива no state в разрешающих правилах запрещает сохранение состояния, первое правило в блоке UNBLOCK разрешает исходящие на заблокированный ресурс, второе правило блокирует «поддельные» входящие с установленными флагами FPA, третье — разрешает все остальные входящие. Директива quick во всех правилах прекращает просмотр фаерволом всех последующих правил фильтрации при совпадении с данным правилом. Результат (тройное рукопожатие пропущено):
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!
Итак, провайдер — ТТК Ульяновск (бывш. 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="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 ...
Директива 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!
