Меньше двух суток от выхода патча до взлома: разбор атаки на WordPress
Задача
Клиент обратился с типичной жалобой: хостинг прислал уведомление о вирусе, а в панели Google Search Console появился незнакомый подтверждённый владелец. Сайт производственной компании, WordPress, около двадцати плагинов, живой трафик из поиска. Логи веб-сервера были включены буквально в момент обращения, то есть история запросов отсутствовала. Нужно было понять, как злоумышленник попал на сайт и что успел сделать, вычистить всё до последнего файла и закрыть точку входа — не сломав при этом работающий сайт.
С чего всё началось
Три наблюдения клиента на момент обращения:
- 1Антивирус хостинга нашёл файл в каталоге плагинов с именем вида «system-monitor» и случайным хвостом из символов.
- 2В файловом менеджере несколько папок имели сегодняшнюю дату изменения, хотя никто ничего не трогал.
- 3В Google Search Console появился чужой владелец, подтверждённый через HTML-файл в корне сайта.
Точка входа: не плагин, а само ядро
Первая гипотеза была очевидной — дырявый плагин. На сайте стоял файловый менеджер, исторически известный критической уязвимостью, и он же выглядел главным подозреваемым: в корне сайта нашлись служебные каталоги, которые создаёт только он.
Гипотеза оказалась ложной. Установленная версия была новее уязвимой, а следы работы файлового менеджера датировались временем после появления первого чужого администратора. Это был инструмент закрепления, а не вход.
Настоящей точкой входа оказалось ядро WordPress. Свежая на тот момент уязвимость CVE‑2026‑63030 (кампания получила имя wp2shell) — связка путаницы маршрутов в REST API и SQL‑инъекции в параметре выборки записей. Эксплуатация не требует ни аутентификации, ни действий пользователя: злоумышленник напрямую создаёт администратора и загружает код.
Хронология захвата
Дальше сайт больше недели жил своей жизнью. Восстановленная картина:
- 19 июляСозданы два администратора с именами, похожими на служебные аккаунты — расчёт на то, что владелец примет их за системные
- 19–21 июляЕщё три администратора от других инструментов: судя по почтовым доменам в профилях, сайт нашли и заняли несколько разных групп
- 21 июляВ каталоге плагинов разворачиваются два поддельных плагина: веб-шелл для выполнения команд и файловый менеджер для загрузки файлов
- 22 июляПоявляется must-use плагин — такие WordPress загружает всегда и не показывает в обычном списке — и ещё один шелл под видом кэширующего плагина
- 25–26 июляНовые администраторы с именами вида «administrator» с числовым хвостом, шеллы расползаются по каталогам изображений
- 27 июляМассовая правка времени изменения у сотен файлов и подтверждение чужих прав в Search Console
Три приёма, которые стоит знать
1. Подделка дат изменения файлов
Самый неприятный сюрприз. Вредоносные файлы имели даты 2022–2024 годов — то есть выглядели как древние, давно лежащие и потому безобидные. На деле весь этот код появился в течение одной недели: злоумышленник массово проставлял файлам произвольное время изменения — простая операция, доступная любому, у кого есть выполнение команд.
| Что нашли | Дата на файле | На самом деле |
|---|---|---|
| Веб-шелл под видом плагина | январь 2024 | эта неделя |
| Каталоги с данными для спама | ноябрь 2022 | эта неделя |
2. Вредоносный .htaccess, защищающий от конкурентов
В корне сайта лежал переписанный файл настроек сервера с необычной логикой: он запрещал выполнение всех PHP‑файлов, кроме короткого белого списка. В списке были системные файлы WordPress — и полтора десятка чужих имён.
# Схема вредоносного .htaccess (не дословный код) # Выполнение PHP запрещено всем… <FilesMatch "\.phpquot;> Deny from all </FilesMatch> # …кроме системных файлов и бэкдоров первой группы <FilesMatch "^(index|wp-login|defaults|mah|networks|…)\.phpquot;> Allow from all </FilesMatch>
То есть первая группа, захватившая сайт, закрыла его от всех остальных, оставив работающими только собственные бэкдоры. Побочный эффект — на сайте перестали работать плановые задачи WordPress. Владелец этого не замечал.
3. Плагин «Security Patch», закрывающий ту самую дыру
Самая изящная находка. В каталоге must-use плагинов лежал файл, который представлялся как «Security Patch — усиление REST API». И он действительно закрывал доступ к уязвимому маршруту — для всех, кроме тех, кто знает секретный заголовок с паролем.
Ради чего всё затевалось
Не ради разрушения. Сайт превратили в инструмент для чужого SEO.
Видит обычный сайт компании — ничего подозрительного
Получает спам-страницы под чужие коммерческие запросы
- В главный файл сайта был вшит код, подгружающий контент с внешнего сервера. Он проверял, кто пришёл, и отдавал спам только поисковым роботам — обычный посетитель видел нормальный сайт.
- В корне лежало около десятка файлов-дорвеев, генерирующих страницы под чужие коммерческие запросы. К моменту разбора их уже активно обходили Googlebot и краулеры ИИ-сервисов — спам попал в индекс.
- В каталогах изображений и внутри легитимных плагинов были рассажены дополнительные точки входа. Папки для них назывались правдоподобно: cloudflare, nextjs, jquery3 — на беглый взгляд не отличить от настоящих.
Ущерб здесь репутационный и поисковый: чужие страницы в индексе, риск санкций поисковиков, подтверждённый чужой владелец в Search Console.
Что помогло всё восстановить
Главным инструментом расследования оказался git‑репозиторий — система контроля версий, которую мы сами завели за несколько месяцев до инцидента, просто для более удобной разработки.
Поскольку датам файлов доверять было нельзя, а логов не было, единственным надёжным эталоном стало «состояние сайта на момент последнего коммита». Сравнение с ним дало исчерпывающий ответ: какие файлы изменены, какие добавлены и как выглядели оригиналы. Больше того — оно опровергло первоначальную версию. По датам казалось, что сайт заражён с 2022 года; сравнение с репозиторием показало, что на момент коммита сайт был полностью чист, и всё уложилось в одну неделю.
Что мы сделали
- Собрали улики до любых изменений: архив вредоносных файлов и дамп базы — это нужно и для разбора, и на случай, если что-то пойдёт не так
- Удалили десять чужих администраторов — в первую очередь: через админку злоумышленник может залить всё заново за минуту
- Изолировали вредоносный код: не удаление, а перенос в карантин за пределы публичной папки — операция обратимая, а улики сохраняются
- Восстановили изменённые файлы из чистых версий
- Обновили ядро WordPress до версии с исправлением — до этого шага сайт оставался уязвим, и его могли взломать повторно в любую минуту
- Обновили плагины и удалили неиспользуемые: неактивный плагин — это всё ещё исполняемый код, доступный по прямой ссылке
- Сменили секретные ключи в конфигурации, чтобы разлогинить всех, включая злоумышленника
- Запретили выполнение PHP в каталогах загрузок и изображений: туда попадают файлы от пользователей, и исполняемому коду там делать нечего
- Настроили для спам-страниц, попавших в индекс, ответ «страница удалена навсегда» (код 410) — так поисковики выбрасывают их быстрее, чем при обычной ошибке 404
- Отозвали чужой доступ в Search Console вручную: удаление файла подтверждения уже подтверждённые права не отзывает
- Починили оформление, которое «поехало» после обновления плагинов: нужные стили когда-то были вписаны прямо в файлы плагина, и обновление их стёрло. Перенесли правки в тему — туда, где они переживают обновления, — а заодно нашли и устранили ошибку, из-за которой часть стилей годами не применялась на мобильных устройствах
Результат
Сайт полностью вычищен и закрыт: десять чужих администраторов удалены, около сорока вредоносных объектов отправлены в карантин, ядро и плагины обновлены, спам-страницы выбрасываются из поиска. Главным инструментом расследования стал git-репозиторий, который мы сами завели за несколько месяцев до инцидента для удобства разработки: сравнение с ним позволило проверить каждый файл и доказать, что заражение длилось неделю, а не годы, — подробности в разборе выше.
Чек-лист: что сделать со своим сайтом сегодня
Если ядро не обновлялось несколько месяцев — это главный риск, важнее любых плагинов. Здесь от публикации уязвимости до взлома прошло меньше двух суток.
Все администраторы должны быть вам знакомы. Аккаунты со «служебными» именами — расчёт именно на то, что вы примете их за системные.
WordPress загружает их всегда и не показывает в обычном списке — идеальное место для закрепления. В норме там либо пусто, либо то, что вы поставили сами.
Штатными средствами WordPress это делается одной командой — и сразу показывает посторонние файлы.
Именно удалите, а не деактивируйте: неактивный плагин — это всё ещё исполняемый код, доступный по прямой ссылке.
Их отсутствие в этом кейсе стоило самой ценной части картины — точного вектора первого запроса.
Даже для сайта без активной разработки: в момент инцидента он превращается в эталон, с которым можно сверить каждый файл.
Что осталось за кадром
Хостинг в этой истории отработал корректно: сайты одного аккаунта оказались изолированы друг от друга, и взлом одного не дал доступа к соседним. Это стоит проверять при выборе хостинга: на площадках без такой изоляции один дырявый сайт компрометирует все остальные на аккаунте.
Отдельно проверялось, не утекли ли учётные данные для отправки почты. Не утекли — и по приятной причине: сайт отправлял письма системными средствами, которым логин и пароль не нужны. Хранить было нечего.