MAATRIX / Блог / Нашли веб-шелл: удалить нельзя оставить — что делать по порядку

Нашли веб-шелл: удалить нельзя оставить — что делать по порядку

MAATRIX

Вы зашли на сервер по FTP, SSH или через файловый менеджер панели — и увидели файл, которого точно не заливали: странное имя, PHP-код с обфускацией, дата изменения в пятницу вечером, когда никто из команды не работал. Рука тянется удалить его немедленно — и это первая ошибка. Ниже — порядок действий, который сохраняет улики, помогает понять масштаб компрометации и закрыть все ходы, а не только тот, который вы случайно заметили.

Почему не стоит удалять веб-шелл в первую же минуту

Найденный файл — это не причина компрометации, а её следствие. Атакующий не оставляет один файл: обычно это точка входа плюс один-два запасных бэкдора на случай, если основной найдут и удалят. Если вы удаляете найденный шелл, не разобравшись, как он попал на сервер и что ещё было сделано за время его работы, происходит одно из двух: либо атакующий возвращается через второй, ещё не найденный ход, либо вы закрываете симптом, оставляя нетронутой саму уязвимость — и через неделю история повторяется с новым файлом и новым именем.

Есть и вторая причина не спешить: сам файл — это улика. По времени его создания и изменения, по содержимому, по тому, какие запросы к нему приходили в логах веб-сервера, можно восстановить, когда именно произошёл взлом, через какую уязвимость и что злоумышленник успел сделать за это время — просмотреть базу, скачать файлы, добавить пользователя, встроиться в другой скрипт. Удалённый файл эту возможность закрывает: сервер не хранит внутри себя историю того, что происходило до удаления, а увидеть время создания задним числом вы уже не сможете.

Поэтому правильный порядок обратный интуитивному: не «увидели — стёрли», а «увидели — зафиксировали — разобрались — почистили всё найденное разом». Разберём каждый шаг.

Шаг 1. Изолируйте сервер, но не выключайте его

Первая инстинктивная реакция после осознания масштаба — выключить машину. Это тоже ошибка, только другого рода: при выключении теряется содержимое оперативной памяти, активные сетевые соединения и процессы без файла на диске — а именно там иногда живут самые интересные следы, если бэкдор не ограничивается одним PHP-файлом. Задача на этом шаге — не остановить сервер, а перекрыть атакующему возможность действовать дальше, сохранив при этом состояние машины для анализа.

Если у вас VPS с доступом к firewall на уровне провайдера или локальному ufw/iptables, самый быстрый вариант — закрыть входящий и исходящий трафик, оставив только своё подключение:

# входящий трафик — только с вашего IP на SSH
ufw default deny incoming
ufw default deny outgoing
ufw allow out to ВАШ_DNS_ИЛИ_РЕПОЗИТОРИЙ port 443 proto tcp
ufw allow from ВАШ_IP to any port 22 proto tcp
ufw enable

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

Если это виртуальный хостинг или сервер с несколькими сайтами и вы не хотите ронять их все, точечная изоляция — на уровне конкретного сайта: перевести его в режим обслуживания на веб-сервере, не трогая сам файл и не выключая сервер целиком:

# в конфиге сайта — отдать 503 всем, кроме вашего IP
location / {
    if ($remote_addr != "ВАШ_IP") {
        return 503;
    }
}

Это временная мера на часы, не на дни: она снижает риск, пока вы собираете улики и ищете масштаб, но не заменяет реальную чистку.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Шаг 2. Зафиксируйте состояние до любых лечащих действий

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

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

mkdir -p ~/evidence/$(date +%Y%m%d)
cp -p /var/www/html/wp-content/uploads/подозрительный-файл.php ~/evidence/$(date +%Y%m%d)/
sha256sum /var/www/html/wp-content/uploads/подозрительный-файл.php >> ~/evidence/$(date +%Y%m%d)/hashes.txt
stat /var/www/html/wp-content/uploads/подозрительный-файл.php >> ~/evidence/$(date +%Y%m%d)/stat.txt

Флаг -p в cp сохраняет время изменения и права — без него вы теряете как раз ту метку времени, которая нужна для следующего шага. sha256sum даёт контрольную сумму: если позже возникнет вопрос, тот ли это файл, что был найден изначально, хеш это подтвердит.

