Как очистить WordPress от вирусов вручную, если плагин не справляется
Ручная чистка WordPress после взлома: где смотреть, что искать, как сравнивать с эталоном и не убить рабочий контент.
Содержание
- Почему плагины не справляются
- Шаг 0: изолировать сайт и снять грязный бэкап
- Шаг 1: сравнить файлы с эталоном
- Шаг 2: обыскать типичные точки скрытия
- Шаг 3: какие сигнатуры искать
- Шаг 4: чистка базы данных
- Шаг 5: восстановление и проверка
- Шаг 6: закрыть точку входа
- Что я делаю на типовом разборе
- Когда вызвать подрядчика
- Частые вопросы
Ручная чистка WordPress занимает 4-5 часов на средний случай и идёт по шагам: изолировать сайт, снять грязную копию, сравнить файлы с эталоном ядра, обыскать типовые места, вычистить базу, восстановить и закрыть точку входа. Плагины-сканеры видят известные сигнатуры, а свежую закладку пропускают.
Wordfence показал десять заражённых файлов, а после автоматической чистки сайт сначала упал, а через два дня снова стал перенаправлять на казино. Если такая ситуация знакома, то добро пожаловать в реальность: автоматические сканеры WordPress справляются примерно в половине случаев. Дальше нужны руки, голова и SSH. В статье, как разобрать заражённый WordPress самому, что искать, чем сравнивать и как убедиться, что точка входа закрыта, а не временно прикрыта.

