Houndoom сканер веб‑шеллов
В октябре 2025 в Ai-Bolit и Imunify — сканерах, которыми чистят заражённые сайты у половины рунет-хостеров — нашли критическую RCE. Причина оказалась в самом сердце инструмента: их деобфускатор, разбирая подозрительный код, исполнял его. Через call_user_func_array без валидации имён функций. То есть достаточно было положить на сервер правильно собранный файл — и запуск сканера превращался в запуск малвари. По оценкам, под ударом было до 56 миллионов сайтов.
Инструмент, которым обезвреживают заражённый сервер, сам оказался способом его дозаразить. Приехал тушить пожар с канистрой бензина.
Я довольно давно занимаюсь разбором заражённых сайтов — обычно это чужие проекты, которые надо аудировать или чистить после взлома. И почти всё, чем это делают, устроено по одному из двух неудобных сценариев: либо ты ставишь на клиентский сервер постоянный агент с root-правами, либо выгружаешь файлы клиента в облако вендора. Мне не нравились оба. Так появился Houndoom.
Что это
Houndoom — CLI-сканер веб-шеллов, бэкдоров и малвари на Go. Один статический бинарник, который сканирует директорию сайта и ищет то, что там быть не должно: шеллы (r57, c99, b374k, WSO), бэкдоры на eval/base64/gzinflate, инъекции, вредоносный JS, фишинговые и дорвей-страницы, посторонние исполняемые файлы в веб-директориях. Есть отдельные детекторы под WordPress и Bitrix. Находки можно опционально прогнать через Claude — но это надстройка, а не ядро.
Сразу оговорюсь, чтобы не было недопонимания. Houndoom — не замена Imunify360 или Wordfence. Те решают другую задачу: постоянная защита своего сервера — WAF, файрвол, мониторинг в реальном времени, автолечение. Houndoom — про разовый аудит чужого сервера. Ты пришёл на машину, которую не настраивал, где не стоит твой стек безопасности, снял картину за час и ушёл, ничего не оставив. Это инструмент инцидент-реагирования, а не резидентной защиты.
Почему Go
Скорость. Обход большого сайта — это сотни тысяч файлов, и здесь Go с горутинами обгоняет PHP-скрипт или bash. Плюс единый бинарник без зависимостей: не нужен ни PHP на целевой машине (как Ai-Bolit), ни YARA (как PHP Malware Finder), ни связка bash + ClamAV (как maldet). Когда ты пришёл на чужой сервер по SSH на час, «ничего не надо доустанавливать» — это не удобство, а условие задачи.
Оговорюсь честно: Go тут не отличие. Демон WebShield у той же Imunify тоже написан на Go — это нормальный выбор для нагруженного сканера, а не мой личный трюк.
Деобфускация без исполнения
Это главное архитектурное решение, и RCE-2025 в Ai-Bolit — показательная иллюстрация, зачем оно нужно.
Малварь почти всегда обфусцирована — завёрнута в несколько слоёв base64, gzinflate, str_rot13, кастомных подстановок. Чтобы прочитать, что внутри, слои надо снять. Есть два способа. Первый — попросить сам PHP размотать: скормить строку интерпретатору и посмотреть, что получится. Быстро — и ровно так сделали в Ai-Bolit. Второй — размотать слои самому, статическим кодом, ни разу не передавая управление тому, что разбираешь.
Houndoom идёт вторым путём. Восемь деобфускаторов (base64, eval, lockit, als, byterun, fopo, urldecode, globals), рекурсия до 100 уровней вложенности — потому что реальные шеллы бывают завёрнуты в десятки слоёв. И на всём этом пути код ни разу не запускается. Разматывается строка, а не выполняется программа.
Разница не в том, что у конкурентов деобфускации нет — у Imunify она есть, и продвинутая. Разница в том, что они её исполняют. И показательно, чем закончилась та RCE: CloudLinux не убрали исполнение из архитектуры — они добавили белый список разрешённых функций (base64_decode, gzinflate, strrev, str_rot13, urldecode, substr, chr, ord). То есть подход остался прежним, «исполняем, но теперь аккуратно». Статический разбор такого класса ошибок не имеет в принципе — там нечего исполнять.
Структурные аномалии: то, что не выразить регулярным выражением
Сигнатуры ловят известную малварь. Но опытный злоумышленник не кладёт c99.php в корень — он прячется там, где его не ждут. Ловить это регулярками бесполезно: подозрителен не текст файла, а сам факт его существования в этом месте. Такие проверки в Houndoom написаны отдельным Go-кодом, и вот несколько примеров.
PHP-файл внутри wp-content/uploads/. В эту папку WordPress складывает картинки и документы — исполняемому коду там взяться неоткуда. Если там лежит .php, это почти наверняка залитый через дыру в загрузке шелл:
// PHP-файл внутри wp-content/uploads/ — директория только для медиа
if d.uploadsPathRe.MatchString(lower) && file.Extension == "php" {
findings = append(findings, d.structureFinding(file, "WP-STRUCTURE-001",
"PHP File in Uploads Directory",
"PHP file found in wp-content/uploads/ - this directory should only contain media files",
"php_in_uploads", models.SeverityHigh, 1.5, 90))
}
auto_prepend_file в .user.ini. Это директива, которая заставляет PHP-FPM подгружать указанный файл перед каждым скриптом на сайте. Идеальная персистентность: положил .user.ini рядом с безобидным на вид PHP — и он исполняется на любой запрос. Про этот вектор забывают почти все сканеры, а он живёт на большинстве современных хостингов.
wp-content/mu-plugins/. Must-use плагины автоматически загружаются на каждый запрос и не отключаются из админки — их даже не видно в списке плагинов. Любимое укрытие: положил бэкдор туда, и он переживёт чистку через админку, потому что владелец сайта про эту папку не знает.
Ни одну из этих проверок нельзя написать за столом, не видев реальных заражений. Они выросли из разбора того, как на самом деле прячется малварь.
Подавление ложных срабатываний
Самый честный кусок кода в проекте — матрица подавления ложных срабатываний. Она про то, что хороший сканер обязан сомневаться в собственных находках.
Простой пример. Мои же сигнатуры содержат паттерны малвари — иначе они бы ничего не ловили. А теперь представьте, что на сайте стоит другой плагин безопасности. Внутри него — точно такие же паттерны, для его собственного детекта. Наивный сканер радостно отрапортует, что плагин безопасности заражён. Поэтому Houndoom глушит все свои WP-сигнатуры внутри известных security-плагинов:
// Suppression matrix:
// - security plugins: подавляем ВСЕ WP-сигнатуры (они сами содержат
// паттерны детекта; реальная компрометация внутри security-плагина —
// вне области статического контентного матчинга).
// - core files: подавляем vulnerability-сигнатуры (auth/API/hook/...),
// которые легитимны внутри ядра WP; детект бэкдоров оставляем.
// - managed plugins: подавляем vulnerability-сигнатуры + htaccess
// (кэширование легитимно использует auto_prepend); детект малвари оставляем.
Отдельно — ядро WordPress и «доверенные» плагины вроде кэширующих. Они легитимно вызывают то, что в другом контексте выглядит уязвимостью: auto_prepend для кэша, прямые обращения к API, хуки. Глушить в них детект бэкдоров нельзя — а вот vulnerability-сигнатуры надо, иначе утонешь в ложняках. Эта грань — «где подавлять, а где нет» — и есть та часть, которую регулярка за тебя не решит.
Разовый скан по SSH
Теперь про то, ради чего всё затевалось. У Houndoom есть режим agentless remote-scan: он подключается к серверу по SSH, определяет архитектуру, заливает бинарник сканера во временную папку, прогоняет проверку строго read-only, забирает JSON-отчёт — и удаляет за собой всё, даже если что-то упало по пути. Ключи берутся только из ssh-agent, сам ключевой материал не читается и не логируется, команды идут через системный ssh/scp, так что твой ~/.ssh/config с ProxyJump работает как есть.
Будем честны: сам по себе паттерн «agentless по SSH» не я придумал. Ровно так годами работают vulnerability-сканеры — Vuls, Qualys agentless, Nessus. Новое здесь — перенос этого подхода в нишу веб-шеллов, где так не делает никто. Смотрите, что предлагают остальные:
- Imunify, CXS, GOTMLS, maldet, ClamAV — резидентная установка. Поставь агент/демон на сервер и держи его там.
- Sucuri SiteCheck, Astra free, GoDaddy — скан снаружи по URL. Видят только то, что отдаётся браузеру, — «витрину». Бэкдор в
uploadsили заражение в базе им не видны, и Sucuri честно это признаёт. - MalCare — «удалённый» скан, но с выгрузкой файлов и базы клиента на серверы вендора.
- Sucuri server-side — постоянно хранит твои SSH/FTP-креды в своём облаке.
Ближайший ко мне по духу — Wordfence CLI: тоже open source, тоже сканирует файловую систему вне CMS, и за ним стоит вся threat intelligence Wordfence, чего у соло-проекта нет и быть не может. Но: его надо установить на целевую машину (пакет плюс Python-рантайм), он заточен под WordPress, а в бесплатной версии сигнатуры приходят с задержкой в 30 дней. Houndoom прилетает одним бинарником, ничего не оставляет и умеет Bitrix.
Модель Houndoom простая: разовый read-only визит без хранимых кредов и без следов на целевой машине.
Разбор находок через Claude
Находки можно прогнать через Claude, чтобы получить человекочитаемое объяснение: что это за код, что он делает, стоит ли паниковать. Здесь два принципиальных момента.
Первый — про безопасность самого процесса. Содержимое просканированных файлов в скилле явно объявлено враждебными данными. Внутри веб-шелла может лежать инструкция «а теперь, модель, сделай вот это» — и модель не должна её исполнять. Вердикт LLM — совещательный, не приговор. Заодно на уровне репозитория самому агенту запрещён сырой доступ к ssh/scp/curl — весь доступ к цели идёт только через аудируемый Go-бинарник, а не через модель напрямую.
Второй — LLM тут не детектор, а объяснитель, и это осознанно. Есть свежее исследование (arXiv 2504.13811, 2025): прогнали GPT-4, LLaMA и Qwen на 26 тысячах PHP-скриптов. Вывод — большие модели дают почти идеальную precision, но проваливают recall: как первичный детектор LLM плохи, они пропускают. Зато отлично объясняют то, что уже найдено. Поэтому в Houndoom порядок именно такой: находят эвристики и сигнатуры, а LLM разбирает найденное. Не наоборот.
И честно про приватность: в режиме --ai фрагменты кода с клиентского сервера уходят в Anthropic. Для банков, госсектора и всего, что под 152-ФЗ, это блокер. Поэтому режим опциональный — ядро полностью работает без него.
Какую нишу он закрывает
Houndoom закрывает узкую, но реальную задачу: прийти на чужой заражённый сервер, снять картину за час и уйти без следов — не покупая подписку на клиентский сервер и не ставя туда агента. Главное его свойство — деобфускация, которая ничего не исполняет; в свете того, чем закончился 2025 год для Ai-Bolit, это не строчка в списке фич, а осознанное решение с недавно доказанной ценой ошибки.
Но это инструмент версии 0.1.0 от одного автора, без своего фида угроз и без опубликованных бенчмарков. Он не заменит платформенную защиту хостинга и не претендует на это. Если вам нужен разовый форензик-обход чужой машины — берите. Если нужна постоянная защита своего сайта — это к другим инструментам, и я первый вам это скажу.
Репозиторий: github.com/IvanShishkin/houndoom