Анализ дампа памяти Linux: пошаговый разбор инцидента в volatility3

Production-сервер Ubuntu 20.04 в финтех-компании. CPU стабильно под 90 %. Zabbix фиксирует исходящий трафик на нетипичный внешний IP. На файловой системе — ничего. Антивирус молчал, crontab штатный, автозагрузка чистая. Два дня аналитики ковыряли диск — безрезультатно. Дамп оперативной памяти снимали уже от безысходности, как последний шаг. За первые двадцать минут анализа в volatility3 нашлось всё: процесс с удалённым бинарником, исходящее соединение на криптомайнинг-пул и инъецированный код в адресном пространстве легитимного systemd-сервиса. Без анализа дампа памяти Linux этот тикет закрыли бы как «аномалия мониторинга».
Дальше — пошаговый разбор: как снять дамп RAM с живого Linux-сервера, подготовить symbol tables для volatility3 и провести анализ, который покажет то, что невидимо на файловой системе.
Что живёт в оперативной памяти и почему диск этого не покажет
RAM — рабочая область операционной системы. Всё, что активно прямо сейчас — запущенные процессы, сетевые соединения, загруженные модули ядра, содержимое bash-сессий — находится здесь. Данные энергозависимы: при перезагрузке пропадают безвозвратно. Для DFIR-аналитика (Digital Forensics and Incident Response — цифровая криминалистика и реагирование на инциденты) это жёсткое окно: дамп нужно снять до любого вмешательства в систему. Перезагрузили сервер «для профилактики» — потеряли улики.
Почему диск не заменяет RAM:
Fileless malware не оставляет файлов на диске. Вредоносный код загружается прямо в память процесса и работает оттуда — ни бинарника, ни хеша, ни записи в лог. По данным Hive Security (со ссылкой на Picus Red Report 2026), техника Process Injection — T1055 по классификации MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1055 идентифицируют конкретные приёмы злоумышленников) — входит в топ-10 техник у 80 % проанализированных образцов вредоносного ПО. В Linux Process Injection реализуется через системный вызов ptrace, механизм LD_PRELOAD (переменная окружения, заставляющая загрузчик подключить указанную библиотеку к каждому процессу) или запись в /proc/<pid>/mem.
Удалённые бинарники продолжают работать после удаления файла с диска. Атакующий запускает процесс, стирает файл — в /proc/<pid>/exe остаётся ссылка с пометкой (deleted), но только пока процесс жив. В дампе памяти этот процесс сохраняется целиком.
Bash-история восстанавливается из RAM даже после history -c и удаления .bash_history. Команды хранятся в адресном пространстве процесса bash до его завершения. Атакующие нередко используют bash, awk, base64 и другие штатные утилиты для постэксплуатации (так называемый Living Off The Land) — все вызовы остаются в памяти.
Сетевые соединения фиксируются на момент снятия дампа, включая C2-каналы (Command & Control — обратная связь зловреда с сервером управления), которые не попадают в стандартные лог-файлы.
По данным Mandiant M-Trends 2025, медианное время нахождения атакующего в инфраструктуре до обнаружения — 11 дней. За это время злоумышленник успевает подчистить диск. RAM же хранит следы текущей активности до перезагрузки.
Без инструмента вроде volatility3, который разбирает ядерные структуры данных, сырой дамп — просто бинарный блоб. Можно прогнать strings, но это как искать иголку в стоге: отдельные строки найдёте, а связи между процессами, сетевыми соединениями и кодом — нет.
Снятие дампа памяти Linux — LiME и подготовка
LiME (Linux Memory Extractor) — загружаемый модуль ядра (LKM, Loadable Kernel Module), который считывает физическую память и записывает её в файл. Стандартный инструмент для форензики Linux в production: взаимодействие между пространством пользователя и ядром при снятии сведено к минимуму, дамп получается криминалистически корректным.
Что нужно заранее: root-доступ к серверу, установленные заголовки ядра (linux-headers для текущей версии) и компилятор (build-essential). Для облачных VM есть альтернативный путь — snapshot через API провайдера: VMware отдаёт .vmem-файлы при suspend, KVM — через virsh dump <домен> <файл> --memory-only.
Сборка и загрузка LiME
Собираем модуль и снимаем дамп:
sudo apt install -y build-essential linux-headers-$(uname -r)
git clone https://github.com/504ensicsLabs/LiME.git
cd LiME/src && make
sudo insmod lime-$(uname -r).ko "path=/evidence/mem.lime format=lime"
ls -lh /evidence/mem.lime
Что происходит: make компилирует LiME под текущее ядро. insmod загружает модуль и запускает копирование физической памяти в файл. Формат lime включает метаданные о диапазонах адресов; альтернатива format=raw даёт чистый бинарный дамп без заголовков.
Как понять, что получилось: ls -lh покажет размер файла — он должен быть примерно равен объёму RAM сервера (16 ГБ RAM → файл ~16 ГБ). Если размер нулевой или файла нет — модуль не загрузился; смотрим dmesg | tail на ошибки. Обычно причина — несовпадение версии заголовков ядра.
Для серверов, где нельзя ставить заголовки или компилятор, есть AVML (Acquire Volatile Memory for Linux) от Microsoft. Написан на Rust, скачивается готовым бинарником, запускается sudo ./avml memory.lime. Поддерживает сжатие на лету: флаг --compress уменьшает 8-гигабайтный дамп примерно до 3 ГБ — удобно для передачи по сети.
Фиксация целостности дампа
Сразу после снятия считаем контрольную сумму: sha256sum /evidence/mem.lime > /evidence/mem.lime.sha256.
Зачем: если результаты анализа потребуются для внутреннего расследования, целостность доказательства должна быть подтверждена. Без хеша, зафиксированного до копирования файла на аналитическую станцию, результаты можно оспорить. Типичная ошибка — пересчитать хеш уже после передачи по сети. Считайте прямо на сервере, до переноса.
Symbol tables — ключ к volatility3 для форензики Linux сервера
Volatility3 — открытый фреймворк для анализа дампов памяти (Windows, Linux, macOS). Чтобы он корректно разобрал структуры данных Linux-ядра в дампе, ему нужны symbol tables — таблицы символов в формате ISF (Intermediate Symbol Format, промежуточный формат символов).
Зачем это нужно: ядро Linux хранит информацию о процессах, сетевых соединениях и модулях в специфических структурах (например, task_struct для описания процесса). Расположение полей в этих структурах меняется от версии к версии ядра. ISF-файл сообщает volatility3, где именно в памяти искать нужные поля для конкретного ядра. Без него фреймворк не знает, как интерпретировать сырые байты.
В volatility2 (предыдущая версия) использовались «профили» — заранее собранные описания ядер. Проблема: профиль для ядра 5.4.0-26-generic не подходил для 5.4.0-42-generic. Каждое минорное обновление требовало нового профиля. На production-серверах с нестандартными ядрами аналитик часто оставался без рабочего инструмента. Volatility3 через ISF решает это: формат гибче, поддержка новых ядер — вопрос генерации файла, а не ожидания обновления фреймворка.
Как получить ISF для своего ядра
Определяем версию ядра из дампа и подбираем symbol table.
Версию ядра извлекает команда python3 vol.py -f /evidence/mem.lime banners.Banners. Ожидаемый вывод — строка вида Linux version 5.4.0-26-generic (buildd@lcy01-amd64-029) (gcc version 9.3.0).
Быстрый путь — готовый ISF. Репозиторий volatility3-symbols на GitHub содержит предгенерированные файлы .json.xz для Ubuntu, Debian, AlmaLinux и других популярных дистрибутивов. Ищем по строке версии ядра. Скачанный файл кладём в директорию symbols внутри установки volatility3 — фреймворк подхватит автоматически. На ISF-сервере доступно более 1300 готовых символов.
Ручной путь — dwarf2json. Если готового ISF нет (кастомное ядро, редкий дистрибутив), нужна утилита dwarf2json. Ей нужен vmlinux с отладочными символами — не путать с vmlinuz из /boot/, который сжат и stripped (очищен от debug-информации). Для Ubuntu debug-символы ставятся из пакета linux-image-$(uname -r)-dbgsym. После получения vmlinux команда ./dwarf2json linux --elf /path/to/vmlinux генерирует ISF. Опционально можно добавить System.map для повышения точности.
На практике ручной путь отнимает от тридцати минут до нескольких часов. Рекомендация, проверенная на собственном опыте: собирайте ISF заранее для каждого ядра в вашей инфраструктуре. В момент инцидента разбираться с генерацией символов — потеря критического времени.
Volatility3: пошаговый анализ дампа скомпрометированного сервера
Фреймворк установлен (клонирован с GitHub, зависимости через pip install -r requirements.txt, опционально — pycryptodome и yara-python), ISF на месте. Все команды volatility3 следуют единому формату: python3 vol.py -f <дамп> <плагин>. Ниже — плагины в порядке, который работает в реальных расследованиях: от общей картины к деталям. Volatility3 поддерживает более 40 Linux-специфичных плагинов, но на практике ядро анализа — 7–8 из них.
Процессы и дерево связей — ищем аномалии
Карта процессов — отправная точка любого анализа. Она показывает, что работало на сервере в момент снятия дампа.
Плагин linux.pslist (аналог ps в живой системе) выводит список процессов с PID, PPID (идентификатор родительского процесса), именем, UID и временем создания. Плагин linux.pstree отображает те же данные деревом parent → child — видны цепочки запуска.
На что смотреть в выводе:
- Процессы
bashилиsh, запущенные отwww-dataчерезapache2/nginx— вероятный веб-шелл. В деревеlinux.pstreeэто выглядит как цепочкаnginx → bash → curl → python3. Если видите такое — почти наверняка компрометация. - Процессы из
/tmp,/dev/shm,/var/tmp— легитимные сервисы оттуда не запускаются. - Процессы с UID 0 (root), которых нет в штатной конфигурации. Python или Perl от root без видимой причины — сигнал.
- Несколько экземпляров процессов, которые должны существовать в единственном числе.
Плагин linux.psaux показывает аргументы командной строки каждого процесса — полезно для обнаружения закодированных payload-ов или подозрительных флагов.
Bash-история из памяти — что удалил атакующий
Даже если атакующий выполнил history -c и удалил .bash_history, набранные команды остаются в адресном пространстве процесса bash до его завершения. Плагин linux.bash извлекает их из памяти.
Пример вывода из документации volatility3:
PID Process CommandTime Command
1733 bash 2020-01-16 14:00:36.000000 sudo apt upgrade
1733 bash 2020-01-16 14:00:41.000000 chmod +x meterpreter
1733 bash 2020-01-16 14:00:42.000000 sudo ./meterpreter
Строки chmod +x meterpreter и sudo ./meterpreter — прямое свидетельство компрометации. Meterpreter — агент постэксплуатации из фреймворка Metasploit. Временные метки CommandTime фиксируют точный момент действия и позволяют выстроить timeline.
Нюанс: в выводе иногда появляются «мусорные» строки вроде AWAVH — неинициализированные данные в памяти, а не команды. Не пугайтесь, просто отфильтровывайте при составлении отчёта.
Сетевые соединения — следы C2
Сетевые артефакты показывают, с кем общался зловред в момент снятия дампа.
Плагин linux.sockstat отображает активные сокеты (аналог ss или netstat). Плагин linux.ip.Addr показывает сетевые интерфейсы с IP-адресами, MAC и состоянием.
Что искать:
- Исходящие соединения от процессов, которые не должны ходить в интернет.
systemd-journaldс соединением на порт 443 — аномалия. - Listening-порты вне штатной конфигурации: 4444, 8888 и подобные — типичные порты бэкдоров.
- Интерфейсы в promiscuous mode (режим захвата всего трафика сегмента) — в выводе
linux.ip.Linkвидно по полю Promiscuous. Легитимные серверы в этом режиме не работают; наличие указывает на сниффер.
Подозрительные IP стоит сразу проверить через TI-сервис: например, AbuseIPDB (публичная база репутации IP-адресов; бесплатный API: 1000 запросов/день, abuseConfidenceScore >75 — рекомендуемый порог для блокирования).
Malfind — поиск вредоносного кода в памяти Linux
Malfind — главный плагин для обнаружения fileless malware. Он находит код, не соответствующий ни одному файлу на диске.
Плагин linux.malfind сканирует адресные пространства процессов и выявляет регионы с правами rwx (чтение + запись + исполнение), не привязанные к файлу — так называемые anonymous mappings. Легитимные программы почти никогда не используют rwx для anonymous-регионов. Шеллкод и инъецированные payload-ы — регулярно.
Типичная находка из документации volatility3: PID 540, процесс networkd-dispat (системный сетевой демон), регион 0x7f1506482000–0x7f1506483000, Anonymous Mapping, protection rwx. Дизассемблер показывает характерные паттерны: add byte ptr [rax], al (нулевые байты), stc и другие инструкции, типичные для padding шеллкода. В легитимном системном сервисе без JIT-компиляции такой регион — почти наверняка инъекция (T1055).
Для расширенного поиска — плагин yarascan.YaraScan прогоняет дамп через YARA-правила (YARA — язык описания сигнатур для обнаружения вредоносного ПО). Команда python3 vol.py -f memory.lime yarascan.YaraScan --yara-file rules.yar ищет совпадения с известными семействами зловредов. Подход соответствует D3FEND D3-MBT (Memory Boundary Tracking) — методике MITRE для детектирования аномалий в кодовых сегментах процессов.
Модули ядра — охота на руткиты
Руткиты уровня ядра маскируются как загружаемые модули и удаляют себя из стандартных списков. Вот где начинается самое интересное.
Плагин linux.lsmod выводит загруженные модули ядра — аналог одноимённой команды. Но руткит убирает свою запись из этого списка после загрузки, становясь невидимым для lsmod на живой системе.
Плагин linux.check_modules решает задачу: он сравнивает два независимых источника информации о модулях в памяти и выявляет расхождения. Модуль, который присутствует в памяти, но отсутствует в стандартном списке — красный флаг.
Ещё один полезный плагин: linux.check_creds проверяет credential-структуры процессов на аномалии. Если процесс, запущенный от обычного пользователя, имеет UID 0 в ядерной структуре cred — произошёл privilege escalation (повышение привилегий). Для детектирования сопутствующих изменений стоит обратить внимание на Sigma-правило lnx_auditd_disable_aslr_protection.yml из репозитория SigmaHQ (Sigma — открытый формат правил обнаружения угроз, конвертируемый в форматы конкретных SIEM). Это правило отслеживает отключение ASLR (Address Space Layout Randomization — рандомизация адресного пространства), что атакующие делают для упрощения эксплуатации.
Собираем timeline инцидента
Отдельные плагины дают фрагменты. Задача аналитика — собрать их в хронологию. Порядок, проверенный на реальных кейсах:
banners.Banners→ версия ядра и дистрибутива. Определяет baseline — какие сервисы и процессы ожидаемы.linux.pslist+linux.pstree→ карта процессов с пометками подозрительных.linux.bash→ история команд с временными метками. Привязываем действия атакующего к timeline.linux.sockstat+linux.ip.Addr→ сетевая активность. Коррелируем подозрительные IP с процессами из шага 2.linux.malfind+yarascan.YaraScan→ инъецированный код. Маппим находки на процессы.linux.lsmod+linux.check_modules→ проверка на руткиты.linux.lsof→ открытые файлы подозрительных процессов. Артефакты в/tmp,/dev/shmили с флагом(deleted)— приоритетные.
Имеет смысл извлечь все printable-строки из дампа утилитой strings и прогнать через grep на IP-адреса, URL, пароли — грубый, но быстрый метод первичного triage (первичной сортировки).
Результат — timeline с конкретными временными метками, процессами, сетевыми адресами и артефактами. Это основа для IR-отчёта и следующих шагов: блокировка C2-адресов на firewall, изоляция хоста, поиск lateral movement (горизонтального перемещения) по остальным серверам.
Полезная практика — собрать базовые плагины в bash-скрипт, который прогоняет их последовательно и записывает вывод в отдельные файлы. Пять минут на написание скрипта экономят часы при каждом следующем инциденте. Если хочется по-настоящему прочувствовать весь цикл от дампа до timeline — на HackerLab.pro есть категория forensics с задачами разного уровня; после регистрации доступны таски, где можно разобрать готовый дамп без риска сломать production.
Больше половины DFIR-специалистов, с которыми я пересекался на IR-кейсах, до сих пор работают с volatility2. Не потому что он лучше — по привычке. На ядрах до 5.x это ещё как-то держалось. Начиная с ядра 6.1 Linux перешёл с красно-чёрных деревьев на maple tree для хранения VMA (Virtual Memory Areas — областей виртуальной памяти процессов), и vol2 эти структуры не парсит. Плагины падают или возвращают мусор. Volatility3 с корректным ISF обрабатывает новые ядра штатно — это не вопрос предпочтений, а вопрос работоспособности инструмента. Кто не перешёл — на современных дистрибутивах уже работает вслепую.
В русскоязычном пространстве тема анализа дампа памяти Linux покрыта на уровне «вот как скомпилировать LiME». Интерпретация вывода плагинов, корреляция артефактов, построение timeline — не описано почти нигде. Англоязычные источники (документация volatility3, исследования LevelBlue, практические гайды) ушли на годы вперёд. Для тех, кто строит карьеру в DFIR и расследовании инцидентов Linux, систематическое чтение этих материалов — необходимость. А базовый трек IB Basics на codeby.school — то, что я бы прошёл вместо хаотичного ковыряния в YouTube, когда переходил из системного администрирования в ИБ.
Эту тему и смежные навыки разбирают на практике в курсе «Цифровая криминалистика и реагирование на инциденты (DFIR)» Codeby Academy.