Новость: Магистральный провайдер RETN, не смотря на то, что он магистральный, осуществляет фильтрацию трафика посредством DPI. Поскольку оператор он магистральный, и, в частности, занимается доставкой зарубежного трафика, то на выходе мы имеем цензуру для многих провайдеров, в том числе и тех, кто чхать хотел на всякие «запретные списки», но имеют RETN в аплинках.
DPI — совокупное название технологий, при которых оборудование «залазит» во внутрь трафика, то есть реагирует не только на заголовки пакетов разного уровня, но и на содержимое.
Во избежание помех тест проводился из нескольких городов и с нескольких провайдеров, что позволило исключить фактор локальных провайдерских фильтраций (второй, косвенный тест основывался на использовании TTL-сканирования, который всегда указывал на район RETN'а).
Посмотрим, как именно RETN осуществляет DPI, ревностно исполняя законы о защите наркотиков от суицидальных порнографических детей, нарушающих авторские права.
За основу возьмём широко известный журнал Стервозинки (широко известен он только тем, что был заблокирован, заблокирован за какуют-то нелепицу, причём заблокирован давно, и из бана выползать не собирается).
Адрес этого поста внесён в список запрещённых к просмотру гражданами Российской Федерации. В связи с этим я осуществил необратимые манипуляции с доменным именем журнала таким образом, чтобы не существовало ни одного достоверного и однозначного алгоритма обращения получившейся хеш-функции.
Посмотрим на бытовые симптомы проблемы:
wget -d --tries 1 http://stervozzinka.dreamwidth.og/15580.html
Рядом на консоли запускаем tcpdump host stervozzinka.dreamwidth.og
17:17:14.376828 IP local.49510 > dreamwidth.og.http: Flags [P.], seq 1:136, ack 1, win 115, options [nop,nop,TS val 11199749 ecr 1627034663], length 135 17:17:17.924801 IP local.49510 > dreamwidth.og.http: Flags [P.], seq 1:136, ack 1, win 115, options [nop,nop,TS val 11200636 ecr 1627034663], length 135 17:17:18.068805 IP local.49509 > dreamwidth.og.http: Flags [P.], seq 1:136, ack 2, win 115, options [nop,nop,TS val 11200672 ecr 1627029045], length 135
Одинаковый seq — признак перепосыла сегмента. Но понять, где блокируют (на получении ответа или на отправке запроса) мы не можем. Но точно видим, что таки блокируют, ибо просто так TCP-сегменты не перепосылают.
Переключимся со своевольного wget'а на что-то попроще, чтобы точно контролировать отправляемое:
echo -e "GET /15580.html HTTP/1.1nHost: stervozzinka.dreamwidth.ogn"|nc stervozzinka.dreamwidth.og 80
Это нас никак не продвинет, однако даст некоторую свободу экспериментировать с заголовками. Указанный запрос так же блокируется.
А вот для вариаций (которые нарушение RFC, но varnish'ем со стороны dreamwidth обрабатываются нормально) мы можем увидеть некоторые особенности:
GET /15580.html HTTP/1.1nHost: stervozzinka.dreamwidth.ogn"
(два пробела после GET) — пускаетGET /15580.html HTTP/1.1nHost: stervozzinka.dreamwidth.ogn
(два пробела перед HTTP/1.1 — не пускаетget /15580.html HTTP/1.1nHost: stervozzinka.dreamwidth.ogn
(get маленькими буквами) — пускаетGET /15580.html HTTP/1.1nIgnore:menHost: stervozzinka.dreamwidth.ogn
(лишний заголовок между GET и HOST) — не пускает
Предварительный вывод — скучный и примитивный exact matching. Если так, то как оно понимает, что из содержимого пакета заголовок, а что нет?
Так что…
echo -e "GET /15580.htmlnnHost: stervozzinka.dreamwidth.ogn"|nc stervozzinka.dreamwidth.og 80
— не пускает.
Для тех, кто не понял — я поставил два перевод строки после GET, то есть Host уже относится к телу, а не к заголовку. А ещё я убрал HTTP/1.1, то есть это plain HTTP 1.0, у которого не бывает заголовка Host, то есть мы запросили /15580.html с сервера без указания на hostname.
Заметим, запрос без hostname работает: GET /15580.html nn
Другими словами, мы видим, что DPI проверяет что-то совсем несусветное — наличие Host в BODY. В результате дропаются запросы, к блокируемому сайту не имеющие никакого отношения.
Усложним эксперимент:
echo -e «POST /nnА ты знаешь, что они банят по содержимому? Например: GET /15580.html nnHost: stervozzinka.dreamwidth.ogn"|nc stervozzinka.dreamwidth.og 80
Ой, ой, ой. Нам забанили отправку POST'а с невинным содержанием. Этот POST не дошёл до сервера. Не может быть?
Давайте проверим, и отправим пост более культурными методами:
curl -d "GET /15580.html nnHost: stervozzinka.dreamwidth.ogn" dreamwidth.og
Наше предположение — фильт требует наличия обоих строчек в одном пакете и не проверяет его валидность:
curl --connect-timeout 10 -d "GET /15580.htmln`seq 1 10000`nHost: stervozzinka.dreamwidth.ogn" 69.174.244.50
Проходит. Это означает, что пакете должны быть оба заголовка. (да-да, если мы напишем такой запрос на сервер, у которого Host: в заголовке пойдёт другим пакетом, то, возможно, мы сможем пробить цензуру.
Ещё одна проверка: проверяют ли люди номер порта?
echo -e "GET /nHost: stervozzinka.dreamwidth.ogn"|nc dreamwidth.og 443
(пустой ответ)
echo -e "GET /15580.htmlnHost: stervozzinka.dreamwidth.ogn"|nc dreamwidth.og 443
(таймаут)
Нет. Трафик на 443ий порт фильтруют с тем же успехом (пропускают обычный, дропают „запрещённый“).
Ещё одна проверка: фильтруют ли по IP? Находим соседний (из того ж сегмента) открытый IP, отвечающий на 80ом порту. Пускают.
Итог
Условия для дропа пакета:
- На любом порту TCP (UDP не проверялось)
- С любыми флагами
- По фактическому наличию в пакете (в любом порядке) строк
- GET /15580.html
- Host: stervozzinka.dreamwidth.og
- Совпадению src_IP с указанным в списке для запрета
Таким образом, это больше напоминает packet filter с поиском сигнатур без регэкспов в проходящих пакетах, а вовсе не не настоящий DPI.
Автор: amarao