Добавление функции обнаружения дрейфа контейнеров в Google Container Explorer

Оригинал Статьи: https://detect.fyi/adrift-in-the-cloud-a-forensic-dive-into-container-drift-f29524f4f6c4

Автор: Alex John

Я лишь перевел ее и оставил здесь, т.к. эта тема показалась мне интересной.

Я уже давно собирался написать в блоге о криминалистической экспертизе контейнеров. Как вы, возможно, знаете, работа в сфере информационной безопасности — это не только реагирование на инциденты; это ещё и множество исследований и постоянное, бесконечное повышение квалификации — неужели это никогда не закончится!? Поэтому я стараюсь максимально использовать то немногое свободное время, которое у меня есть, и мне очень трудно убедить себя создавать инструменты или писать в блоге о чём-либо во время исследований и разработок. В последние несколько месяцев, когда я не был занят инфракрасными камерами, криминалистикой или автоматизацией, я готовился к экзамену GIAC GRID (кстати, я его сдал). После небольшого перерыва я начал подумывать о том, чтобы наконец-то снова взяться за обнаружение и изучение.

Контейнеры, дрейфующие в океане
Контейнеры, дрейфующие в океане

Введение

В этой статье я расскажу о выявлении дрейфа контейнеров, в частности, об анализе дрейфа контейнеров с точки зрения криминалистики, уделяя особое внимание OverlayFS.

Такие инструменты, как Docker Forensics Toolkit, Kube Forensics (с возможностью мгновенного ответа), Dissects Docker Plugin и Docker & Container Explorer от Google, отлично справляются с задачей анализа контейнеров. Мне очень нравится Container Explorer, поскольку он может работать с системами, управляемыми Docker и containerd. Единственное, что меня не устраивает, — это отсутствие возможности быстро и легко определить дрейф контейнера.

Дрейф контейнера

Итак, что же такое «дрейф контейнера»? Говоря простым языком, это означает, что в файловой системе контейнера произошли изменения, и контейнер отклонился от своего исходного состояния, нарушив принцип неизменяемости. Неизменяемость — это основной принцип контейнеризации. Любые непреднамеренные или несанкционированные изменения в файловой системе должны быть расследованы, и зачастую это первый шаг. Если контейнер был взломан или хост, на котором работает контейнер, был взломан, проведение криминалистической экспертизы контейнера поможет вам оценить масштаб инцидента и определить его первопричину и радиус поражения.

Файловая система контейнера

В контейнерных средах файловые системы имеют многослойную архитектуру, которая эффективно управляет файлами для каждого экземпляра контейнера.

Базовый уровень содержит не изменяемый (read-only) образ, общий для всех контейнеров, а изменяемый слой (writable layer) хранит уникальные изменения, относящиеся к конкретному контейнеру.
В инцидентах, с которыми я сталкивался, встречалась только OverlayFS (объединяющая файловая система). Я бы рекомендовал прочитать:

Мой процесс анализа контейнеров обычно включает:

  1. Проверку конфигурации контейнера.

  2. Анализ образа.

  3. Изучение подключенных томов (volumes).

  4. Поиск секретов в файловой системе контейнера (например, с помощью trufflehog).

  5. Проверку директорий diff/upper, чтобы выявить новые файлы, созданные во время работы контейнера.

  6. Анализ логов контейнера на предмет подозрительной активности.

Исследование - Дрейфа контейнеров

Предположим, вы реагируете на инцидент, связанный с контейнером, управляемым Docker, и можете выполнять действия по реагированию в режиме реального времени. В этом случае я бы рекомендовал выполнить команду docker diff или nerdctl diff (containerd), чтобы увидеть изменения в контейнере.

docker diff <container ID>
nerdctl diff <container ID>

Docker

Я мог бы рассказать вам, как вручную найти доступный для записи слой файловой системы контейнера на диске, или показать вам. Я написал скрипт на Python для определения доступного для записи слоя и его файлов. Давайте рассмотрим вывод этого инструмента.

Определение верхнего (доступного для записи) каталога
Определение верхнего (доступного для записи) каталога

Для заданного контейнера с ID, заканчивающимся на e139, мы можем найти соответствующий файл с идентификатором монтирования по адресу:

/var/lib/docker/image/overlay2/layerdb/mounts/<container ID …e139>/mount-id

Обратите внимание на ID контейнера, выделенный жёлтым цветом? Считайте mount ID из вышеупомянутого файла mount-id — значение выделено синим на скриншоте. Затем можно найти слой diff контейнера, построив путь со значением mount ID, которое мы только что получили (обратите внимание на синюю подсветку на скриншоте выше):

/var/lib/docker/overlay2/<mount ID>/diff

Глядя на древовидную структуру слоя различий, мы видим файлы, которые были созданы и удалены. Удалённые файлы отображаются как пустые файлы в верхнем каталоге. Файл /etc/host.conf был удалён из контейнера, и он выделен. При просмотре файла host.conf видно, что это символьное устройство нулевого размера с номером устройства 0/0.

древовидная структура с отображением добавленных и удаленных (пустых) файлов
древовидная структура с отображением добавленных и удаленных (пустых) файлов

Сравнение вывода скрипта Python с выводом docker diff для контейнера показывает тот же результат, что и ниже. A — добавлено, D — удалено, C — изменено.

Разница между скриптом и докером
Разница между скриптом и докером

Containerd

В containerd каждый слой образа контейнера представляет собой моментальный снимок, для управления которым используется OverlayFS. Containerd использует хранилище «ключ-значение» (Bolt DB), расположенное по указанному ниже пути, для хранения метаданных, связанных с контейнерами, пространствами имён, метками и т. д.

/var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db

Я нашёл браузер CLI Bolt DB здесь, поэтому воспользуюсь им, чтобы найти на диске верхний каталог для заданного контейнера. Давайте откроем файл meta.db с помощью boltbrowser.

Для заданного контейнера с идентификатором, оканчивающимся на ***898f42, запишите значение ключа name в указанном ниже пути в соответствующем пространстве имен. Также см. прикрепленный скриншот.

snapshots -> overlayfs -> <container ID>
Поиск ключа Name
Поиск ключа Name

Значение ключа name показано ниже. Это значение соответствует ключу снимка в другом файле БД — metadata.db.

k8s.io/175/7ee0c6c2ac27dcf810737b6426611e42bd91d963edcad5aa3fd12db20d898f42

Чтобы найти путь на диске, к которому привязан ключ моментального снимка containerd, давайте воспользуемся boltbrowser и откроем файл metadata.db, расположенный по указанному ниже пути.

/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/metadata.db

Открыв файл metadata.db, давайте найдём вышеупомянутое значение ключа name (k8s.io/175/…) в разделе «Снимки». Обратите внимание на ключ id и его значение z, выделенные красным на скриншоте ниже? Значение z в ASCII равно 122 в десятичном виде.

Поиск верхнего каталога
Поиск верхнего каталога

Соответствующая папка в snapshots/122/fs содержит файлы в верхнем каталоге рассматриваемого контейнера. И, конечно же, мы видим явно подозрительный исполняемый скрипт l33t.sh в /opt/pinot.

Список верхних каталогов контейнера, который мы исследовали
Список верхних каталогов контейнера, который мы исследовали

Разве это не было весело и не стоило потраченного времени?

Добавление функции обнаружения дрейфа в инструмент Google Container Explorer

Важно знать, как вручную обнаруживать дрейф контейнеров, но давайте автоматизируем этот процесс. Я с нетерпением жду, когда функция «Контрольные точки контейнеров для криминалистической экспертизы»(с мгновенным откликом) станет общедоступной в Kubernetes. Я хотел создать что-то, что имитирует вывод изменений в файловой системе, генерируемый при создании контрольных точек контейнера с помощью этой функции.

Я мог бы написать другой инструмент с нуля или добавить эту замечательную функцию в существующий. Учитывая тот факт, что Container Explorer поддерживает как Docker, так и containerd, я решил добавить эту функцию в него. При использовании переключателя debug в Container Explorer при монтировании файловых систем контейнера отображаются нижние и верхние каталоги. Но для этого необходимо смонтировать файловую систему контейнера. Бывают ситуации, когда полезно смонтировать всю файловую систему контейнера, но мы, специалисты по реагированию на инциденты, всегда ищем быстрые решения, и анализ всей файловой системы контейнера — это то, чем я буду заниматься только в случае крайней необходимости. Не говоря уже о том, что иногда на хосте может быть несколько контейнеров, и даже не спрашивайте меня о узлах EKS/GKE! Вы можете найти мою версию Container Explorer здесь, в моем репозитории на GitHub.

Мой PR-документ был объединен с проектом.

ДЕМОНСТРАЦИЯ

Для наглядного примера я создал кластер EKS и развернул уязвимый инстанс Pinot. Я выполнил Groovy RCE, сделал снимок состояния узла, создал том и прикрепил его к своему криминалистическому EC2. Я смонтировал том в режиме только для чтения в /mnt/case. Я также смонтировал том с другого скомпрометированного EC2 (с контейнером, управляемым Docker) в /mnt/case-1.

Недавно добавленную функцию обнаружения дрейфа контейнеров можно вызвать из командной строки с помощью drift или псевдонима diff. Она также определяет, является ли файл исполняемым. Чтобы просмотреть дрейф для конкретного контейнера, укажите идентификатор контейнера после команды drift, например:

sudo ce -i /mnt/case/ --output json --support-container-data supportcontainer.yaml drift <container ID here>

Вывод инструмента показывает смещение контейнера от узла EKS.

Дрейф узла EKS — контейнера
Дрейф узла EKS — контейнера

Для контейнеров, управляемых Docker, при указании инструмента на точку монтирования /mnt/case-1 выводится следующий результат.

EC2 — Управляемый Docker дрейф контейнеров
EC2 — Управляемый Docker дрейф контейнеров

На этом этапе я бы запустил сканирование ClamAV и YARA для этих файлов и посмотрел, что получится. Вы также можете скопировать файлы и проанализировать их. Я бы просмотрел файлы журналов на предмет ссылок на эти файлы или большого количества ошибок/исключений, сгенерированных контейнерным приложением в журналах, чтобы увидеть, как файл был загружен/записан в файловую систему.

Вот и всё, ребята! Легко представить себе, что обнаружение дрейфа контейнеров — это панацея в криминалистической экспертизе контейнеров. Обнаружение дрейфа контейнеров действительно упрощает работу специалистов по цифровой криминалистике и реагированию на инциденты, поскольку помогает быстро выявить изменения в контейнере. Однако следует отметить, что сосредоточенность исключительно на дрейфе контейнеров не даёт полного представления об инцидентах, связанных с контейнерами. С точки зрения криминалистической экспертизы «мёртвого ящика» необходимо изучить несколько других критически важных областей, таких как анализ изображений, файлы журналов, данные об оркестровке, переменные среды и секреты, тома и привязки, а также взаимодействие на уровне хоста. Изучая эти дополнительные аспекты CFIR, команды DFIR могут получить более целостное представление о контейнерных средах и лучше реагировать на инциденты.

Ссылки:

  1. https://docs.docker.com/engine/storage/drivers/select-storage-driver/

  2. https://github.com/docker-forensics-toolkit/toolkit

  3. https://github.com/fox-it/dissect.target/blob/main/dissect/target/plugins/apps/container/docker.py

  4. https://github.com/google/docker-explorer

  5. https://github.com/keikoproj/kube-forensics

  6. https://github.com/google/container-explorer

  7. https://sysdig.com/blog/guide-kubernetes-forensics-dfir/

  8. https://www.didactic-security.com/resources/docker-forensics.pdf

  9. https://docs.docker.com/engine/storage/drivers/overlayfs-driver/#deleting-files-and-directories

  10. https://povilasv.me/how-to-monitor-containerd/

  11. https://kubernetes.io/blog/2022/12/05/forensic-container-checkpointing-alpha/

  12. https://kubernetes.io/blog/2023/03/10/forensic-container-analysis/#file-system-changes-rootfs-diff-tar

Комментарии (0)