Главная » Веб-серверы и PHP » Поиск вредоносного кода на сайте через консоль

Поиск вредоносного кода на сайте через консоль

Сайт взломали: в выдаче появились чужие ссылки, посетителей перекидывает на сторонние ресурсы, хостер прислал письмо о рассылке спама. Нужно быстро найти, где лежит вредоносный код, не дожидаясь антивируса панели.

Коротко: по известному фрагменту ищем командой grep -rl 'фрагмент' /var/www/. Если фрагмента нет, ищем по типичным сигнатурам (eval(base64_decode, gzinflate и т. п.), по недавно изменённым файлам через find, по PHP-файлам в uploads. Для WordPress сверяем ядро и плагины с эталоном: wp core verify-checksums и wp plugin verify-checksums --all.

Перед поиском

  • Сделайте копию сайта и базы в отдельный каталог: она пригодится для разбора и на случай, если при чистке удалите лишнее.
  • Не запускайте найденные файлы и не открывайте их в браузере. Смотрите только в консоли: less, head.
  • Если на сервере несколько сайтов, ищите по всем: заражение часто расходится по соседним каталогам одного пользователя.

Поиск по известному фрагменту

Если код уже известен (например, видели его в исходнике страницы или в письме хостера), достаточно grep:

grep -rl 'что ищем' /путь/где/ищем
grep -rl 'evil-domain.example' /var/www/

Ключи: -r — рекурсивно по каталогам, -l — выводить только имена файлов. Полезные дополнения:

  • -F — искать строку как есть, без регулярных выражений (удобно, если во фрагменте скобки и точки);
  • -i — без учёта регистра;
  • --include='*.php' — только PHP-файлы, так быстрее;
  • -n вместо -l — показать номер строки и саму строку.
grep -rlF --include='*.php' '$_POST[' /var/www/example.com
grep -rn --include='*.js' 'String.fromCharCode' /var/www/example.com/wp-content

Поиск по типичным сигнатурам

Чаще код неизвестен. Тогда ищем конструкции, которые в нормальном коде встречаются редко, а во вредоносном — постоянно: выполнение строки, декодирование base64, распаковка сжатых данных.

grep -rlE --include='*.php' \
  'eval\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13)' /var/www/

grep -rlE --include='*.php' \
  '(assert|eval|system|shell_exec|passthru)\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)' /var/www/

Будут и ложные срабатывания: base64_decode законно используют многие плагины. Поэтому каждый найденный файл открываю и смотрю глазами. Признаки вредоноса — одна бесконечная строка, бессмысленные имена переменных, код до <?php в начале файла или после закрывающего тега в конце.

Если установлен ClamAV, его стоит прогнать дополнительно: clamscan -ri /var/www/ (-r — рекурсивно, -i — показать только заражённые). Сигнатурный антивирус ловит не всё, но известные веб-шеллы находит.

Недавно изменённые файлы

Если примерно известна дата взлома, find покажет, что менялось после неё:

# PHP-файлы, изменённые за последние 7 дней
find /var/www/example.com -type f -name '*.php' -mtime -7 -ls

# изменённые после указанной даты
find /var/www/example.com -type f -newermt '2026-10-01' -ls

# по ctime: время изменения inode нельзя подделать командой touch
find /var/www/example.com -type f -name '*.php' -ctime -7 -ls

Злоумышленники часто выставляют вредоносным файлам старую дату модификации, чтобы спрятать их среди остальных. ctime так не поменять, поэтому я смотрю оба варианта. Учтите, что обновление плагинов и ядра тоже меняет даты, — сверяйтесь с журналом обновлений.

PHP-файлы там, где их быть не должно

В WordPress каталог wp-content/uploads предназначен для картинок и документов. PHP-файлы в нём почти всегда означают веб-шелл:

find /var/www/example.com/wp-content/uploads -type f \
  \( -name '*.php' -o -name '*.phtml' -o -name '*.php[0-9]' \) -ls

find /var/www/example.com -name '.htaccess' -newermt '2026-10-01' -ls

Второй командой проверяем подменённые .htaccess: через них часто настраивают редиректы на сторонние сайты или разрешают выполнение PHP в каталогах с загрузками.

Проверка ядра и плагинов WordPress

WP-CLI умеет сравнивать файлы с контрольными суммами с wordpress.org — это самый надёжный способ найти подменённые файлы ядра:

cd /var/www/example.com
wp core verify-checksums
wp core verify-checksums --include-root
wp plugin verify-checksums --all

Для ядра команда выводит предупреждения File doesn't verify against checksum (файл изменён), File doesn't exist (файла нет) и File should not exist (лишний файл в каталогах ядра). Ключ --include-root добавляет проверку корня сайта: там часто лежат посторонние PHP-файлы. Каталог wp-content этой командой не проверяется.

wp plugin verify-checksums --all работает только для плагинов из каталога wordpress.org. Платные и самописные плагины она пропустит с предупреждением Could not retrieve the checksums …, skipping. Их и тему придётся проверять grep и сравнением с дистрибутивом от разработчика.

Не забудьте про базу: вредоносный JavaScript нередко внедряют в записи и опции. Поиск по всем таблицам: wp db search '<script' --all-tables. Список администраторов (нет ли лишних): wp user list --role=administrator.

Что делать после того, как код найден

  1. Найдите точку входа. Посмотрите в логах веб-сервера POST-запросы к найденным файлам: grep 'POST /wp-content/uploads' /var/log/nginx/access.log. Так видно IP и первый запрос, а часто и уязвимый плагин.
  2. Замените ядро на чистое: wp core download --force --skip-content --version=<текущая версия>. Плагины из каталога переустановите: wp plugin install <slug> --force.
  3. Удалите веб-шеллы и лишние файлы, обновите всё до актуальных версий, удалите неиспользуемые плагины и темы.
  4. Смените все пароли: администраторов WordPress, пользователя базы данных, SSH/SFTP, панели хостинга. Обновите ключи и соли: wp config shuffle-salts — это завершит все активные сессии.
  5. Проверьте cron пользователя сайта (crontab -l -u www-data или от владельца сайта): вредонос часто восстанавливает себя по расписанию.
  6. Через несколько дней повторите поиск по тем же сигнатурам и по -ctime.

Если сайт проще перенести на чистый сервер, чем лечить на месте, пригодится инструкция Перенос сайта WordPress через командную строку. Переносите только проверенные файлы и базу. Базовая настройка сервера под WordPress описана в статье Настройка VPS для WordPress.

Частые вопросы

Почему grep ничего не нашёл, а сайт всё равно заражён?

Код может лежать в базе данных, а не в файлах, быть закодирован так, что сигнатуры не совпадают, или находиться за пределами каталога сайта (cron, файлы в /tmp). Проверьте базу через wp db search, задания cron и файлы, изменённые за период взлома.

Можно ли удалить все найденные файлы с base64_decode?

Нет. Эта функция есть во многих легитимных плагинах и в самом ядре. Удаляйте только то, что после просмотра однозначно вредоносно, а файлы ядра и плагинов лучше перезаписать чистыми копиями.

Что делать с файлами, которые verify-checksums пометил как изменённые?

Файлы ядра не должны меняться вообще, поэтому их не правят вручную, а перезаписывают командой wp core download --force той же версии и локали. После этого запустите проверку ещё раз: предупреждений о несовпадении быть не должно. Файлы с пометкой File should not exist просмотрите и удалите.