Почему плагины не справляются
Плагины-сканеры (Wordfence, MalCare, Sucuri, iThemes Security) работают по сигнатурам. У каждого своя база, у некоторых ещё и эвристика. Они отлично ловят типовые шеллы и стандартные инжекты пятилетней давности.
Но реальный взлом 2026 года часто выглядит не как шелл, а как:
- Корректный с точки зрения PHP код, который кладётся в существующий плагин и активируется только через cron при определённых условиях.
- Запись в базе данных в формате сериализованного массива, которая разворачивается на лету и инжектится в footer темы.
- Файл
.icoили.jpgвuploads/, в EXIF которого зашит payload, а в рабочем плагине добавлена строчкаinclude 'uploads/2024/01/icon.ico';.
Сигнатурный сканер такие штуки пропускает. Эвристика иногда ловит, но даёт массу ложных срабатываний, и пользователь устаёт от красных предупреждений на каждый легитимный плагин.
Поэтому стратегия «поставлю Wordfence, он сам всё почистит» работает на лёгких случаях. Если сайт уже неделю редиректит, а плагин ничего не находит — значит инфекция сложнее, чем умеет автомат.
Шаг 0: изолировать сайт и снять грязный бэкап
До того, как что-то трогать, делаем две вещи. Они нужны, чтобы потом не пожалеть.
Закрыть сайт от посетителей
Можно через .htaccess (блок по IP всех, кроме своего):
Order Deny,Allow
Deny from all
Allow from 1.2.3.4
Или через одну строку в wp-config.php, выводящую заглушку. Цель — остановить заражение посетителей и отключить инфектор от сети, чтобы он перестал общаться со своим C2.
Снять полный бэкап грязного состояния
Это контринтуитивно, но обязательно. Вы будете править файлы, удалять, восстанавливать. Что-то пойдёт не так. Без снимка «до» вы не сможете откатиться.
Команда на сервере:
cd /home/user/example.ru/
tar czf ~/dirty-backup-$(date +%F).tar.gz public_html/
mysqldump -u DBUSER -p DBNAME | gzip > ~/dirty-db-$(date +%F).sql.gz
Архив храните вне webroot, чтобы его нельзя было скачать через браузер. И не путайте его с чистым бэкапом для восстановления, подпишите имя dirty-.
Шаг 1: сравнить файлы с эталоном
WordPress прекрасен тем, что у каждого его файла есть официальная контрольная сумма. Это даёт быстрый ответ на вопрос «что в core поменяно».
Через WP-CLI
Если на сервере установлен WP-CLI (на нормальных хостингах есть):
wp core verify-checksums
wp plugin verify-checksums --all
Команды покажут, какие файлы ядра и плагинов отличаются от официальных. Полезно прогнать обе и сохранить вывод в файл, потом по нему пройтись.
Без WP-CLI
Если WP-CLI нет, скачиваете на свою машину чистый WordPress той же версии, что у вас на сайте, и плагины тех же версий. Затем diff -r:
diff -r wp-clean/ wp-site/wordpress/ > diff-core.txt
diff -r plugin-clean/contact-form-7/ wp-site/wp-content/plugins/contact-form-7/ > diff-cf7.txt
Версию ядра и плагинов смотрите в админке или в файле wp-includes/version.php и */readme.txt плагинов.
Что не сравнивается
Темы. У них нет официальной checksum, кроме официальных тем twenty*. Свою кастомную тему придётся проверять вручную или сравнивать с git-репозиторием, если он у вас есть. Если репы нет — это отдельный урок: после чистки заведите git хотя бы для wp-content/themes/.
Шаг 2: обыскать типичные точки скрытия
У вредоносного кода в WordPress есть несколько любимых мест. Прохожусь по ним по порядку.
wp-config.php
Обычно туда дописывают include грязного файла либо подменяют ключи аутентификации. Открываете и сверяете построчно с тем, что там должно быть. Никаких посторонних define, никаких if(isset($_REQUEST...)), никаких eval.
.htaccess в корне и во вложенных папках
Корневой .htaccess обычно содержит блок # BEGIN WordPress ... # END WordPress. Всё, что не в этих блоках, надо проверять. Особенно строки с RewriteCond %{HTTP_USER_AGENT} и RewriteRule на сторонний домен.
Не забывайте про .htaccess во вложенных каталогах. Команда:
find . -name ".htaccess" -ls
Иногда злоумышленник кладёт .htaccess в uploads/2024/, который разрешает выполнение PHP в этой папке.
wp-content/themes/<active>/
Самые частые жертвы: — functions.php — в неё дописывают функции с именами вроде wp_initialize_hash, _load_theme_settings, которые на самом деле инжектят скрипт. — header.php, footer.php — туда вставляют строку с <script> или echo подгружаемого payload. — любой PHP-файл с цифрой в имени или с подчёркиванием в конце — часто это файлы, добавленные взломщиком.
Открываете каждый файл темы и читаете глазами. Если файлов много и не понимаете, что в них — ищите подозрительные конструкции (см. ниже).
wp-content/plugins/
Каждый плагин сравниваете с чистой копией с wordpress.org. Любой «лишний» файл — под подозрение.
Особенно внимательно смотрите плагины, которые вы не помните, как ставили. Иногда злоумышленник создаёт свой «плагин» в wp-content/plugins/wp-cache-helper/ и активирует его, чтобы автозагружался. Содержимое — один файл с шеллом.
wp-content/uploads/
Здесь PHP-файлов быть не должно вообще. Никогда. Команда:
find wp-content/uploads -type f -name "*.php*" -ls
find wp-content/uploads -type f -name "*.phtml" -ls
find wp-content/uploads -type f -name "*.phar" -ls
find wp-content/uploads -type f -name "*.htaccess" -ls
Всё, что нашлось — на изучение и в большинстве случаев на удаление. Часто внутри будет короткий загрузчик второй стадии.
Корневые файлы с непонятными именами
Команда:
ls -la / | grep -v "^d"
find . -maxdepth 1 -type f -ls
Файлы вроде wp-class.php, wp-info.php, news.php, about_us.php в корне — это не WordPress. Это бэкдоры. Уносим в карантин для изучения, потом удаляем.
Шаг 3: какие сигнатуры искать
Здесь grep становится лучшим другом. Несколько паттернов, которые ловят 80% инжектов:
# eval с base64 - самая классика
grep -rEn "evals*(s*base64_decode" wp-content/
# eval с gzinflate
grep -rEn "evals*(s*gzinflate" wp-content/
# preg_replace с модификатором /e (запрещён в PHP 7+, но встречается)
grep -rEn 'preg_replace([^)]*"/e' wp-content/
# заглавные конструкции вызова через переменные функций
grep -rEn "\$_(POST|REQUEST|COOKIE)[[^\]]*]s*(" wp-content/
# create_function (тоже устарел, но в инжектах попадается)
grep -rEn "create_functions*(" wp-content/
# подключения внешних скриптов с подозрительных доменов
grep -rEn "https?://[^"']*.(top|xyz|ru.com|cf|gq|ml|tk)" wp-content/
Выводы сохраняйте в файлы и просматривайте глазами. Не каждое срабатывание — вирус. Например, base64_decode сам по себе встречается в легальных плагинах, важен контекст.
Менее очевидные сигнатуры
Современные инжекты прячутся хитрее. Признаки:
- Длинная строка без пробелов и переводов строк — часто это закодированный payload.
- Конструкция вида
${"GLOBALS"}["x"]— попытка обойти статический анализ. - Файл с одним коротким PHP-блоком, который читает другой файл и вызывает его.
- Использование
assert()с пользовательским вводом — это формаevalпод другим именем. - Файл с произвольными байтами в начале и обычным кодом дальше — попытка перебить кодировку.
Я обычно прогоняю ещё две команды:
# Файлы, изменённые за последние 14 дней (если знаете, что не правили)
find wp-content -mtime -14 -type f -name "*.php" -ls
# Файлы с правами 777 (нормально таких быть не должно)
find . -type f -perm 0777 -ls