Логи снимайте до того, как настроите ротацию или перезапустите сервисы — свежий рестарт nginx или php-fpm сам по себе логи не стирает, но лучше не рисковать:

cp /var/log/nginx/access.log /var/log/nginx/error.log ~/evidence/$(date +%Y%m%d)/
cp /var/log/auth.log ~/evidence/$(date +%Y%m%d)/ 2>/dev/null
journalctl -u php8.3-fpm --since "-14 days" > ~/evidence/$(date +%Y%m%d)/php-fpm.log

И снимок текущей активности — процессов, сетевых соединений, открытых файлов:

ps auxf > ~/evidence/$(date +%Y%m%d)/processes.txt
ss -tulpn > ~/evidence/$(date +%Y%m%d)/connections.txt
lsof -i > ~/evidence/$(date +%Y%m%d)/open-files.txt
crontab -l > ~/evidence/$(date +%Y%m%d)/cron-root.txt

Всю эту папку ~/evidence стоит сразу скопировать за пределы сервера — на локальную машину или в отдельное хранилище. Если компрометация серьёзная, есть риск, что сам сервер вы позже переустановите целиком, и локальные копии исчезнут вместе с ним.

Если у хостинг-провайдера есть снапшоты дисков и бюджет позволяет — снимите снапшот прямо сейчас, до чистки. Это резервная копия текущего, скомпрометированного состояния: пригодится, если понадобится более глубокий анализ или придётся откатываться и сверяться, что именно было на сервере до вмешательства.

Шаг 3. Найдите точку входа и оцените масштаб

Один найденный файл почти никогда не отвечает на вопрос «как он сюда попал» и «что ещё затронуто». Эти два вопроса — и есть содержание третьего шага.

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

grep -i "подозрительный-файл\|/uploads/" /var/log/nginx/access.log | less

Обратите внимание на User-Agent и IP запросов, которые заливали файлы через формы обратной связи, устаревшие плагины CMS или незащищённые эндпоинты загрузки — типичные ходы для веб-шеллов на сайтах. Если сайт на WordPress или похожей CMS, отдельно проверьте, не через забытый или давно не обновлявшийся плагин ли пришёл файл — это частый сценарий, разобранный подробнее в статье «Взломали через плагин, которым не пользовались два года».

Масштаб. Найдите все файлы, изменённые примерно в то же время, что и обнаруженный шелл, — это кандидаты на дополнительные бэкдоры:

find /var/www -type f -newermt "2026-08-20" ! -newermt "2026-08-27" -printf "%T+ %p\n" | sort

Дополнительно поищите характерные признаки обфусцированного кода по всей директории сайта — не полагайтесь на то, что нашли единственный файл:

grep -rlE "eval\s*\(|base64_decode\s*\(|gzinflate\s*\(|assert\s*\(\s*\\\$_" /var/www/html --include=*.php

Это грубый фильтр: часть совпадений окажется легитимным кодом, часть подозрительных файлов таким способом не найдётся — обфускация постоянно эволюционирует, и рассчитывать на один паттерн нельзя. Отнеситесь к результату как к списку кандидатов на ручную проверку, а не как к финальному вердикту.

Проверьте также типичные места закрепления, помимо файлов самого сайта:

  • ~/.ssh/authorized_keys — не добавлен ли чужой публичный ключ;
  • crontab -l для всех пользователей и /etc/cron.d/ — не появилась ли новая задача;
  • список пользователей ОС (cat /etc/passwd — нет ли новых UID) и админов в самой CMS/базе;
  • конфиги веб-сервера и .htaccess — не добавлены ли посторонние правила rewrite или proxy_pass на внешний адрес.

Если на этом этапе картина не складывается — не видно ни точки входа, ни ясного масштаба, а признаки указывают на то, что затронуты системные файлы или получен root-доступ, — это повод привлечь специалиста по форензике, а не гадать дальше самостоятельно. Общая логика такого разбора и граница, где нужен внешний эксперт, подробно описаны в статье «Incident response plan: что делать при взломе».

Шаг 4. Чистка — только теперь

Когда точка входа и список затронутых файлов известны, чистка становится осмысленным действием, а не игрой в вычищение симптомов.

Порядок такой:

  1. Удалите все найденные бэкдоры одним проходом, а не по одному — если чистить по частям с перерывами, оставшиеся точки входа дают атакующему шанс восстановить удалённое до того, как вы найдёте остальное.
  2. Закройте саму уязвимость: обновите CMS и плагин, через который пришёл файл, удалите неиспользуемые плагины и темы, если точка входа — заброшенный компонент, ограничьте исполнение PHP в директориях загрузки:
