Пару лет назад я уже публиковал статью о реагировании на инциденты в Linux-системах. Она по-прежнему может быть полезна как практическая шпаргалка, но с тех пор изменился и мой опыт, и требования к таким материалам. В этом цикле статей я хочу разобрать Linux live response более системно.
Большинство материалов по Linux IR сводятся к спискам команд: посмотреть процессы, сеть, пользователей, cron, логи. Проблема в том, что сами по себе команды мало что дают, если нет порядка их применения, ограничения live response, смысл полученного вывода и вообще — методологии, базиса работы.
Этот материал исправляет такие упущения. Он будет разбит на две части. В этой статье обсудим принципы расследования, изоляции, подготовки инструментария, начала анализа и исследования сети и процессов.
Добро пожаловать под кат.

Сценарий, для которого написана статья
В статье обсудим сценарий, в котором специалисту по информационной безопасности поступило указание провести анализ Linux-хоста с неуточненным подозрением на компрометацию. В реальных условиях такой сценарий практически невозможен — почти всегда будет конкретный алерт в SIEM, EDR и т.д., который послужит хорошей подсказкой. Здесь нет ни конкретного алерта, ни указания, с чего начать, ни атрибуции к конкретному вредоносному актору. Именно этот случай самый сложный — приходится работать по умолчанию. Если алерт есть, он, скорее всего, изменит порядок действий, приведенный в этом материале, ведь при алерте, сформированном из события в /var/log/httpd/access_log целесообразно начать с изучения служб, файлов и логов веб-сервера, а не с чего-либо еще.
Тем не менее, статья может быть использована, прежде всего, для расследования «by default», а также для адаптирования под конкретные узкие сценарии.
О чём рассказывается в статье
Статья посвящена live response – анализу работающей системы без ее остановки. Изучаться будут процессы, сервисы, сеть, планировщик, пользователи, логи, артефакты приложений.
Что остаётся за рамками
Memory forensics, reverse engineering вредоносных бинарей, IR в контейнерных и облачных окружениях — каждая из этих тем заслуживает отдельного разговора.
Подозрительный Linux-хост — что дальше?
Основные принципы расследования
Прежде чем запускать первую команду, стоит разобраться с тем, как правильно работать на скомпрометированном хосте. Нарушение этих принципов может уничтожить улики быстрее, чем сам атакующий.
Принцип первый: порядок важнее скорости
Данные, важные для расследования, могут изменяться на хосте с разной скоростью. Как минимум, одни данные могут исчезнуть при перезагрузке или со временем, а другие — остаться на диске или удаленном лог-сервере. Отсюда следует RFC 3227 и его концепция Order of Volatility: начинать расследование нужно с наиболее волатильных данных, постепенно двигаясь к стабильным. Сначала процессы и сеть, в последнюю очередь — логи. Логи пишутся на диск и с высокой долей вероятности могут подождать. Список процессов исчезнет в момент перезагрузки.
Если алерт пришёл в три ночи и первый порыв — сделать shutdown -r now — мы рискуем потерять всё самое ценное.
Принцип второй: минимальный след
Каждое действие на скомпрометированной системе оставляет след и потенциально уничтожает улики. Запуск команды меняет atime файлов. Запись результатов на локальный диск перезаписывает секторы. Установка нового пакета через apt меняет dpkg.log — который, весьма вероятно, потребуется изучить позднее.
Как следствие, результаты анализа лучше сразу отправлять на внешний хост или носитель, а не писать в файлы на исследуемом хосте. Лучше избегать команд установки и лишних перезапусков сервисов.
Принцип третий: цель IR — понять, а не вылечить
Это достаточно контринтуитивный принцип, который часто игнорируют. Реагирование на инцидент — это не операция по удалению вредоносного ПО. Попытка «вычистить» систему и вернуть ее в эксплуатацию после удаления всех найденных файлов не является надежной тактикой. Суть в том, что при хорошо организованной атаке с успешным проникновением никогда нельзя гарантировать, что все вредоносы были найдены и нейтрализованы. Атакующий может оставить несколько механизмов закрепления, из которых будут найдены не все.
Правильный финал расследования — восстановление из резервной копии, сделанной до момента компрометации. Так, NIST SP 800-61 Rev. 3 относит к recovery operations, помимо прочего, восстановление из clean backups. А расследование нужно для другого: установить точное время и вектор заражения, определить, были ли затронуты другие системы, и — главное — собрать все возможные индикаторы компрометации. IP-адреса C2-серверов, хэши и строки вредоносных файлов, имена backdoor-аккаунтов, паттерны поведения — всё это питает YARA-правила, политики EDR, корреляционные правила SIEM и базы threat intelligence. Компрометация одного хоста, расследованная правильно, усиливает защиту всей инфраструктуры.
Шаг 1. Изоляция
Первый практический шаг после определения объекта атаки — изоляция хоста от сети до выяснения. Скомпрометированный хост — это не только жертва, но и плацдарм для дальнейшего развития атаки:
Во-первых, атакующий через C2-канал может продолжать отдавать команды: удалять улики, запускать новые процессы, проводить эксфильтрацию данных.
Во-вторых — и это критичнее — хост становится транзитом во внутреннюю инфраструктуру. Со скомпрометированной машины атакующий видит всё, что видит она: соседние хосты, shared-ресурсы и все, к чему есть сетевая связность. Поверхность атаки остальной инфраструктуры резко расширяется ровно настолько, насколько эта инфраструктура связана с зараженной машиной.
Сетевая изоляция решает эти проблемы. Правила сетевой изоляции должны запрещать скомпрометированному хосту инициировать любые соединения, равно как и принимать их, исключая сотрудников команды IR с выделенными IP.
Правильная изоляция выглядит так: хост помещается в карантинный VLAN или отключается на уровне NAC, либо изолируется средствами EDR — это быстрее всего и не требует физического доступа. Локальный файрвол — iptables или ufw — здесь лишь крайний запасной вариант. Если атакующий получил root, он мог уже изменить правила файрвола или установить руткит, который их обходит. Изоляция на уровне сети надёжнее именно потому, что не зависит от состояния самого хоста.
Естественно, изоляция не означает выключение. Выключение — это потеря всех волатильных данных и прямое нарушение первого принципа расследования. Хост должен оставаться запущенным, сетевые соединения — заблокированы.
Шаг 2. Подготовка: принеси свои инструменты
Атакующий с root-доступом мог изменить систему глубже, чем кажется. Пока не доказано отсутствия повышения привилегий до root, мы не можем доверять файлам системы полностью.
Замена /usr/bin/ps на версию, скрывающую его процессы — классический трюк руткитов. Ещё один — модификация $PATH в системных профилях так, что вместо /usr/bin/ls выполняется /tmp/.hidden/ls с вредоносной нагрузкой. Это означает, что запуская стандартные системные утилиты на скомпрометированном хосте, мы рискуем получить ложный вывод. Или ещё хуже — выполнить код атакующего.
Решение — принести собственные бинари с заведомо чистой машины той же архитектуры и той же версии ОС и переключить окружение на них до начала любого анализа.
Чистая машина должна быть идентична исследуемой по ОС и архитектуре. Это не будет проблемой в корпоративных средах, где нет «зоопарка» ОС.
Что войдет в набор:
Шелл и привилегии —
bash,sh,sudo. Без доверенного шелла всё остальное теряет смысл: если bash скомпрометирован, любая команда потенциально выполняется через него. sudo нужен потому что большинство IR-команд требуют root.Анализ процессов —
ps,pstree,lsof,pidof,pgrep. Это основа live response. ps даёт снимок всех процессов, pstree показывает иерархию — именно по ней видно, что bash был запущен кроном, а не пользователем.lsofвскрывает что конкретный процесс держит открытым: файлы, сокеты.Сеть —
ss,ip,netstat,tcpdump.ssиnetstatпоказывают активные соединения и listening ports — то, что исчезнет при перезагрузке.ipнужен для проверки сетевых интерфейсов и маршрутов: атакующий мог добавить интерфейс или изменить маршрутизацию.tcpdump— на случай, если нужно захватить живой трафик.Файловая система —
find,ls,stat,file,strings,findс фильтрами по времени изменения (-mtime) позволяет найти что появилось на диске в окне компрометации.statпоказывает точные временны́е метки файла.fileопределяет реальный тип файла независимо от расширения — переименованный ELF-бинарь всё равно будет опознан.stringsвытаскивает читаемые строки из бинарей: IP-адреса, пути, URL — потенциальные IOC.Системная информация —
uname,hostnamectl,df,free,dmesg. Первый шаг любого расследования — зафиксировать, что за машина перед нами: архитектура, версия ядра, hostname, время работы. dmesg показывает сообщения ядра — там видно загрузку сторонних модулей, что является признаком руткита.Службы и логи —
systemctl,journalctl,ausearch,auditctl,crontab. Для проверки закрепления: systemd-сервисы, запланированные задачи, auditd-события.Пакетный менеджер —
dpkg(илиrpmдля RHEL-based). Позволяет проверить что установлено на системе и когда —dpkg.logфиксирует время установки каждого пакета.Вспомогательные —
grep,awk,sed,cut,sort,uniq,head,tail,cat,less,tar,gzip. Для работы с текстовыми выводами и логами.
Ниже приведена команда подготовки доверенного набора утилит для Debian 13 x86_64.
mkdir -p /tmp/trusted_staging/{bin,lib}
TOOLS="ps pstree lsof ss ip netstat find grep awk sed cat less head tail
file stat strings ls tar gzip cut sort uniq bash sh sudo
pidof pgrep tcpdump df free uname hostnamectl dmesg
journalctl systemctl ausearch auditctl crontab dpkg"
for tool in $TOOLS; do
path=$(which $tool 2>/dev/null)
[ -n "$path" ] && cp -L "$path" /tmp/trusted_staging/bin/
done
# Библиотеки только для нужных бинарей
for bin in /tmp/trusted_staging/bin/*; do
ldd "$bin" 2>/dev/null | grep "=> /" | awk '{print $3}' | \
while read lib; do
cp -L "$lib" /tmp/trusted_staging/lib/ 2>/dev/null
done
done
tar czf /tmp/trusted.tar.gz -C /tmp/trusted_staging .
Получившийся архив trusted.tar.gz требуется передать на исследуемый хост и развернуть следующим образом:
sudo mkdir -p /mnt/usb sudo tar xzf trusted.tar.gz -C /mnt/usb
export PATH=/mnt/usb/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export LD_LIBRARY_PATH=/mnt/usb/lib
exec /mnt/usb/bin/bash --norc --noprofile
Важный нюанс: sudo — setuid-бинарник. Он работает только если его владелец root и установлен бит s. При копировании этот бит может не сохраниться. После переноса на жертву нужно восстановить права вручную:
sudo chown root:root /mnt/usb/bin/sudo
sudo chmod u+s /mnt/usb/bin/sudo
При этом мы используем системное sudo, то есть все равно проходим через потенциально скомпрометированное окружение. Кроме того, для setuid-бинарей динамический загрузчик работает в secure-execution mode и игнорирует LD_LIBRARY_PATH. Это значит что наш trusted sudo загружает библиотеки из системных путей, а не из /mnt/usb/lib. От подмены самого бинаря он защищён, от подмены его библиотек — нет. Это две проблемы, которые можно считать компромиссом, но если у вас есть метод решения — пожалуйста, дайте знать в комментариях.
Для всех остальных инструментов toolkit — ps, lsof, ss, find и прочих — LD_LIBRARY_PATH работает штатно.
Флаги --norc --noprofile запрещают загрузку .bashrc и .profile — они тоже могут содержать вредоносный код.
Проверить, что монтирование доверенных бинарников прошло успешно, можно с помощью команды which:

Ограничения
Переключение PATH и LD_LIBRARY_PATH на использование своих бинарников не является абсолютной защитой. Если атакующий подменил /bin/bash до того как запущено расследование, всё что выполняется через него потенциально под его контролем. Частичное решение — exec /mnt/usb/bin/bash --norc --noprofile: мы заменяем текущий процесс shell на доверенный. Если было скомпрометировано ядро, руткит на уровне ядра может перехватить системные вызовы независимо от того какие бинарники используются. Если есть подозрение на kernel-level compromise — нужен offline анализ с загрузкой с внешнего носителя.
Кроме того, в ходе анализа могут использоваться бинарники не из доверенного набора — наподобие timedatectl. Риск их подмены низкий: атакующие подменяют то что мешает обнаружению — ps, ls, netstat, find. Подмена timedatectl даёт атакующему мало преимущества.
Всё это — осознанный риск. Полностью доверенное окружение потребовало бы загрузки с внешнего носителя и работы без запуска кода с исследуемого хоста вообще. Это область форензики — за рамками live response. Если такой риск неприемлем для вашей организации, вы можете включить требующиеся бинарники в доверенный набор. Механика остается та же, увеличивается лишь размер архива.
А что насчёт BusyBox?
Статически собранный BusyBox — хороший вариант для базового trusted toolkit: один проверенный бинарь даёт shell и набор минимальных applet-утилит вроде sh, ls, cat, grep, awk, sed, find, ps, netstat, tar, gzip. Но BusyBox едва ли может считаться полной заменой, он не заменит systemctl, journalctl, ausearch, auditctl, dpkg/rpm.
Шаг 3. Фиксация исходного состояния
Для начала надо удостовериться, на какой системе мы находимся. Для удобства в статье приведены результаты выполнения команд на ВМ Debian 13 с несколькими условно вредоносными файлами и службами.
Надо упомянуть, что каждый шаг расследования должен быть задокументирован: команда, время выполнения, вывод. Это важно — через несколько часов аналитик сам не вспомнит, что именно видел в выводе ss до того, как соединение закрылось. Кроме того, если дело дойдет до служебного расследования, потребуются документированные доказательства. Самый простой способ — команда script:
sudo script -a /mnt/usb/ir_session_$(date +%Y%m%d_%H%M%S).log
Эта команда пишет полный лог терминальной сессии — каждую команду и каждый вывод — в файл на доверенном носителе. Завершить сессию логирования — Ctrl+D.

После этого можно идти дальше. uname -a даёт одну строку с самым важным: именем хоста, версией ядра, архитектурой. С неё стоит начать. Вполне возможно, эта команда потребуется еще раньше, на этапе подготовки доверенных бинарников, для определения архитектуры целевой системы.
hostnamectl — то же самое, но добавляет детали, в том числе информацию о Firmware Date.
Зафиксируйте время системы с помощью команды timedatectl. Это важно, особенно расхождение между временем машины и UTC.
uptime показывает время работы системы. Это важно, ведь если машина перезагружалась недавно, часть данных, нужных для анализа, была утеряна.

w и who показывают активные сессии прямо сейчас. На нашей машине залогинены два пользователя: admin014 через tty2 и investigator через pts/0. Активная сессия чужого пользователя во время расследования — красный флаг. Кроме этого, w показывает и аптайм системы.

last даёт историю логинов. Смотреть нужно на незнакомые имена пользователей, логины с нестандартных IP-адресов, логины в нерабочее время.
Кратко про дамп памяти
В полноценном DFIR-процессе дамп памяти желательно снимать ранним шагом — сразу после первичной изоляции и фиксации минимального контекста. Память может содержать то, чего уже нет на диске. Но memory acquisition в Linux — отдельная тема для статьи: Volatility анализирует уже снятый дамп, а сам дамп снимается отдельными инструментами вроде LiME. Поэтому в этой статье memory forensics рассматривать не будем.
Шаг 4. Сетевое состояние
ip a показывает сетевые интерфейсы. Смотрим, сколько интерфейсов и какие адреса. В норме на сервере один внешний интерфейс плюс loopback. Конечно, здесь возможны варианты в зависимости от назначения сервера, поэтому в ходе анализа во всех случаях полезно консультироваться с владельцами сервиса из ИТ. Так или иначе, лишний интерфейс — потенциальный туннель для связи с С2.
ip route show показывает таблицу маршрутизации.

ss -tunp , ss -tulnp , sudo ss -tunp state established
— главные команды этого шага. Запускать обязательно с sudo: без root колонка Process остаётся пустой и половина ценности теряется. Флаги: t — TCP, u — UDP, l — только listening, n — без резолвинга имён, p — показать процесс.

ss -tulnp показывает только listening sockets — что ждёт входящих подключений. Здесь ищем bind shell и backdoor-порты. Если атакующий уже подключился к машине или машина сама установила соединение наружу — это будет в ESTABLISHED. Их надо смотреть через ss -tunp (активные соединения без фильтрации ) и ss -tunp state established (только ESTABLISHED). Важно помнить, что при примененной изоляции установленных соединений быть не должно, кроме соединения с машиной аналитика.
На тестовой машине вывод сразу даёт находку:
tcp LISTEN 0.0.0.0:4444 0.0.0.0:* users:(("nc",pid=11720,fd=3))
Netcat слушает входящие соединения на всех интерфейсах по порту 4444. Это подозрительный listener. pid=11720 — это наш прямой след к процессу для дальнейшего расследования. Всё остальное в выводе легитимно: avahi-daemon на 5353 это mDNS, cupsd на 631 это принтер.
Шаг 5. Процессы и открытые файлы
ps aux — то, с чего стоит начать. Он даст полный список процессов на момент запуск команды. Смотрим на несколько вещей сразу: команда запуска, пользователь, терминал.

Из предыдущего раздела мы помним, что nc -lp 4444 запущен от root с PID 11720. Netcat от root в состоянии LISTEN — это уже достаточный повод для расследования.
Используем команду pstree -p -s 11720 , чтобы увидеть информацию о родительском процессе.

Это критически важная находка. nc запущен напрямую из systemd — значит, это сервис, а не процесс запущенный пользователем. Без pstree мы бы видели просто nc в списке процессов и не знали откуда он взялся. С pstree — сразу понятно: ищем systemd unit.
Команда sudo lsof -p 11720 вскрывает, что процесс держит открытым.

txt — это исполняемый файл: /usr/bin/nc.traditional. Путь к бинарнику зафиксирован. TCP *:4444 (LISTEN) подтверждает что процесс слушает на порту 4444.
Цепочка собралась: ss дал нам подозрительный порт → порт привёл к PID → pstree показал что источник это systemd → lsof зафиксировал бинарник.
Зафиксировав путь к бинарю через lsof, стоит сразу снять с него три артефакта:
sha256sum /usr/bin/nc.traditional
file /usr/bin/nc.traditional
strings /usr/bin/nc.traditional
sha256sum даст хэш файла. Этот хэш можно проверить на VirusTotal, добавить в YARA-правило, передать в EDR как индикатор для hunting по всей инфраструктуре.
file показывает реальный тип файла независимо от расширения и имени. В нашем случае: легитимный ELF 64-bit динамически скомпонованный бинарь для GNU/Linux. Это важная проверка — атакующие иногда переименовывают исполняемые файлы под безобидные расширения или маскируют под системные утилиты. file смотрит на magic bytes, а не на имя.

nc.traditional оказался легитимным системным бинарём — атакующий не подменял файл, а просто использовал штатный инструмент системы в своих целях. Это техника Living off the Land, или LotL — использование легитимных утилит чтобы не оставлять следов собственного вредоносного ПО strings вытаскивает все читаемые строки из бинарного файла. Это первый шаг анализа подозрительного файла без полноценного reverse engineering — быстро и не требует специальных знаний.

В выводе видны импортируемые функции: setsockopt, strcpy, sleep, strncpy — ничего подозрительного. Но на реально вредоносном бинаре strings может показать куда более интересные артефакты: IP-адреса C2-серверов, URL для загрузки ВПО, пути к файлам, имена пользователей, фрагменты команд. Всё это — прямые IOC которые можно сразу передавать в TI.
Для подозрительного файла запускать можно, например, так:
strings <файл> | grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' # IP-адреса
strings <файл> | grep -E 'https?://' # URL
strings <файл> | grep -E '/home/|/tmp/|/var/' # подозрительные пути
Реальный вредоносный файл почти наверняка будет обфусцирован хотя бы частично. Например, скрипт на Питоне может быть обфусцирован при помощи PyArmor. Но даже в этом случае есть шанс найти полезную информацию с помощью strings.
Например, месяц назад я применял strings для изучения этого вредоносного EXE. Несмотря на обфускацию с помощью PyArmor, strings позволил найти несколько характерных строк, которые были использованы для написания детектирующего YARA-правила:
Скрытый текст
rule PyArmor_PyInstaller_Dropper_Bladabindi {
meta:
description = "Detects PyArmor-obfuscated PyInstaller dropper (Bladabindi family)"
author = "MagicHappens"
date = "2026-06-15"
score = 100
reference = "SHA256: b5e9fc679fa9cdaf55585b5f91ccc71e05fc2ed8b08fb214c9f25514f8c9f81f"
strings:
$pyarmor = "pyarmor_runtime_000000\\pyarmor_runtime.pyd" ascii
$pyz = "PYZ.pyz" ascii
$payload = "uusd.exe" ascii
$python = "python313.dll" ascii
condition:
uint16(0) == 0x5A4D and
filesize > 13MB and filesize < 15MB and
$pyarmor and $pyz and $payload and $python
}
ps aux даёт снимок процессов на момент запуска — статичный. Если нужно наблюдать за системой в динамике, используй top, лучше с флагом -с для отображения полных путей к командам.
top -c
Итог первой части
Итак, хост изолирован, окружение подготовлено, состояние системы зафиксировано, сеть и процессы проверены, а лабораторный подозрительный listener привязан к PID, родительскому процессу и конкретному бинарю. Это уже даёт первые IOC и направление дальнейшего анализа.
Во второй части продолжим двигаться по принципу Order of Volatility — от более изменчивых артефактов к менее изменчивым: systemd-сервисам, cron, автозапуску, пользователям, пакетам, файловым артефактам, логам, построению таймлайна и действиям после завершения расследования. В конце второй части будет приведен мини-плейбук с коротким списком описанных в двух частях действий.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ нового VDS — HABRFIRSTVDS.