Шаг 4: чистка базы данных
Файлы — это половина истории. Вторая половина в БД. Куда смотреть:
Таблица wp_options
Там два опасных поля: siteurl и home. Если в них прописан не ваш домен, а что-то типа http://evil.example/redirect/, это и есть редирект.
Проверка:
SELECT option_id, option_name, option_value
FROM wp_options
WHERE option_name IN ('siteurl', 'home');
Также интересны записи с длинным сериализованным значением и неочевидным option_name. Особенно *_transient_* и *_cron. Иногда туда сохраняют payload, который активируется по cron.
Команда найти потенциально подозрительные autoload-записи большого размера:
SELECT option_id, option_name, LENGTH(option_value) AS size
FROM wp_options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 30;
Если в топе видите неизвестное имя с 100-500 КБ — открывайте и смотрите. Чаще всего там невинный кеш плагина, но иногда лежит и payload.
Таблица wp_users
Лишние пользователи с правами администратора — явный признак взлома. Команда:
SELECT u.ID, u.user_login, u.user_email, u.user_registered, m.meta_value
FROM wp_users u
JOIN wp_usermeta m ON u.ID = m.user_id
WHERE m.meta_key = 'wp_capabilities'
AND m.meta_value LIKE '%administrator%';
Все администраторы, которых вы не знаете — удалить. Перед удалением сменить пароль вашему админу и проверить email-привязку, чтобы вас не угнали через «восстановить пароль».
Таблица wp_posts
Реже, но бывает: в post_content записи или страницы вшит <script>. Поиск:
SELECT ID, post_title, post_status
FROM wp_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%<iframe%';
Не каждое срабатывание — вирус (бывают легитимные виджеты), но просмотреть стоит.
Таблицы с префиксом не вашего сайта
Если у вас префикс wp_, а вы видите таблицы с префиксом, например, wp1_ или secrt_ — ищите, кто их создал. Иногда атакующие создают теневые таблицы для своих нужд.
Шаг 5: восстановление и проверка
После чистки нужно убедиться, что сайт жив и работает.
Восстановить ядро
Самый надёжный способ — удалить все файлы ядра WordPress (всё, кроме wp-config.php, wp-content/, корневого .htaccess) и развернуть свежую копию того же релиза. Скачать с wordpress.org/download/releases/.
cd ~/example.ru/
rm -rf public_html/wp-includes
rm -rf public_html/wp-admin
rm -f public_html/wp-*.php
rm -f public_html/index.php
rm -f public_html/license.txt
rm -f public_html/readme.html
# заливаем свежий wordpress без wp-content и wp-config
unzip wordpress-X.X.X.zip -d /tmp/wp/
cp -R /tmp/wp/wordpress/* public_html/
Это убивает всё, что внедрено в core, и не трогает ваш контент.
Восстановить плагины
Те плагины, которые вы оставляете, удалите целиком и поставьте заново из админки или wp-cli:
wp plugin install contact-form-7 --version=5.9 --activate
Темы — аналогично, но если тема кастомная — заливаете из git или из чистого бэкапа двухмесячной давности.
Сменить все секреты
В wp-config.php есть восемь констант с солями. Сгенерируйте новые на https://api.wordpress.org/secret-key/1.1/salt/ и подставьте. Это аннулирует все активные сессии и заставит злоумышленника, если у него есть валидная кука, перелогиниться.
Параллельно сменить: — пароль админа WordPress (через профиль), — пароль базы данных (через панель хостинга и потом обновить в wp-config.php), — пароль FTP/SSH, — пароль панели хостинга, — API-ключи всех интеграций (Метрика, СМС, CRM).
Финальная проверка
# Запустить сайт и пройтись по основным страницам
curl -s https://example.ru/ | grep -iE "eval|atob|fromCharCode"
curl -s https://example.ru/wp-login.php | head -50
# Проверить логи доступа за последний час
tail -1000 /var/log/apache2/access.log | grep -E "POST|wp-admin"
# Прогнать сторонние сканеры
# sitecheck.sucuri.net, virustotal.com, transparencyreport.google.com
Если ничего подозрительного — можно открывать сайт. Перед этим включите Wordfence или аналог в режим максимальной защиты на пару недель, чтобы быстро ловить попытки повторного взлома.
Шаг 6: закрыть точку входа
Это самое важное и его чаще всего пропускают. Сайт почистили, открыли, через два дня снова заражение. Почему? Потому что не нашли, как именно они зашли в первый раз.
Возможные точки входа:
| Источник | Что проверить |
|---|---|
| Уязвимый плагин/тема | Какая версия стояла на момент взлома и какие CVE на неё известны |
| Слабый пароль админа | Журнал входов в Wordfence, логи nginx за месяц до взлома |
| XML-RPC брутфорс | wp-cron.php и xmlrpc.php в логах с подозрительной активностью |
| Хостинг-уровень | Запросить у хостера, не было ли инцидентов на shared, не утекли ли соседи |
| FTP-пароль | Если использовался FTP без TLS, пароль мог уйти в открытом канале |
| Локальная машина | Смотрели ли FTP/SSH-пароли антивирусом, нет ли стилеров |
По каждому пункту делаете действие. По плагинам — удаляете старое и не используете уязвимые. По паролю — меняете на длинный сгенерированный, включаете 2FA. По XML-RPC — отключаете, если не нужен:
<Files xmlrpc.php>
Require all denied
</Files>
По локальной машине — проверяете, чем пользуетесь для FTP, не лежит ли пароль в открытом виде в FileZilla, не было ли подозрительных программ.

Что я делаю на типовом разборе
Чтобы не быть голословным, типовой разбор у меня выглядит так:
- Полчаса — снимаю бэкапы, изолирую сайт, ставлю заглушку.
- Час — сравнение с эталоном, поиск инжектов в файлах.
- 30-40 минут — разбор базы данных, чистка
wp_options, проверка пользователей. - Час — перезаливка ядра, переустановка плагинов, смена секретов.
- Полчаса — финальная проверка, прогон сканеров, открытие сайта.
- 20-30 минут — отчёт клиенту: что было, что сделано, что закрыто на будущее.
Итого 4-5 часов на средний случай. Тяжёлые случаи (заражение нескольких сайтов на shared, давний инжект, который мутировал) могут растянуться на день-два.
Когда вызвать подрядчика
Делать самому имеет смысл, если у вас есть SSH, желание читать PHP и пара свободных вечеров. В остальных случаях это работа специалиста. По моему опыту, когда владелец пытается чистить сайт сам без бэкграунда — в половине случаев он сносит важные кастомные правки в теме, в четверти случаев пропускает скрытый инжект и ловит повторное заражение через неделю.
Сигналы, что пора звать на помощь: — В админку не пускает (заблокировали или сменили пароль). — Хостер прислал письмо «обнаружили вредоносное ПО, заблокируем через 24 часа». — Сайт — основной канал клиентов, и каждый день простоя стоит больше работы специалиста. — Уже почистили, а через два дня снова редирект.
В моих абонементах тариф «Авария» включает разбор такого инцидента полностью: от снятия грязного бэкапа до отчёта и инструкций по защите. Если сейчас горит и сайт лежит — пишите в контактах, смотрим сегодня. Другие материалы по безопасности и восстановлению есть в рубрике безопасность.
Частые вопросы
Как очистить WordPress от вирусов вручную
Изолируйте сайт, снимите копию в заражённом виде, скачайте с wordpress.org чистое ядро своей версии и сравните файлы. Дальше обыщите папку загрузок, темы и плагины, вычистите базу и закройте уязвимость.
Почему плагины-антивирусы не справляются
Они ищут известные сигнатуры. Свежая закладка, собранная под конкретный сайт, под сигнатуру не подходит, а вредонос в базе данных многие сканеры вообще не смотрят.
Сколько времени занимает ручная чистка
Около 4-5 часов на средний случай: примерно 30-40 минут уходит на разбор базы и 20-30 минут на отчёт, остальное на файлы и поиск точки входа.
Что делать после чистки
Сменить все пароли, обновить ядро, темы и плагины, пройти по чек-листу защиты WordPress и понаблюдать за файлами. Без закрытой точки входа сайт заражается снова за недели.