location ~* /uploads/.*\.php$ {
    deny all;
}
  1. Уберите закрепление, найденное на шаге 3: чужие ключи из authorized_keys, посторонние cron-задачи, лишних пользователей ОС и админов CMS.
  2. Сверьте очищенный код с эталоном — с git-репозиторием проекта, если он есть, или с чистой копией дистрибутива CMS того же релиза. Совпадение контрольных сумм системных файлов CMS с официальным дистрибутивом — куда надёжнее, чем «на глаз вроде чисто».

Восстановление из бэкапа — рабочий вариант, но с оговоркой: берите точку до предполагаемой даты компрометации, а не первую попавшуюся вчерашнюю копию — если бэкапы делаются каждый день, велик шанс, что и вчерашний архив уже содержит шелл. И даже восстановленную из чистого бэкапа копию стоит повторно прогнать через проверку на признаки заражения — таблица команд для этого в статье «Как проверить сервер на майнер и вирусы».

Шаг 5. Смена всех паролей и ключей — без исключений

Пока не ясно, что именно успел увидеть атакующий за время работы веб-шелла, разумно считать скомпрометированным всё, до чего теоретически можно было дотянуться с текущими правами процесса веб-сервера или дальше — если была попытка повышения привилегий.

Что менятьПочему
Пароли админ-панели CMS и всех учётных записей с правами публикацииВеб-шелл обычно выполняется от имени веб-сервера — оттуда легко читаются конфиги с паролями к БД и сессии админов
Пароль к панели хостинга/провайдераЕсли конфиги сайта содержали доступ к API панели или письма с паролем лежали в почтовом ящике на том же сервере
SSH-ключиСтарые ключи считать потенциально скомпрометированными, если был шанс читать ~/.ssh или домашние директории; сгенерировать новую пару и убрать старый публичный ключ из authorized_keys
Пароли к базе данныхКонфиг подключения к БД — первое, что ищут в файлах CMS
API-ключи и токены сторонних сервисов (платёжные шлюзы, почтовые API, облачные хранилища)Хранятся в тех же конфигах, что и пароль к БД, и часто не имеют ограничения по IP
FTP/SFTP-паролиКлассический путь повторного заражения — забытый одинаковый пароль на нескольких аккаунтах

Меняйте не выборочно, а всё сразу — частичная смена паролей часто оставляет ровно ту одну точку доступа, через которую атакующий вернётся.

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

  • есть признаки, что скомпрометирован root или системные бинарники (например, изменённые ps/ss/top, которые перестали показывать реальную картину);
  • масштаб затронутых файлов неясен даже после разбора логов и поиска по паттернам;
  • сервер жил без должного контроля долгое время и вы не уверены, что это первая компрометация;
  • цена часа вашего времени на углублённую проверку каждого файла выше, чем цена разворачивания сервера заново из чистого образа и переноса только проверенных данных.

Переустановка с нуля — не признание поражения, а рациональный выбор: гораздо проще гарантировать чистоту системы, поднятой заново, чем доказать себе и другим, что вычищенная система действительно чиста на сто процентов.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Можно ли просто восстановить сайт из бэкапа и не разбираться, откуда пришёл шелл?

Можно, но тогда вы не устраняете причину — если точка входа осталась открытой (уязвимый плагин, слабый пароль, забытая форма загрузки), восстановленный сайт заразят повторно, обычно в течение недель, а не месяцев.

Обязательно ли обращаться к специалисту по форензике?

Нет, если масштаб и точка входа ясны из логов и файловой системы, а компрометация ограничена уровнем сайта. Да, если есть признаки root-доступа, руткита или вы обслуживаете данные с регуляторными требованиями (платежи, персональные данные) и нужна формальная фиксация инцидента.

Как понять, что чистка полная, а не временная?

Контрольная точка — контрольные суммы файлов CMS совпадают с официальным дистрибутивом, в authorized_keys и списке пользователей нет лишнего, cron чист, а логи за несколько дней после чистки не показывают повторных попыток обращения к удалённым путям.

Нужно ли уведомлять пользователей сайта о взломе?

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

Сколько времени в среднем занимает весь цикл — от находки до полного восстановления?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →