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

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

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

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 (системный сетевой демон), регион 0x7f15064820000x7f1506483000, 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 инцидента

Отдельные плагины дают фрагменты. Задача аналитика — собрать их в хронологию. Порядок, проверенный на реальных кейсах:

  1. banners.Banners → версия ядра и дистрибутива. Определяет baseline — какие сервисы и процессы ожидаемы.
  2. linux.pslist + linux.pstree → карта процессов с пометками подозрительных.
  3. linux.bash → история команд с временными метками. Привязываем действия атакующего к timeline.
  4. linux.sockstat + linux.ip.Addr → сетевая активность. Коррелируем подозрительные IP с процессами из шага 2.
  5. linux.malfind + yarascan.YaraScan → инъецированный код. Маппим находки на процессы.
  6. linux.lsmod + linux.check_modules → проверка на руткиты.
  7. 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.