Короткий ответ
WordPress на host.example рассылает спам: закройте исходящий SMTP с этого хоста (25/465/587), снимите копию wp-content и БД, найдите свежий PHP/плагин/мусор в uploads, не удаляйте дерево до tar. Не «оптимизируйте» сайт плагином очистки в проде. Не разбирайте webshell запуском в браузере. Очередь mailq сохраните (заголовки), не обязательно каждое тело письма в тикет.
Связанные: дефейс, процесс, секреты.
Симптомы и как отличить
- IP сайта в блоклистах, PTR
host.example. mailqтысячи писем с From вашего домена или случайных.- CPU php-fpm.
- Плагин «hello-dolly» внезапно плюс неизвестный
wp-xxx.phpв uploads.
| Картина | Не взлом WP | Куда |
|---|---|---|
| Свой SMTP-маркетинг с того же IP | репутация | вынести рассылку |
| Форма Contact и редкий легитимный mail | мало | rate, не IR |
| Спам с поддельным From без очереди на хосте | чужой сервер | SPF, не PHP |
| Пользовательский Outlook | ящик | не webroot |
Возможные причины
- Webshell в
uploads/themes. - Нулевой/нулевых плагин, nulled theme.
- Украденная учётка
wp-adminivan.petrov. - Компрометация хоста целиком (cron PHP вне WP).
- Плагин SMTP с украденными creds шлёт через SendGrid — тоже инцидент, другой канал.
- Редко: соседний vhost на том же PHP-FPM.
Диагностика
1. Стоп исходящей почты с хоста
sudo nft add rule inet filter output tcp dport { 25, 465, 587 } ip daddr != 127.0.0.1 drop
# или UFW egress, если так принято; главное — не disable фильтра
sudo mailq | head
sudo postqueue -p | headСохраните postqueue -p в /var/ir/wp-spam/.
Windows IIS+WP реже, но PHP может слать через pickup:
Get-NetTCPConnection -RemotePort 25,587 | Format-Table2. Копия
sudo mkdir -p /var/ir/wp-spam
sudo tar -czf /var/ir/wp-spam/wp-content.tgz -C /var/www/html wp-content
sudo cp /var/www/html/wp-config.php /var/ir/wp-spam/wp-config.php
sudo find /var/www/html/wp-content -name '*.php' -mtime -30 -ls3. Откуда mail()
sudo grep -R --include='*.php' -n 'mail(' /var/www/html/wp-content 2>/dev/null | head
sudo tail -n 200 /var/log/php*-fpm.log 2>/dev/null
sudo grep POST /var/log/nginx/access.log | grep -E 'wp-content/uploads|wp-login' | tailПлагины:
wp plugin list --path=/var/www/html 2>/dev/null || ls /var/www/html/wp-content/plugins
wp user list --path=/var/www/html 2>/dev/null || truewp CLI может отсутствовать — тогда только ls и БД.
4. Пользователи WP и cron
Новые администраторы, неизвестные wp_users. wp-cron.php шторм. Системный cron www-data.
sudo crontab -u www-data -l
sudo ls -la /var/www/html/wp-content/mu-pluginsНе выкладывайте SQL-эксплойт. Читайте таблицу штатным wp user list / phpMyAdmin с jump.
Решение
Сдерживание
- Egress SMTP drop.
- Maintenance /
wp_dieдля анонимов при необходимости. - Смена паролей WP-админов, ключей
wp-config(AUTH_KEYи соли — генерация штатная), пароль БД. - Выключить неизвестные плагины после копии: переименование каталога плагина.
sudo mv /var/www/html/wp-content/plugins/suspicious /var/ir/wp-spam/suspicious-pluginЧистка
Удаление webshell из uploads — после hash. Сверяйте PHP внутри uploads (их там быть не должно). Ядро WP лучше переустановить из официального пакета той же версии, не «скачать с форума». Темы/плагины — из репозитория, не nulled.
Очередь:
sudo postsuper -d ALL
# только когда копия очереди есть и бизнес согласен потерять и легитимные письма в очередиIIS+WP
Тот же смысл: стоп исходящего, копия C:\inetpub\wwwroot, поиск .php в uploads, смена секретов пула.
PHP в uploads и соседние vhost
Исполнение PHP в wp-content/uploads отключите на nginx/IIS после копии: location ~* /uploads/.*\.php { deny all; } — штатный приём, не эксплойт. Проверьте все vhost на том же php-fpm: спам мог идти с соседнего сайта, а вы чистите «главный» WordPress. Пользователь ivan.petrov как WP-админ: сессии wp_usermeta, неизвестные admin — удалить после экспорта списка. Egress на 10.0.10.55:587 если это ваш smarthost — исключите его в nft, иначе форма и спам делят один путь; после чистки вернёте только smarthost, не мир на 25.
Не ставьте «security plugin» в пике: лишний PHP с write. Ядро — официальный пакет той же версии. Nulled theme считайте бэкдором, даже если «купили на маркетплейсе». Очередь postsuper -d ALL только когда на хосте не должно быть легитимной почты. WS-042 веб-мастера не используйте как FTP-клиент к прод, пока инцидент открыт.
Как проверить, что проблема устранена
mailqпуст, новые письма не копятся.- Egress 25/587 с хоста закрыт кроме согласованного релея, если форма нужна.
- Нет PHP в
uploads. - Плагины известны, админы WP известны.
- Репутация IP: запрос на delist после чистки, не до.
- Соли и пароли сменены.
Если не помогло
- Очередь растёт при drop 25 — шлют через внешний HTTP API плагина; режьте процесс/плагин и исходящий 443 к тому API временно.
- Спам с другого vhost — смотрите все корни PHP.
- После чистки снова PHP в uploads — запись webroot жива (права 777), исправьте владельца, не только файл.
- PTR/IP общий на shared hosting — чистите свой vhost, delist может быть не ваш; тогда эскалация хостеру.
Профилактика
- Запрет исходящего 25 с веб-серверов, только один smarthost с auth.
- Запрет исполнения PHP в
uploads(правила nginx/IIS). - Обновления WP, MFA админов, лишние плагины долой.
- Не 777 на
wp-content. - Мониторинг
mailqи блоклисты.
FAQ
Можно ли просто сменить пароль admin и не трогать файлы?
Нет. Webshell не знает ваш новый пароль и продолжит mail().
Удалить весь wp-content/uploads?
Сначала копия. Потом можно чистить PHP из uploads, не обязательно все JPEG.
Нужно ли менять префикс таблиц wp_?
Не лечит. Секреты и файлы важнее косметики.
WordPress на Windows Server
Да, тот же IR. SMTP может идти через локальный IIS pickup — смотрите очередь и процессы php-cgi.
Форма «свяжитесь с нами» умрёт без 587
Временно да. Вернёте через smarthost после чистки, не через mail() в интернет.