Исследуем «Ревизор» Роскомнадзора

ФСБшник хороший

Ревизор — программно±аппаратный комплекс для мониторинга доступа к сайтам из реестра со стороны провайдеров — берет свое начало в октябре 2015 года, когда компания «МФИ Софт», та же компания, что сделала СОРМы, выиграла тендер на разработку ПО за 84 миллиона рублей. Согласно условиям тендера, разработчик должен был предоставить работоспособное ПО под Windows и Linux и 700 аппаратных «Агентов» в срок до 14.12.2015, всего через 2.5 месяца, и, похоже, все было готово даже на пару недель раньше дедлайна. Провайдерам в добровольно-принудительном порядке уже в начале декабря предлагалось установить один из трех вариантов Ревизора: в виде образа виртуальной машины VMWare, основанной на OpenWRT 14.07, в виде программы-сервиса под Windows, либо же в виде железного «Агента», который представлял из себя маршрутизатор TP-Link MR3020 с установленным на него OpenWRT и необходимым ПО. Многие провайдеры отказывались от установки комплекса из-за того, что он не сертифицирован, а использоваться будет только им во вред, а другим устройств просто не досталось, и им пришлось установить софтовую версию.

Итак, у меня в руках последняя версия VMWare-образа и exe-файла Ревизора. Давайте же посмотрим, что внутри!
image

Исследование образа 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 


В скрипте /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  /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.
image
Внутри пакета имеется несколько скриптов и два исполняемых 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":"\u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d","content":null},{"id":9,"statusCode":"200","header":null,"headerRegexp":null,"contentRegexp":"\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d","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-серверу и открывает сайты, думая, что открывает их через проверяемого провайдера, у которого стоит Ревизор.
image
Прошу прощения у пострадавшего провайдера, у которого внезапно открылись все заблокированные сайты, да еще и из Чехии!

Вместо заключения

Делать API по HTTPS, но отключать проверку TLS- и SSH-сертификатов? Использовать роутеры с 4 МБ ROM стоимостью 1500₽ в розницу? Держать сервер с Debian Oldstable без обновлений безопасности? За 84 миллиона такое можно себе позволить.
Быть может, Роскомнадзор затеял этот проект из-за зависти к Blockcheck? Кто знает…

© Habrahabr.ru