Эта статья для вас, если в 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 ООО «МТ ФИНАНС»

Комментарии (0)