Перейти к содержимому

Как найти скрытый процесс Linux: расследование через сравнение /proc и ps

Как найти скрытый процесс Linux: расследование через сравнение /proc и ps
Время чтения: 10 мин.

На сервере с Debian при разборе инцидента всё выглядело чисто: ps aux показывал стандартный набор — systemd, sshd, nginx, пара кронов. CPU в норме. Но сетевой мониторинг фиксировал два established-соединения на внешние адреса, а ss -tupna показывала эти соединения без имени процесса — пустые поля вместо PID. Три команды в /proc — и нашёлся PID, который ps упорно не показывал. Ещё три — и стала видна полная командная строка атакующего с путём к бинарнику, скопированному в /var/lib под случайным именем. Десять минут работы. Ниже — пошаговый разбор метода, чтобы вы могли повторить его на своём сервере.

Зачем атакующие скрывают процессы на Linux-сервере

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

Криптомайнинг. Майнер жрёт CPU, и если его видно в top или ps, администратор заметит аномалию за минуты. Скрытый процесс работает месяцами, генерируя доход атакующему.

C2-канал (Command & Control — канал управления заражённой машиной). Backdoor, через который атакующий возвращается на сервер. Пока процесс скрыт, доступ сохраняется.

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

Во всех случаях атакующему нужно, чтобы стандартные утилиты администратора — ps, top, htop — не показывали вредоносный процесс. Для этого используются руткиты.

Почему ps показывает не всё: механика rootkit-скрытия

Руткит (rootkit) — вредоносное ПО, главная задача которого — скрыть присутствие атакующего в системе. В классификации MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1014 — её идентификаторы) это техника T1014 — Rootkit, тактика Defense Evasion (уклонение от обнаружения).

Есть два принципиально разных уровня скрытия, и понимание разницы критично для выбора метода обнаружения.

Userland-руткиты: подмена на уровне библиотек

Userland-руткит работает в пользовательском пространстве — там же, где запускаются обычные программы. Самый распространённый метод — перехват через LD_PRELOAD. Это переменная окружения Linux, которая заставляет динамический загрузчик подгружать указанную библиотеку (файл .so) раньше стандартных, включая libc. Если атакующий создаёт .so-файл, в котором переопределена функция readdir() (её используют ps, ls, top для получения списка файлов и каталогов), эти утилиты просто «не видят» нужный процесс.

Именно так работает открытый инструмент libprocesshider: он перехватывает readdir() и пропускает записи с заданным именем. По публичным данным, группировка TeamTNT использовала libprocesshider в реальных кампаниях для сокрытия криптомайнера XMRig.

Активация происходит двумя способами. Первый — через переменную окружения LD_PRELOAD=/path/to/malicious.so. Действует на один процесс, не требует root. Второй — через файл /etc/ld.so.preload. Действует на все динамически скомпонованные программы в системе, требует root.

На практике вредоносные .so-файлы часто размещаются во временных каталогах (/tmp, /dev/shm) или в скрытых подкаталогах домашних директорий, а затем перемещаются в системные пути вроде /usr/local/lib, чтобы мимикрировать под легитимные библиотеки.

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

Kernel-руткиты: скрытие средствами ядра

Kernel-руткит работает внутри ядра операционной системы. Два основных механизма:

LKM-руткиты (Loadable Kernel Module — загружаемый модуль ядра) встраиваются в ядро как модуль и перехватывают системные вызовы напрямую. Они способны скрыть запись о процессе из /proc, делая его невидимым и для ps, и для прямого ls /proc. Это серьёзнее, чем userland-перехват.

eBPF-руткиты — более современный подход. eBPF (extended Berkeley Packet Filter) — механизм ядра для безопасного выполнения программ внутри ядра. Известны proof-of-concept eBPF-руткиты (ebpfkit, TripleCross), демонстрирующие сокрытие процессов и сетевых соединений средствами eBPF. Преимущество для атакующего — загрузка не зависит от конкретной версии ядра и не рискует вызвать kernel panic, в отличие от LKM.

Даже kernel-руткиты оставляют следы. Исследователи отмечают, что некоторые eBPF-руткиты скрывают каталог процесса от ls /proc, но stat /proc/PID может вернуть информацию, если PID известен. Запомните этот нюанс — он пригодится дальше.

Анализ /proc: почему этот каталог надёжнее ps

/proc — виртуальная файловая система (procfs), которую ядро Linux генерирует в оперативной памяти. Для каждого запущенного процесса ядро создаёт каталог /proc/[PID], где PID — числовой идентификатор процесса. Содержимое каталога даёт полную картину:

Файл/каталог Что содержит Зачем при расследовании
/proc/[PID]/cmdline Командная строка запуска (аргументы через нулевой байт) Восстановить, как именно запущен процесс
/proc/[PID]/exe Символическая ссылка на исполняемый файл Найти и скопировать бинарник
/proc/[PID]/maps Карта памяти (загруженные библиотеки) Обнаружить подгруженные вредоносные .so
/proc/[PID]/fd/ Открытые файловые дескрипторы Увидеть сокеты, файлы, пайпы процесса
/proc/[PID]/status UID владельца, потребление памяти, состояние Определить, от чьего имени работает процесс
/proc/[PID]/environ Переменные окружения процесса Найти LD_PRELOAD и другие подозрительные переменные

Когда вы запускаете ps aux, утилита сама обращается к /proc, читает эти файлы и форматирует вывод. Но если между ps и /proc стоит перехватчик (LD_PRELOAD-библиотека, подменяющая readdir()), ps покажет отфильтрованный список. Прямое обращение к /proc через встроенные команды оболочки использует другой путь выполнения — и если перехватчик нацелен только на конкретные утилиты, скрытый процесс станет виден.

Два источника одних данных с разной уязвимостью к подмене. Разница между ними — индикатор компрометации.

Сравнение /proc и ps: пошаговый поиск скрытых процессов на сервере

Переходим к практике. Что нужно: SSH-доступ к серверу с правами root (или sudo), стандартные утилиты ps, ls, cat.

Шаг 1. Собираем PID из /proc. Перечисляем все числовые каталоги — каждый соответствует активному процессу:

ls -d /proc/[0-9]* | sed 's|/proc/||' | sort -n > /tmp/pids_proc.txt

ls -d выводит только имена каталогов. Шаблон [0-9]* фильтрует по числовым именам. sed убирает префикс /proc/, sort -n сортирует числа по возрастанию. Результат — текстовый файл со списком PID, которые знает ядро.

Шаг 2. Собираем PID из ps. Получаем список PID, которые показывает утилита ps:

ps -eo pid --no-headers | tr -d ' ' | sort -n > /tmp/pids_ps.txt

Флаг -eo pid выводит только столбец PID для всех процессов. tr -d ' ' убирает пробелы форматирования.

Шаг 3. Сравниваем и ищем расхождения:

# PID из /proc, которых нет в ps — кандидаты на скрытые процессы
comm -23 /tmp/pids_proc.txt /tmp/pids_ps.txt

comm -23 выводит строки, которые есть только в первом файле. Если результат непустой — эти PID видит ядро, но ps их скрывает.

На чистой системе вывод будет пустым. Иногда проскочит 1-2 PID короткоживущих процессов ядра — они появляются между выполнением двух команд (гонка состояний, а не заражение). Повторите команду 2-3 раза: если один и тот же PID стабильно присутствует — это сигнал к расследованию.

При eBPF-руткитах метод с ls /proc может не сработать, потому что руткит скрывает каталог и от ls. Тогда используйте перебор с stat: пройдитесь по диапазону PID командой for pid in $(seq 1 65535); do stat /proc/$pid > /dev/null 2>&1 && echo $pid; done и сравните результат с ps. В ряде исследований показано, что stat обходит eBPF-скрытие процессов.

Восстановление командной строки процесса: что делал атакующий

Допустим, сравнение выявило скрытый PID — для примера возьмём 764. Сначала убеждаемся, что каталог существует: stat /proc/764. Если команда вернула информацию — процесс реален.

Командная строка. Файл /proc/764/cmdline содержит аргументы запуска, разделённые нулевым байтом (\0). Читаем и делаем вывод читаемым: cat /proc/764/cmdline | tr '\0' ' '. Результат может выглядеть как /var/lib/randomname --config /tmp/.X12-unix/cfg — или как имитация системного процесса: [kworker/0:3] с пробелами в конце. Атакующие часто маскируют имя процесса под системный (Masquerading, T1036 — техника применима в том числе на Linux).

Исполняемый файл. Ссылка /proc/764/exe ведёт к бинарнику: ls -la /proc/764/exe. Если файл удалён с диска, путь покажет суффикс (deleted), но бинарник всё ещё можно скопировать: cp /proc/764/exe /tmp/recovered_malware. Получите хеш для проверки: sha256sum /tmp/recovered_malware.

Открытые соединения и файлы. Каталог /proc/764/fd/ содержит символические ссылки на все ресурсы: ls -la /proc/764/fd/. Сетевые сокеты отобразятся как socket:[inode]. Дополнительно — lsof -p 764 (если lsof не скомпрометирован).

Карта памяти. Файл /proc/764/maps перечисляет загруженные библиотеки. Если в списке есть .so-файлы из /tmp, /dev/shm или скрытых каталогов (имя начинается с точки) — это может быть компонент LD_PRELOAD-руткита.

Переменные окружения. Файл /proc/764/environ (разделитель — нулевой байт): cat /proc/764/environ | tr '\0' '\n' | grep LD_PRELOAD. Если LD_PRELOAD указывает на подозрительный .so — вы нашли механизм скрытия.

