Ревизор — программно±аппаратный комплекс для мониторинга доступа к сайтам из реестра со стороны провайдеров — берет свое начало в октябре 2015 года, когда компания «МФИ Софт», та же компания, что сделала СОРМы, выиграла тендер на разработку ПО за 84 миллиона рублей. Согласно условиям тендера, разработчик должен был предоставить работоспособное ПО под Windows и Linux и 700 аппаратных «Агентов» в срок до 14.12.2015, всего через 2.5 месяца, и, похоже, все было готово даже на пару недель раньше дедлайна. Провайдерам в добровольно-принудительном порядке уже в начале декабря предлагалось установить один из трех вариантов Ревизора: в виде образа виртуальной машины VMWare, основанной на OpenWRT 14.07, в виде программы-сервиса под Windows, либо же в виде железного «Агента», который представлял из себя маршрутизатор TP-Link MR3020 с установленным на него OpenWRT и необходимым ПО. Многие провайдеры отказывались от установки комплекса из-за того, что он не сертифицирован, а использоваться будет только им во вред, а другим устройств просто не досталось, и им пришлось установить софтовую версию.
Итак, у меня в руках последняя версия VMWare-образа и exe-файла Ревизора. Давайте же посмотрим, что внутри!
Исследование образа VM
Первым делом, образ я переконвертировал в RAW, чтобы иметь возможность просто подмонтировать его, не запуская в вирутальной машине. Для этого есть замечательная утилита в составе qemu, qemu-img, которая умеет конвертировать все популярные форматы образов виртуальных машин между собой:
$ qemu-img convert -O raw ra-wrt-x86-disk1.vmdk rev-clean.raw
Подмонтировать файл образа нам поможет прекрасная программа kpartx из пакета multipath-tools, которая умеет анализировать образ и делать все автоматически, иначе нам бы пришлось монтировать каждый раздел вручную, указывая его смещение, что очень неудобно:
$ sudo kpartx -a rev-clean.raw
Нас встречают два раздела с файловыми системами ext2 и ext4:
Disk: /dev/loop1
Size: 52.5 MiB, 55050240 bytes, 107520 sectors
Label: dos, identifier: 0x00000000
Device Boot Start End Sectors Size Id Type
>> /dev/loop1p1 * 512 8703 8192 4M 83 Linux
/dev/loop1p2 9216 107519 98304 48M 83 Linux
Первый раздел — загрузочный, монтируется в /boot. На нем находится Grub2 и ядро. Ничего интересного.
Второй раздел представляет из себя файловую систему OpenWRT. Каким образом нам найти отличия от штатной сборки OpenWRT? Элементарно — по дате изменения файлов!
2014-04-10+18:34:34.0000000000 ./lib/firmware/rtl_nic/rtl8105e-1.fw
2014-04-10+18:34:34.0000000000 ./lib/firmware/rtl_nic/rtl8106e-1.fw
2014-04-10+18:34:34.0000000000 ./lib/firmware/rtl_nic/rtl8106e-2.fw
2014-04-10+18:34:34.0000000000 ./lib/firmware/rtl_nic/rtl8168d-1.fw
…
2014-09-16+23:45:16.0000000000 ./lib/netifd/netifd-proto.sh
2014-09-16+23:45:16.0000000000 ./lib/netifd/netifd-wireless.sh
2014-09-16+23:45:16.0000000000 ./lib/netifd/utils.sh
2014-09-21+14:46:54.0000000000 ./bin/ipcalc.sh
…
2015-10-23+12:04:49.0000000000 ./bin/revizor_postboot
2015-10-23+12:04:49.0000000000 ./bin/revizor_postupdate
2015-10-23+12:04:49.0000000000 ./dev
2015-10-23+12:04:49.0000000000 ./dev/console
2015-10-23+12:04:49.0000000000 ./etc/agent_id
2015-10-23+12:04:49.0000000000 ./etc/config/dropbear
2015-10-23+12:04:49.0000000000 ./etc/dropbear/dropbear_dss_host_key
2015-10-23+12:04:49.0000000000 ./etc/dropbear/dropbear_rsa_host_key
2015-10-23+12:04:49.0000000000 ./etc/opkg.conf
2015-10-23+12:04:49.0000000000 ./etc/shadow
2015-10-23+12:04:49.0000000000 ./etc/shells
2015-10-23+12:04:49.0000000000 ./etc/ssl
2015-10-23+12:04:49.0000000000 ./etc/ssl/certs
2015-10-23+12:04:49.0000000000 ./etc/ssl/certs/revizor_opkg.crt
2015-10-23+12:04:49.0000000000 ./root
2015-10-23+12:04:49.0000000000 ./root/.ssh
2015-10-23+12:04:49.0000000000 ./root/.ssh/id_rsa
2015-10-23+14:49:17.0000000000 ./etc/crontabs
2015-10-23+14:49:17.0000000000 ./etc/crontabs/root
2015-10-23+14:49:17.0000000000 ./etc/revizor_server
2015-10-29+14:27:19.0000000000 ./bin/revizor_boot
2015-10-29+14:27:19.0000000000 ./etc/config/network
2015-10-29+14:27:19.0000000000 ./etc/netfallback.conf
2015-10-29+14:27:19.0000000000 ./etc/rc.local
2015-11-03+15:43:21.0000000000 ./etc/init.d/dropbear
2015-11-03+15:43:21.0000000000 ./usr/lib/opkg/info/dropbear.conffiles
2015-11-03+15:43:21.0000000000 ./usr/lib/opkg/info/dropbear.control
2015-11-03+15:43:21.0000000000 ./usr/sbin/dropbear
2015-11-03+17:05:22.0000000000 ./bin/admin/admsrv
2015-11-03+17:05:22.0000000000 ./bin/revizor_logger
2015-11-03+17:05:22.0000000000 ./bin/revizor_preboot
2015-11-03+17:05:22.0000000000 ./etc/passwd
2015-11-09+17:10:52.0000000000 ./bin
2015-11-09+17:10:52.0000000000 ./bin/admin/admcli
2015-11-09+17:10:52.0000000000 ./bin/revizor_updater
2015-11-09+17:10:52.0000000000 ./etc/config
2015-11-09+17:10:52.0000000000 ./etc/config/system
2015-11-09+17:10:52.0000000000 ./etc/dropbear
2015-11-09+17:10:52.0000000000 ./etc/dropbear/authorized_keys
2015-11-09+17:10:52.0000000000 ./etc/inittab
2015-11-13+12:06:31.0000000000 ./bin/admin/netfallback
2015-11-16+15:31:23.0000000000 ./bin/admin
2015-11-16+15:31:23.0000000000 ./bin/admin/pwd-sh
2016-02-09+11:09:52.0000000000 ./etc
2016-02-09+11:09:52.0000000000 ./etc/revizor_firmware_version
2016-02-09+11:09:53.0000000000 ./bin/ash
2016-02-09+11:09:53.0000000000 ./bin/cat
2016-02-09+11:09:53.0000000000 ./bin/chgrp
…
Можем проследить процесс создания образа: первые файлы появились 23 октября, затем файлов докидывали в течение ноября, и, похоже, 16 ноября появился готовый базовый образ, который обновили и кастомизировали 9 февраля.
Процесс загрузки скриптов, относящихся к Ревизору, сделан небрежно — они просто добавлены в /etc/rc.local:
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.
/bin/admin/admsrv &
/bin/admin/netfallback &
/bin/revizor_boot &
exit 0
/bin/admin/admsrv меняет пароль пользователя admin путем хеширования первой строки файла /etc/agent_id, который уникальный для каждой виртуальной машины и устройства (вида DICK-BUTT-I386), без первого символа, алгоритмом MD5, и обрезает полученный хеш до 12 символов, который и будет паролем. В самом же файле /etc/agent_id защита от дурака — 28 переносов строк, которые не влезут в стандартный терминал размером 80×24. Вероятно, предполагается, что кто-то не знает про Shift+PgUp/PgDown. Этот же скрипт запускает SSH-сервер (dropbear) на порту 2222, доступный извне, на 2 минуты после старта.
#!/bin/sh
sleep 2
chmod a+rw /etc/opkg.conf
chmod a+rw /etc/netfallback.conf
ADMIN_PORT=2222
ADMIN_TIMEOUT=120
read ADMIN_PWD </etc/agent_id
if [ ! -z ADMIN_PWD ]; then
ADMIN_PWD=`echo $ADMIN_PWD | tail -c +2 | md5sum | head -c 12`
if [ ! -z ADMIN_PWD ]; then
echo "admin password: $ADMIN_PWD" | revizor_logger
echo -e "$ADMIN_PWDn$ADMIN_PWD" | passwd admin
fi
fi
/usr/sbin/dropbear -F -p 0.0.0.0:$ADMIN_PORT -n -K 30 -I 300 &
PID=$!
sleep $ADMIN_TIMEOUT
kill -9 $PID
В скрипте /bin/admin/netfallback происходит непотребство — если через 5 секунд после загрузки Ревизор не получил IP-адрес от DHCP-сервера, он устанавливает статический адрес 192.168.0.254, и далее в цикле каждые 30 секунд пытается понять, не появился ли DHCP-сервер и не выдал ли он нам IP-адрес.
Отвечающий за загрузку ПО скрипт /bin/revizor_boot добавляет собственный репозиторий в файл пакетного менеджера opkg.conf, запускает процесс обновления и стартует cron, в котором настроена проверка обновлений каждые 15 минут. Другие репозитории отсутствуют.
#!/bin/sh
if [ ! -f /rom/etc/opkg.conf ]; then
read REVIZOR_SERVER </etc/revizor_server
if [ -z "$REVIZOR_SERVER" ]; then
REVIZOR_SERVER="revizor.mfisoft.ru"
fi
mkdir -p /rom/etc
OPKG_CFG=`cat /etc/opkg.conf | grep -v '^src revizor '`
echo "$OPKG_CFG" > /rom/etc/opkg.conf
echo "src revizor https://$REVIZOR_SERVER/updates/openwrt-x86/common" >> /rom/etc/opkg.conf
cp -f /rom/etc/opkg.conf /etc/opkg.conf
fi
rm -f /usr/lib/opkg/lock
/bin/revizor_preboot
sleep 2
/bin/revizor_updater -f /rom/etc/opkg.conf
/etc/init.d/cron start
/bin/revizor_postboot
Файлы /bin/revizor_preboot и /bin/revizor_postboot пусты.
Скрипт /bin/admin/pwd-sh, который используется в качестве логин-шелла (прописан в /etc/inittab для tty1 и ttyS0), использует крайне необычную технику входа — запуск SSH-клиента на localhost. Дело в том, что OpenWRT используется преимущественно в домашних роутерах, у которых нет ни экрана, ни легкого доступа к параллельному порту, поэтому пароль на доступ просто не нужен. В случае Ревизора это бы означало, что любой может получить root-доступ, просто нажав Enter для активации консоли в виртуальной машине. Как правило, для осуществления логина используют программы вроде getty, вы их видели в любом дистрибутиве, именно getty спрашивает и проверяет логин и пароль. Здесь же, по какой-то причине, разработчики не стали устанавливать getty, а просто подключались к самому себе через SSH, ведь SSH-сервер запросит аутентификацию. Поначалу я думал, что сделано это не просто так, что таким образом захотели хитро отключить вход из-под пользователя root, но нет, SSH-сервер настроен самым обычным образом.
Шеллом для пользователя admin задан скрипт /bin/admin/admcli, который позволяет выполнять следующие команды:
system reboot
system resetfs
system update
log
info
ifconfig
route
arp
ping
nslookup
traceroute
net proxy clear
net proxy set
net fallback
Пакеты из репозитория подписываются ключом «МФИ Софт», подпись проверяется средствами opkg:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 12303214825491704792 (0xaabdccb2d4c0abd8)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=RU, ST=Russia, O=MFISOFT
Validity
Not Before: Oct 21 10:21:46 2015 GMT
Not After : Aug 5 10:21:46 2289 GMT
Subject: C=RU, ST=Russia, O=MFISOFT
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:cc:ed:e0:84:c4:7b:4e:49:2d:11:86:41:0f:f8:
51:97:42:91:76:34:38:96:e0:9e:a4:3c:7b:30:f6:
15:b2:1e:03:0e:12:46:96:f9:57:a1:db:2d:63:8a:
dc:01:2e:e7:10:56:8d:c3:d5:de:5a:bb:d7:75:e3:
6b:e3:d5:6a:04:4d:f4:65:81:05:07:d7:d0:a8:29:
ab:9d:83:81:00:04:73:27:39:db:d3:c8:ba:d3:78:
41:84:d9:8b:62:21:00:51:fc:78:06:ce:f7:db:e6:
5b:fd:d7:b6:2b:0f:72:9e:63:d8:06:f1:dd:2d:c5:
17:f1:a9:b8:d3:5e:ad:6c:d5
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
F6:F9:BB:39:1B:20:4F:B4:11:B5:CE:EA:C2:F5:95:DB:24:DB:49:53
X509v3 Authority Key Identifier:
keyid:F6:F9:BB:39:1B:20:4F:B4:11:B5:CE:EA:C2:F5:95:DB:24:DB:49:53
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
16:31:a0:2f:01:1b:06:a3:31:d3:d2:50:38:b4:c2:57:ec:6d:
a0:25:5e:e0:35:68:92:dd:38:fc:1a:ef:88:2d:e8:b9:1b:d7:
f5:ef:97:14:75:ef:65:1c:f9:ae:61:43:05:49:74:08:8a:d5:
19:01:e3:63:ff:69:57:34:74:9e:b8:7d:6d:5b:2a:66:59:a6:
9d:b4:a3:3f:41:91:30:26:1f:0e:3a:24:2b:36:0e:68:f8:e8:
44:f5:5a:18:ea:5e:48:8e:a9:8f:03:25:87:ba:60:9c:93:ac:
cb:43:b7:ee:6d:6c:85:88:77:40:a7:b4:a8:c9:ce:d0:29:6d:
78:0a
-----BEGIN CERTIFICATE-----
MIICMDCCAZmgAwIBAgIJAKq9zLLUwKvYMA0GCSqGSIb3DQEBCwUAMDAxCzAJBgNV
BAYTAlJVMQ8wDQYDVQQIDAZSdXNzaWExEDAOBgNVBAoMB01GSVNPRlQwIBcNMTUx
MDIxMTAyMTQ2WhgPMjI4OTA4MDUxMDIxNDZaMDAxCzAJBgNVBAYTAlJVMQ8wDQYD
VQQIDAZSdXNzaWExEDAOBgNVBAoMB01GSVNPRlQwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBAMzt4ITEe05JLRGGQQ/4UZdCkXY0OJbgnqQ8ezD2FbIeAw4SRpb5
V6HbLWOK3AEu5xBWjcPV3lq713Xja+PVagRN9GWBBQfX0Kgpq52DgQAEcyc529PI
utN4QYTZi2IhAFH8eAbO99vmW/3XtisPcp5j2Abx3S3FF/GpuNNerWzVAgMBAAGj
UDBOMB0GA1UdDgQWBBT2+bs5GyBPtBG1zurC9ZXbJNtJUzAfBgNVHSMEGDAWgBT2
+bs5GyBPtBG1zurC9ZXbJNtJUzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4GBABYxoC8BGwajMdPSUDi0wlfsbaAlXuA1aJLdOPwa74gt6Lkb1/XvlxR172Uc
+a5hQwVJdAiK1RkB42P/aVc0dJ64fW1bKmZZpp20oz9BkTAmHw46JCs2Dmj46ET1
WhjqXkiOqY8DJYe6YJyTrMtDt+5tbIWId0CntKjJztApbXgK
-----END CERTIFICATE-----
Вход по SSH открыт пользователю, обладающему следующим ключом:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCAxFzEe20FUIegQ8p25S/b1SIhVi0XTWZtLDF7FLpMsoxi+JhgzoVEwmCIpoQ9c5Flid0jiqKCVhnm8GRe+qjkxibAOa8WlfiQ16eapqA0Dd6laFW4RzTTiinebPRlLJBsj8xGhrvf4lsKXng5+ZDWXnrz7pICbh62U7MYNEpOuy9x4P4285Xq9ccIuCrCAS8rZ4TdFdzeM+270asIQB/vsQ2joJ1vNn3WzdISmRepknR4eTo6H881vHAiWVTpGioXssvOGyLYfqn0rqVECC9/tknV0hQJP+iYU3mov4+JYvRVa+5m1DLD0Nj0QWKFXl79VNxstwyOt6RDvQrhlxNB root@revizor-agent
Но где же сам Ревизор?
Обновляемся!
Файлов самого ПО в прошивке нет, они поставляются пакетом из репозитория. Загрузим список пакетов с сайта:
n01.rfc-revizor.ru/updates/openwrt-x86/common/Packages
Как видим, репозиторий содержит один пакет «revizor»:
Package: revizor
Version: 1.2.2-34720
Depends: libc, libstdcpp, libpthread, libpcre, libopenssl
Section: utils
Architecture: x86
MD5Sum: 0afc31c21b785690ca38a89d24d749ed
Size: 322098
Filename: revizor_1.2.2-34720_x86.ipk
Source: package/revizor
Description: revizor agent
Скачаем же его!
n01.rfc-revizor.ru/updates/openwrt-x86/common/revizor_1.2.2-34720_x86.ipk
Файл формата IPK представляет из себя архив .tar.gz со структурой DEB-пакета. Собран он неким abelyak.
Внутри пакета имеется несколько скриптов и два исполняемых ELF-файла: revizor-crypto и urlcheck. Первый совсем простой, на данный момент, насколько я понимаю, не используется, и служит для получения публичного ключа из приватного, чтобы использовать его в качестве идентификатора ноды. Файл ключа, который передается в качестве параметра этой утилите, отсутствует, и идентификатор ноды генерируется каждый раз разный.
Перейдем к главному Ревизору — файлу urlcheck. Он написан на C++, динамически слинкован, использует libevent и его OpenSSL-обертку, и обладает нижеперечисленными функциями:
- Выполнение HTTP/HTTPS GET/POST-запросов по определенному URL
- Выполнение запросов по определенному URL с заданным IP-адресом (без разрешения доменного имени через DNS)
- Выполнение нестандартных запросов для обхода DPI: добавление точки в конец домена, двойной слеш в начале URL, экранирование URL
- Определение факта блокировки сайта путем поиска совпадений регулярного выражения в теле и заголовках ответа сервера
- Отправка ICMP-запросов к определенному хосту
- Запуск traceroute до определенного хоста
- Создание SSH-туннеля до сервера Ревизора для предоставления Socks5-прокси
- Отправка журнала из syslog на сервер разработчика
- Перезагрузка устройства
Программа поддерживает IPv6 и работу через прокси.
Общение с сервером Lens, как его называют сами разработчики, происходит по протоколу JSON-RPC, для чего используется URL n01.rfc-revizor.ru/rpclens. Lens отправляет клиенту «задания», которые он должен выполнить. Каждое задание имеет идентификатор, тип, различные опции и параметры. Для проверки заблокированности веб-сайтов сервер передает клиенту белый список адресов, который на данный момент состоит из сайтов ya.ru, google.ru, cbr.ru, gov.ru, hotlog.ru, kremlin.ru, onf.ru, ria.ru, rostelecom.ru, kp.ru, и случайные URL из реестра запрещенных сайтов. Проверка выполняется путем поиска совпадений по регулярным выражениям в теле и заголовке ответа от веб-сайта. Если какие-то ссылки, которые не должны были открыться, не проходят проверки по регулярным выражениям, сервер отсылает запрос на предоставление SSH-туннеля до Socks5-прокси, который поднимается самим urlcheck, чтобы самостоятельно загрузить страницу с помощью curl, проанализировать ее, и сделать скриншот с помощью wkhtmltoimage.
Помимо заданий, сервер также может корректировать настройки Ревизора, например, изменять таймаут подключения и ожидания ответа, ограничивать количество одновременных подключений, устанавливать размер пула потоков для DNS-резолвера.
Развлекаемся!
Хоть общение с API и происходит по протоколу HTTPS, а у сервера имеется сертификат, выданный GeoTrust, Ревизор не выполняет проверку подлинности сертификата, а это значит, что ничто не помешает нам выполнить атаку типа «человек посередине», чтобы прослушивать и модифицировать проходящий трафик. По всей видимости, на сертификаты просто не хватило места в аппаратном решении: в TP-Link MR3020 памяти всего 4 МБ, а образ для x86 не стали переделывать.
Запускаем mitmproxy!
Общение с сервером начинается с команды SetMyParams, в которой клиент передает версию ПО, свой идентификатор агента и случайные 4 цифры в качестве идентификатора сессии.
POST /rpclens HTTP/1.1
Host: n01.rfc-revizor.ru
Connection: close
Content-Length: 176
{"method":"SetMyParams","params":{"version":"WRT-1.2.2.34720","traf":{"duration":3600,"bytes_in":24055,"bytes_out":32636}},"id":"DICK-BUTT-I386---1AE822EF40","session_id":1488}
Server: nginx
Date: Mon, 01 Apr 2016 12:34:56 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/5.2.6
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
{"jsonrpc":"2.0","result":{"status":"done"},"id":"DICK-BUTT-I386---1AE822EF40"}
Далее происходит вызов GetMyTasks, в ответ на который сервер возвращает какое-то задание для клиента. Вот пример настроек для проверки сайтов:
{"method":"GetMyTasks","params":"","id":"DICK-BUTT-I386---1AE822EF40","session_id":1488}
{"jsonrpc":"2.0","result":{"tasks":[{"id_task":"493629","id_task_meta":null,"type":"check","priority":"1","checklist":"own","checklist_count":"2","params":"{"checklist":{"group_id":1,"records":{"records_type":2},"requests":{"get":1,"post":0,"use_dns":1,"check_escaped":0,"add_slashes":0,"add_dot":0,"randomize":0,"report_success":0,"max_redirects":5,"use_dns_only":1,"all_resolved_ips":0},"screenshots":{"fail_screenshots":1,"skip_if_protocol_exist":0,"skip_if_exists_hours":null,"skip_if_over":null,"only_200":1,"skip_3xx":null}}}","status":"CREATED","completion":null,"result":null,"pass":null,"fail":null,"passed_items":null,"failed_items":null,"id_creator":"WWW-ANUS-PYOS","id_lens":"DICK-BUTT-I386---1AE822EF40","ts_create":"1461299321","ts_start":null,"ts_stop":null}],"params":{"DnsThreadsMax":20,"MAXfailedChecklistDownloadCount":100,"MAXfailedReportUploadCount":25,"whiteCheckMinInterval":60000,"connectTimeout":10000,"soTimeout":10000,"maxTotalConnections":50,"maxHttpsConnections":20,"maxContentSize":3000},"ts":1461299347,"zip":1,"tests":[{"id":1,"statusCode":"200","header":null,"headerRegexp":null,"contentRegexp":"u0437u0430u0431u043bu043eu043au0438u0440u043eu0432u0430u043d","content":null},{"id":9,"statusCode":"200","header":null,"headerRegexp":null,"contentRegexp":"u043eu0433u0440u0430u043du0438u0447u0435u043d","content":null},{"id":2661,"statusCode":"409","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2919,"statusCode":"404","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2922,"statusCode":"403","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2923,"statusCode":"451","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2924,"statusCode":"500","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2925,"statusCode":"502","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2926,"statusCode":"503","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2932,"statusCode":"307","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2936,"statusCode":"301","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2967,"statusCode":"302","header":null,"headerRegexp":null,"contentRegexp":".*","content":null},{"id":2968,"statusCode":"302","header":"Location","headerRegexp":"62.33.207.195","contentRegexp":null,"content":null},{"id":3228,"statusCode":"404","header":"Connection","headerRegexp":"close","contentRegexp":null,"content":null},{"id":3580,"statusCode":"307","header":"Location","headerRegexp":".*","contentRegexp":null,"content":null}]},"id":"DICK-BUTT-I386---1AE822EF40"}
Как мы можем видеть, Ревизор будет определять страницы-заглушки по наличию слов «заблокирован» и «ограничен», будет искать IP-адрес заглушки провайдера ТТК 62.33.207.195 в заголовке, а также проверять статус, отдаваемый веб-сервером.
Чтобы установить SSH-туннель с клиентом, сервер посылает команду tunnel_on с портом и количеством миллисекунд, после которого его следует завершить, в качестве параметров к команде:
{"method":"GetMyTasks","params":"","id":"DICK-BUTT-I386---1AE822EF40","session_id":1488}
{"jsonrpc":"2.0","result":{"tasks":[{"id_task":"148411","id_task_meta":null,"type":"service","priority":"1","checklist":null,"checklist_count":"0","params":"{"format":1,"command":"tunnel_on","param1":64123,"param2":60000}","status":"RUNNING","completion":"0","result":null,"pass":"0","fail":"0","passed_items":null,"failed_items":null,"id_creator":"N01-KONA-CHAN","id_lens":"DICK-BUTT-I386---1AE822EF40","ts_create":"1460000000","ts_start":"1460000000","ts_stop":null}],"params":{"DnsThreadsMax":20,"MAXfailedChecklistDownloadCount":100,"MAXfailedReportUploadCount":25,"whiteCheckMinInterval":60000,"connectTimeout":10000,"soTimeout":10000,"maxTotalConnections":50,"maxHttpsConnections":20,"maxContentSize":3000},"ts":1460000000,"zip":1,"tests":null},"id":"DICK-BUTT-I386---1AE822EF40"}
После этого система запускает SSH-клиент Dropbear, который часто используют во встраиваемых системах, путем совершения вызовов fork() и execv(), со следующими параметрами:
/usr/bin/ssh -y -y -K 30 -N -T -R 0.0.0.0:6412:127.0.0.1:1080 -p 22 -i /root/.ssh/id_rsa
Два параметра -y отключают проверку ключа SSH-сервера (зачем?), -N и -T отключают выполнение команд, а -R включает режим проброса порта от сервера клиенту, т.е. открывает на стороне сервера заданный порт (64123) и перенаправляет запросы с него на порт 1080 клиента, на прослушивание которого настроен Socks5-сервер.
Конечно же, первым делом -R была заменена на -D, однако на все исходящие соединения возвращался ICMP Administratively Prohibited.
Если подумать, никто нам не запрещает указать несколько портов для проброса. Почему бы не воспользоваться этим моментом, и не пробросить на себя чуточку больше портов, скажем, диапазон с 1024 до 65535? К сожалению, одно соединение может пробросить около 1000 портов. Не знаю, чем вызвано это ограничение, быть может, виноваты настройки ulimit, а может и OpenSSH. Итак, Socks5-сервер запущен, 65 SSH-соединений установлено, и мы принимаем первое подключение на порт прокси!
Что же произошло? Проверяющий сервер отправляет какому-то действующему Ревизору команду tunnel_on с портом, не проверяя, используется ли он уже кем-то в системе, Ревизор подключается к серверу, но пробросить порт не может, так как мы заняли его раньше. Сервер подключается к нашему Socks5-серверу и открывает сайты, думая, что открывает их через проверяемого провайдера, у которого стоит Ревизор.
Прошу прощения у пострадавшего провайдера, у которого внезапно открылись все заблокированные сайты, да еще и из Чехии!
Вместо заключения
Делать API по HTTPS, но отключать проверку TLS- и SSH-сертификатов? Использовать роутеры с 4 МБ ROM стоимостью 1500₽ в розницу? Держать сервер с Debian Oldstable без обновлений безопасности? За 84 миллиона такое можно себе позволить.
Быть может, Роскомнадзор затеял этот проект из-за зависти к Blockcheck? Кто знает…
Автор: ValdikSS