
Эта статья для вас, если в top вы обнаружили десятки незнакомых процессов, которые нагружают ЦП и память. И точно для вас, если среди них слишком много kworker, запущенных из нестандартных директорий, ведь майнеры, прокси для ботнетов и реверс-шеллы зачастую маскируются под системные процессы. Под катом расскажу, как понять, не используют ли ваш сервер для атак и спама.
Для начала предупрежу, если сервер уже рассылает спам или каким-либо образом участвует в атаке, сразу ограничьте его сеть через панель провайдера, сохранив доступ через консоль. По возможности сделайте снимок диска.
Кто вообще заходил
Прежде чем искать процессы, посмотрите гостевую книгу сервера:
who # кто залогинен прямо сейчас last -20 # последние успешные входы lastb -10 # последние неудачные попытки из /var/log/btmp
Успешный вход в три часа ночи с IP-адреса, который вы никогда не видели, — будет ответом на вопрос, откуда взялись подвисания. Параллельно гляньте журнал SSH:
journalctl -u ssh --since "3 days ago" | grep -i accepted grep "Accepted" /var/log/auth.log | tail -20
Если записей много, отберите сообщения об успешной аутентификации:
journalctl -u ssh.service -u sshd.service --since "3 days ago" --no-pager \ | grep -E 'Accepted (publickey|password|keyboard-interactive)'
На системах, где настроена запись в /var/log/auth.log, можно посмотреть и его. В семействах RHEL аналогичные сообщения часто находятся в /var/log/secure.
Если увидели что-то, не паникуйте и подумайте, не давали ли вы доступы коллегам, не подвязывали ли прокси и т. д. Если объяснения не нашли, продолжаем расследование и проверяем ключи:
grep -HnEv '^[[:space:]]*(#|$)' \ /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null
Сверяйте ключи с теми, которые вы знаете или где вы сами указывали комментарий. Файла может не быть, это норма, если ключи лежат в других местах.
Домашние каталоги не обязательно находятся в /home, а OpenSSH позволяет указывать собственные пути к ключам через AuthorizedKeysFile или вообще получать их через AuthorizedKeysCommand. Если SSH у вас настроен нестандартно, проверьте его конфигурацию и узнайте, откуда именно сервер берёт ключи:
sshd -T | grep -Ei 'authorizedkeys(file|command)'
Заодно посмотрите, нет ли в системе учётных записей с UID 0:
getent passwd | awk -F: '$3 == 0 {print}'
Обычно тут будет только root. Если есть ещё одна учётная запись, выясните, кто и зачем её создал.
Есть ли лишние процессы
После истории входов переходим к дереву процессов. Оно полезнее одного имени в top, потому что показывает «соседей» и «родителей».
ps auxfww
Там ищем процессы от www-data или nginx, которые запускают bash, python или curl — веб-серверу это не нужно, а вот веб-шеллу очень даже.
Дальше — имена-оборотни, например, майнеры любят называться kswapd0, kworker или обычным systemd. Отличить их на самом деле просто, так как настоящие потоки в ps будут в квадратных скобках и без пути к бинарнику.
Также смотрите процессы без файла на диске. Некоторые вредоносы удаляют бинарник после запуска, но ядро-то его помнит:
ls -l /proc/[0-9]*/exe 2>/dev/null | grep ' (deleted)$'
Каждая находка с пометкой deleted — это процесс, чей исполняемый файл стёрт с диска. Для подозрительного PID смотрим, откуда он вообще запустился:
ls -l /proc/12345/exe # куда указывает бинарник ls -l /proc/12345/cwd # рабочий каталог cat /proc/12345/cmdline | tr '\0' ' ' # полная команда запуска
Более широкий поиск даст lsof:
lsof -nP +L1
Он показывает открытые файлы с числом жёстких ссылок меньше единицы — в списке будут не только программы, но и удалённые логи, временные файлы и другие объекты. Заодно это поможет найти того, кто удерживает место на диске.
Если проблема началась с загрузки CPU, проще отсортировать процессы по потреблению:
ps -eo pid,ppid,user,%cpu,%mem,etime,args --sort=-%cpu -ww | head -n 16
Важно, %cpu показывает отношение накопленного процессорного времени к времени жизни процесса, а не загрузку за последнюю секунду. Для текущей картины оставьте открытым top или htop, а для истории глядите мониторинг.
Допустим, внимание привлёк PID 12345. В следующих командах замените его своим значением и выполняйте их в той же оболочке:
pid=12345 ps -p "$pid" -o pid,ppid,user,lstart,etime,args -ww ls -l "/proc/$pid/exe" "/proc/$pid/cwd" tr '\0' '\n' < "/proc/$pid/cmdline" lsof -nP -p "$pid"
Так увидите время запуска, исполняемый файл, рабочий каталог, аргументы и открытые файлы.
Кто слушает порты
Дальше смотрим сетевые сокеты:
sudo ss -tulpn
Нам важна колонка Local Address — каждый такой порт вы должны узнавать по имени. Неизвестный процесс, слушающий случайный порт, — это повод копнуть. Там же в выводе ss видно имя процесса и PID. Проверить, что за программа за портом, можно и через lsof:
sudo lsof -nP -i :4444
Если на сервере есть Docker, посмотрите публикацию портов:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
Прежде чем искать руткит, стоит вспомнить и про собственный docker compose. Соединения проверяем отдельно:
ss -tnp state established
Веб-сервер часто держит кучу входящих соединений на 443-й порт, это ок. Не ок, когда сервер сам держит десятки соединений наружу на странные порты незнакомых адресов. Порты вроде 3333 и 4444 часто встречаются у Stratum-пулов, поэтому смотрите адрес назначения, процесс и характер трафика.
Незнакомый IP проверьте через AbuseIPDB — если адрес уже замечен в сканировании и брутфорсе, вопросов не останется. А если хочется увидеть, кто именно из процессов льёт трафик, ставьте nethogs и смотрите вживую:
sudo nethogs # трафик по процессам в реальном времени
Где прячут автозапуск
Просто убить процесс недостаточно, через пять минут его поднимет автозагрузка. В связи с этим проверяем типовые места:
crontab -l # крон текущего пользователя ls /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ systemctl list-unit-files --state=enabled # включённые юниты
Обратите внимание на подозрительные юниты с невинными именами, запускающие скрипт из /tmp. Также загляните в /etc/rc.local, и в ~/.bashrc, и в ~/.profile — туда тоже любят писать однострочники.
Отдельный момент — /etc/ld.so.preload. Через него можно системно подгружать разделяемые библиотеки перед запуском программ, поэтому файл точно нужно проверить. На обычном сервере его зачастую вообще нет:
test -f /etc/ld.so.preload && cat /etc/ld.so.preload
Если разбираете конкретный подозрительный процесс, отдельно посмотрите, не был ли он запущен с LD_PRELOAD:
tr '\0' '\n' < /proc/12345/environ 2>/dev/null | grep '^LD_PRELOAD='
А что если ps врёт
Может и врать, ведь руткиты подменяют системные библиотеки, и ps иногда их не показывает. Против этого есть два хода.
Первый — инструменты типа unhide. Утилита сравнивает вывод ps с тем, что видно через /proc, системные вызовы и перебор всех PID, а её напарник unhide-tcp ищет порты, которые слушаются, но не показываются:
apt install unhide unhide quick reverse # быстрый поиск скрытых процессов unhide-tcp # скрытые слушающие порты
Второй ход, сканеры руткитов. Из хороших: rkhunter проверяет хеши системных утилит, типовые файлы руткитов и странные права, а chkrootkit дополняет его. Ставятся оба через:
apt install rkhunter chkrootkit rkhunter --check --sk chkrootkit
Для общей проверки здоровья сервера добавьте к ним Lynis. Он не ищет вредоносов, но прогоняет систему по чек-листу харденинга.
И ещё один приём для параноиков (я считаю эту паранойю здоровой). Если подозрения серьёзные, смотрите на сервер снаружи, а не изнутри. Например, просканируйте его с другой машины через nmap и сравните список открытых портов с тем, что показывает ss на самом сервере:
nmap -Pn -p- --open IP_СЕРВЕРА
Если порт виден снаружи, но его нет в выводе ss — это повод задуматься о рутките, но сначала исключите DNAT, Docker/другие network namespaces, reverse proxy, балансировщик и сетевые правила провайдера. К слову, на VDS можно поглядеть и графики трафика в панели.
Что делать, если нашли
Тут будет непопулярное мнение — не надо чистить взломанный VDS руками. Вы никогда не будете уверены, что нашли всё. Да и злоумышленник мог оставить три бэкдора там, куда вы не заглянули. Что советую:
Во-первых, сделайте снапшот, если хотите потом спокойно разобраться, что случилось.
Во-вторых, пересоздайте сервер из чистого образа и перенесите только данные, причём переносите файлы, а не целиком каталоги с правами и кронами.
В-третьих, поменяйте все пароли и ключи, которые хоть теоретически светились на этом сервере, включая те, что лежали в конфигах.
И в-четвёртых, закройте дверь. Парольный вход по SSH выключите, оставьте ключи, поставьте fail2ban и обновите тот софт, через который они залезли. К слову, чаще всего это древний WordPress или открытый панельный порт.
Ну и есть вещи, которые во время инцидента не наверстать. Например, установка auditd не расскажет, какую команду запускали вчера. Для записи запусков программ нужны правила аудита. Например, на 64-битной системе с работающим auditd и доступным изменением правил можно добавить:
auditctl -a always,exit -F arch=b64 -S execve,execveat -k exec_monitoring ausearch -k exec_monitoring -ts recent -i # события за последние 10 минут
Это временное правило для запусков через 64-битные системные вызовы. На x86-64 с поддержкой 32-битных программ при необходимости добавляют аналогичное правило с arch=b32. Важно, учёт всех запусков сам грузит машину, поэтому аудит нужно настраивать под нагрузку и правила хранения журналов.
После короткой проверки временное правило можно удалить, повторив его параметры с -d:
auditctl -d always,exit -F arch=b64 -S execve,execveat -k exec_monitoring
Если добавляли вариант для b32, удалите его тоже. Постоянные правила лучше разместить в /etc/audit/rules.d/. Заранее проверьте, чтобы события писались.
Для контроля изменений файлов лучше AIDE — его смысл в том, чтобы создать эталон на заведомо чистой машине, защитить эту базу от подмены и затем сравнивать с ней состояние системы. А Lynis, про который я уже говорил, полезен для проверки настроек безопасности.
Если есть возможность, журналы шлите на отдельную машину, где нельзя удалять записи. Для обычных системных логов используйте rsyslog или syslog-ng.
Ну и наконец
Проверка, которую я описал, занимает минут десять-пятнадцать, и я советую прогонять её хотя бы раз в месяц, даже когда всё спокойно… Для личного спокойствия, так скажем.
А у вас хостер хоть раз присылал абуз? Расскажите, что оказалось на сервере и через что залезли — интересно, кто с чем сталкивался.
© 2026 ООО «МТ ФИНАНС»