Автоматизация: unhide и обнаружение rootkit Linux

Ручной метод хорош для понимания принципа и для одноразового расследования. Для регулярных проверок есть инструменты, которые делают то же самое автоматически.

unhide сравнивает /proc, системные вызовы и вывод ps. Установка на Debian/Ubuntu: sudo apt install unhide. На RHEL/CentOS: sudo dnf install unhide (может потребоваться EPEL). Три режима:

unhide proc — сравнивает /proc с ps (автоматизация нашего ручного метода). unhide sys — сравнивает ps с результатами системных вызовов ядра. unhide brute — перебирает все возможные PID и проверяет существование каждого.

Отдельная утилита unhide-tcp принудительно проверяет все TCP/UDP-порты и сравнивает с тем, что показывает ss / netstat. Полезна для обнаружения скрытых сетевых сервисов.

chkrootkit проверяет систему на известные признаки руткитов: модифицированные бинарники, подозрительные модули ядра, скрытые процессы. Установка: sudo apt install chkrootkit, запуск: sudo chkrootkit.

rkhunter — аналогичный инструмент с базой сигнатур, проверяющий целостность системных файлов: sudo rkhunter --check.

Нюанс, о котором часто забывают: если сервер скомпрометирован kernel-руткитом, инструменты, запущенные на этой же системе, тоже могут быть обмануты. Надёжнее загрузиться с внешнего носителя (Live USB) и анализировать файловую систему оттуда. Но на продакшн-сервере, который нельзя останавливать, комбинация ручной проверки /proc + unhide — разумный первый шаг.

DFIR Linux сервер: первые действия после обнаружения скрытого процесса

DFIR (Digital Forensics and Incident Response — цифровая криминалистика и реагирование на инциденты) начинается с момента обнаружения скрытого PID. Вот минимальный чеклист — порядок действий здесь принципиален:

  1. Не убивайте процесс сразу. Сначала соберите артефакты: cmdline, exe, maps, fd, environ, сетевые соединения. После kill часть данных потеряна безвозвратно — /proc/PID исчезает вместе с процессом. Я видел, как команды в панике делали kill -9 и потом неделю пытались восстановить, что вообще произошло.

  2. Зафиксируйте сетевую активность: ss -tupna покажет все TCP/UDP-соединения. Соединения без PID и имени процесса при активном eBPF-рутките — дополнительный индикатор компрометации.

  3. Сохраните бинарник: cp /proc/PID/exe /tmp/evidence_binary и sha256sum /tmp/evidence_binary для проверки на VirusTotal.

  4. Проверьте механизм закрепления (persistence — способ, которым малварь обеспечивает себе автозапуск после перезагрузки). Где искать: — systemd: подозрительные юниты в /etc/systemd/system/ и ~/.config/systemd/user/ — cron: crontab -l, содержимое /etc/cron.d/, /var/spool/cron/ — LD_PRELOAD: файл /etc/ld.so.preload существует и содержит путь к .so — userland-руткит (скрытие через файлы и каталоги, T1564.001) — Модули ядра: lsmod и сравнение с известной базовой линией модулей

  5. Снимите дамп памяти, если есть возможность. Модуль LiME (insmod lime.ko "path=/tmp/memdump.lime format=lime") позволяет получить полный образ RAM для анализа в Volatility.

  6. Изолируйте сервер — только после сбора сетевых артефактов. Ротация SSH-ключей, смена паролей, ревью всех authorized_keys — обязательны.

Статические сигнатуры антивирусов ненадёжны против руткитов: даже минимальная модификация бинарника (добавление нулевого байта) может существенно снизить уровень детекции на VirusTotal у части движков. Поведенческий анализ — сверка /proc с ps, мониторинг LD_PRELOAD, проверка загруженных модулей ядра — остаётся основным методом обнаружения.

На разборах инцидентов я вижу одну и ту же ошибку: команда фиксирует подозрительную сетевую активность, запускает ps aux | grep, не находит процесс — и закрывает тикет как false positive. Сверка с /proc занимает три минуты и не требует ничего, кроме shell-доступа и прав root. Но этот шаг пропускают, потому что привыкли доверять выводу ps. Руткиты существуют именно для эксплуатации этого доверия. Если запомните одну вещь из этой статьи — пусть это будет правило: ps показывает то, что ему разрешено показать, а /proc хранит то, что знает ядро. Разница между ними — индикатор компрометации. Если хочешь не просто статьи, а пройти Linux-форензику системно — на codeby.school есть трек IB Basics, где эти навыки собраны в курс без академического тона.

Эту тему и смежные навыки разбирают на практике в курсе «Цифровая криминалистика и реагирование на инциденты (DFIR)» Codeby Academy.