PDA

Просмотр полной версии : Пентест контейнеров Docker и Kubernetes: от побега из контейнера до захвата кластера


Sergei webware
19.04.2026, 13:40
https://forum.antichat.xyz/attachments/4951591/img_5246d88f1a.png

На каждом втором red team-проекте с контейнерной инфраструктурой я вижу одну и ту же картину: команда разработки развернула Kubernetes, настроила CI/CD, накатила Helm-чарты - и считает, что контейнеры сами по себе всё изолируют. Мол, контейнер же! Почти виртуалка! Нет. Контейнер - это группа процессов, отгороженных от хоста набором Linux-примитивов: namespaces, cgroups и capabilities. Никакого отдельного ядра. Стоит одному из этих заборчиков дать трещину - атакующий вываливается на хост, а оттуда до cluster-admin остаётся несколько команд.

https://forum.antichat.xyz/attachments/4951591/1776526663800.png

Дальше - полная цепочка атаки при пентесте контейнеров Docker и Kubernetes: от разведки и container breakout до эскалации привилегий в k8s и захвата кластера. Каждый шаг - из реальной практики, с командами, CVE и ссылками на MITRE ATT&CK.
Разведка контейнерной инфраструктуры снаружи и изнутри
Любой пентест контейнеров начинается с определения поверхности атаки. Контейнерная инфраструктура выставляет наружу характерный набор портов и сервисов - опытный глаз распознаёт их мгновенно.
Внешнее обнаружение Docker и Kubernetes
При внешнем сканировании ищем конкретные порты: 2375/tcp (Docker API без TLS), 2376/tcp (Docker API с TLS), 5000/tcp (Docker Registry), 6443/tcp (Kubernetes API Server), 8080/tcp (K8s API без аутентификации - на self-hosted кластерах бывает), 10250/tcp (kubelet API), 10255/tcp (kubelet read-only), 2379/tcp (etcd). Проверка тривиальна:

curl -s http://:2375/version

для Docker или

curl -k https://:6443/api/v1

для Kubernetes API. Если в ответ прилетает JSON с версиями - вы нашли точку входа.

В Shodan и Censys контейнерные сервисы обнаруживаются запросами вида

product:"Kubernetes" port:6443

,

docker port:2375 200 OK

,

http.title:"Kubernetes Dashboard"

. На реальных проектах я неоднократно находил открытые kubelet API и незащищённые etcd-инстансы именно так - просто вбивая запрос в Shodan.

С точки зрения MITRE ATT&CK это этап Discovery - техника Container and Resource Discovery (T1613). Определяем топологию контейнерной среды перед тем, как планировать дальнейшие шаги.
Ориентирование внутри скомпрометированного контейнера
Если начальная точка - скомпрометированный контейнер (через уязвимое веб-приложение, RCE, утёкшие credentials), первым делом определяем контекст. Нужно понять: мы в Docker-контейнере или в Kubernetes-поде, какие привилегии и capabilities доступны, какие volumes смонтированы.

Проверка идёт по простой цепочке. Файлы в

/var/run/secrets/kubernetes.io/serviceaccount/

- значит мы внутри k8s-пода: там лежат

token

,

ca.crt

и

namespace

. Команда

cat /proc/1/cgroup

покажет идентификаторы cgroup - строки с

docker

или

kubepods

подтверждают контейнеризацию. Через

capsh --print

(или чтение

/proc/1/status

с фильтром по CapEff) смотрим текущие capabilities. Наличие

CAP_SYS_ADMIN

в эффективном наборе - считайте, container breakout у вас в кармане.

Bash:



# Быстрая ориентировка внутри контейнера
cat
/proc/1/cgroup
|
grep
-E
"docker|kubepods|containerd"
ls
-la /var/run/secrets/kubernetes.io/serviceaccount/
mount
|
grep
-E
"docker.sock|hostPath"
cat
/proc/1/status
|
grep
-i cap
env
|
grep
-i kube


Отдельно проверяем наличие Docker socket:

ls -la /var/run/docker.sock

. Его монтирование внутрь контейнера - это, по сути, передача root-доступа к хосту. Ещё стоит посмотреть

mount

- hostPath-монтирования часто открывают прямой путь к файловой системе хоста.
Container breakout - техники побега из Docker (https://forum.antichat.xyz/threads/592681/)
Docker escape - ключевой этап в цепочке атаки. По классификации MITRE ATT&CK это Escape to Host (T1611, Privilege Escalation). Условия для побега делятся на три категории: уязвимости runtime, привилегированные контейнеры и мисконфигурации.
Эксплуатация Docker socket
Смонтированный

/var/run/docker.sock

- самый частый вектор побега из контейнера Docker на реальных пентестах. Этот unix-сокет даёт прямой доступ к Docker daemon API на хосте. Я встречаю его в CI/CD-агентах (Jenkins, GitLab Runner), мониторинг-контейнерах и «admin-образах», куда его монтируют «для удобства». Удобство - оно такое, да.

Если внутри контейнера есть клиент

docker

, атака тривиальна:

docker -H unix:///var/run/docker.sock run --rm -it --privileged --pid=host -v /:/host alpine chroot /host /bin/sh

- новый привилегированный контейнер с полным доступом к ФС хоста. Но даже без docker-клиента хватит

curl

:

Bash:



# Создание привилегированного контейнера через Docker API
CONTAINER
=
$(curl -s -X POST --unix-socket /var/run/docker.sock \
-H "Content-Type: application/json" \
-d '{"Image":"alpine","Cmd":["sh"],"HostConfig":{"Privileged":true,"Binds":["/:/host"]}}' \
http://localhost/containers/create | grep -o '"Id":"[^"]*"' | cut -d'"' -f4)
curl
-s -X POST --unix-socket /var/run/docker.sock
\
http://localhost/containers/
${CONTAINER}
/start


Подключаемся через exec API - и вот он, полный доступ к хосту через

/host

. По MITRE ATT&CK это комбинация Container Administration Command (T1609) и Deploy Container (T1610).

Нюанс из практики: иногда между контейнером и Docker socket стоит прокси-фильтр (например, docker-socket-proxy от Tecnativa), который блокирует опасные вызовы API. Тогда стоит попробовать read-only эндпоинты -

/info

,

/version

,

/containers/json

- они часто пропускаются фильтром и дают ценную информацию о соседних контейнерах.
Привилегированные контейнеры и Linux capabilities
Контейнер с флагом

--privileged

(или

privileged: true

в манифесте пода) получает все Linux capabilities, доступ к устройствам хоста, отключённый seccomp-профиль и видимость всех устройств через

/dev

. Изоляция? Какая изоляция - её тут нет.

Побег через cgroups v1 release_agent - классика. Суть: создаём cgroup, указываем

release_agent

(скрипт, который выполнится на хосте при завершении всех процессов в cgroup), записываем команду и триггерим. Работает благодаря

CAP_SYS_ADMIN

и доступу к cgroup filesystem.

Но даже без полного

--privileged

отдельные capabilities опасны.

CAP_SYS_ADMIN

позволяет монтировать файловые системы, создавать namespaces, манипулировать cgroups - каждое из этих действий может привести к container breakout. Комбинация

CAP_SYS_PTRACE

+

CAP_SYS_ADMIN

позволяет через

nsenter

войти в PID namespace хоста. Даже

CAP_DAC_READ_SEARCH

открывает чтение произвольных файлов хоста через

open_by_handle_at

- на этом построен инструмент DEEPCE.

По данным Red Hat (2024 Kubernetes security report), более двух третей организаций замедлили внедрение контейнеров из-за опасений безопасности, а почти половина понесли финансовые потери от инцидентов с контейнерами. Цифры говорят сами за себя.
Уязвимости container runtime: хронология и практика
Уязвимости runtime - самый критичный вектор, потому что позволяют побег даже из правильно настроенного контейнера. Тут уже не мисконфиг - тут баг в самом фундаменте.

CVE-2019-5736 (runc, CVSS 8.6) - классика container escape. Уязвимость в runc до версии 1.0-rc6 (Docker до 18.09.2) позволяла перезаписать бинарник runc на хосте и получить root. Вектор: через подконтрольный образ или через

docker exec

в контейнер, к которому атакующий ранее имел доступ на запись. Причина - некорректная обработка файловых дескрипторов (CWE-668). CVSS-вектор: AV:L/AC:L/PR:N/UI:R/S:C - обратите внимание на S:C (scope Changed), то есть выход за пределы контейнера на хост.

CVE-2024-21626 (runc, CVSS 8.6) - более свежая. В runc 1.1.11 и ранее из-за утечки внутреннего файлового дескриптора (CWE-403, CWE-668) новый процесс через

runc exec

получал рабочую директорию в файловой системе хоста. Прямой доступ к хостовой ФС - побег без привилегированного режима.

CVE-2024-0132 (NVIDIA Container Toolkit, CVSS 9.0 CRITICAL) - TOCTOU-уязвимость (CWE-367) в версии 1.16.1 и ранее. По данным Wiz Research, специально сформированный образ мог получить доступ к ФС хоста. Эксплойт монтировал корневую ФС хоста внутрь контейнера, а дальше через

docker.sock

- привилегированный контейнер и полная компрометация. Затрагивала Docker, containerd и CRI-O. Podman с нативной CDI-поддержкой не пострадал (CDI обходила уязвимый код). Исправлено в 1.17.4, причём первый патч оказался неполным - обход отслеживается как CVE-2025-23359 (CVSS 8.3, тот же класс TOCTOU). Два патча на одну дыру - бывает.

CVE-2025-9074 (Docker Desktop, CVSS 9.3 CRITICAL) - 2025 год. Контейнеры могли обращаться к Docker Engine API через внутреннюю подсеть Docker (по умолчанию

192.168.65.7:2375

) без аутентификации. Работало независимо от настроек Enhanced Container Isolation и флага «Expose daemon on tcp://localhost:2375 without TLS». Атакующий мог выполнять привилегированные команды к Engine API (CWE-668).

CVE-2025-31133 (runc, CVSS 7.3) - свежая, runc до 1.2.7. При маскировке путей через

/dev/null

runc не проверял, что источник bind-mount действительно настоящий inode

/dev/null

(CWE-61, CWE-363). Два вектора: произвольный mount-гаджет для раскрытия информации хоста и DoS.

Ядерные уязвимости - отдельная категория. CVE-2022-0847 (Dirty Pipe, CVSS 7.8, CWE-665) позволяла перезаписывать данные в read-only файлах. Сама по себе это privilege escalation, но в контексте контейнеров она комбинируется: если внутри контейнера доступен бинарник runc, атакующий перезаписывает его вредоносной версией - и при следующем вызове получает выполнение на хосте. CVE-2022-0492 (CVSS 7.8, CWE-287/CWE-862) - уязвимость в

cgroup_release_agent_write

ядра Linux, позволявшая через cgroups v1 release_agent обойти изоляцию namespaces.
Kubernetes pentest - от пода к кластеру
Docker escape даёт root на одной ноде. Атаки на Kubernetes открывают путь к управлению всем кластером. Kubernetes добавляет собственный слой абстракции - и собственный зоопарк векторов атак.
Атаки на kubelet API и незащищённый etcd
Kubelet API (порт 10250) - агент на каждой ноде, управляющий подами. По умолчанию требует аутентификацию, но на self-hosted кластерах нередко встречается

--anonymous-auth=true

или read-only порт 10255. Через открытый kubelet можно выполнять команды в любом поде на ноде: эндпоинт

/run///

принимает POST с произвольными командами. Прямая реализация Container Administration Command (T1609).

Проверка:

curl -k https://:10250/pods

- если в ответ приходит JSON со списком подов, kubelet открыт. Дальше:

curl -k -XPOST -H "Content-Type: application/x-www-form-urlencoded" https://:10250/run/// -d 'cmd=id'

выполнит команду. В свежих версиях Kubernetes эндпоинт

/run/

может быть отключён; альтернатива -

/exec/

через WebSocket/SPDY (удобнее всего через

kubeletctl

). На одном из проектов я обнаружил 14 нод с открытым kubelet в корпоративном EKS-кластере - security group разрешала доступ со всей внутренней подсети. Четырнадцать. В EKS.

Etcd (порт 2379) - распределённая key-value база, где Kubernetes хранит всё состояние кластера: конфигурации, секреты, токены service accounts. По умолчанию etcd требует TLS-аутентификацию клиентскими сертификатами, но эту настройку иногда отключают. Если etcd доступен без аутентификации, одна команда

etcdctl get / --prefix --keys-only

покажет все ключи кластера, а

etcdctl get /registry/secrets//

- содержимое секрета в открытом виде (если не включено шифрование at-rest). Техника Container API (T1552.007, Credential Access).
RBAC misconfiguration и эскалация привилегий в k8s (https://forum.antichat.xyz/threads/592687/)
Role-Based Access Control - основной механизм авторизации в Kubernetes. На практике мисконфигурации RBAC - главный вектор эскалации привилегий в k8s. Проблема стара как мир: для быстрого решения проблем администраторы выдают избыточные права service accounts. «Потом поправим». Не поправят.

Первым делом после получения доступа к поду проверяем возможности нашего service account:

kubectl auth can-i --list

покажет полный список разрешений. Если в ответе есть

create pods

,

create deployments

или (джекпот)

get secrets

- можно двигаться дальше.

Наиболее опасные комбинации RBAC-прав, которые я эксплуатировал на реальных проектах:

ПравоЧто даёт атакующемуТехника MITRE ATT&CK

create pods

Создание привилегированного пода с hostPath-монтированиемDeploy Container (T1610)

create daemonsets

Развёртывание пода на каждой ноде кластера одновременноDeploy Container (T1610)

get secrets

Чтение всех секретов namespace, включая токены других SAContainer API (T1552.007)

create clusterrolebindings

Привязка cluster-admin к своему SAAdditional Container Cluster Roles (T1098.006)

patch pods

Инъекция контейнера в существующий подContainer Administration Command (T1609)

create serviceaccounts/token

Генерация токенов для любого SA в namespaceContainer API (T1552.007)

Самый быстрый путь к cluster-admin: если SA имеет право

create clusterrolebindings

, выполняем

kubectl create clusterrolebinding pwned --clusterrole=cluster-admin --serviceaccount=:

- мгновенно получаем полный контроль. Техника Additional Container Cluster Roles (T1098.006, Persistence/Privilege Escalation). Одна команда - и кластер ваш.

Если прав на создание clusterrolebindings нет, но есть

create pods

- создаём под с

hostPID: true

,

hostNetwork: true

и

hostPath

монтированием

/

в

/host

, после чего

nsenter -t 1 -m -u -i -n -p -- bash

для входа в PID namespace хоста. Классический Escape to Host (T1611).
Lateral movement через Kubernetes-примитивы
После первоначальной эскалации Kubernetes сам становится инструментом для lateral movement. Забудьте про SSH-туннели - атакующий использует нативные механизмы оркестратора, и это красиво (с точки зрения атакующего, конечно).

Service account tokens в Kubernetes до версии 1.24 по умолчанию монтировались в каждый под как долгоживущие секреты. Даже в более новых версиях с bound tokens часто остаются legacy-конфигурации. Каждый скомпрометированный под - потенциальный источник нового токена с другими правами. Через

kubectl get serviceaccounts -A

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

ConfigMaps и Secrets часто содержат credentials к внешним системам: базам данных, облачным API, CI/CD. Команда

kubectl get secrets -A -o json

на кластере со слабым RBAC выгружает все секреты всех namespace разом. На одном из проектов я вытянул таким образом AWS IAM credentials, которые дали доступ к S3-бакетам с бэкапами продуктивных баз данных. Бэкапы. Продуктивных. Баз.

DaemonSets - объект Kubernetes, который гарантирует запуск пода на каждой ноде кластера. Для атакующего это механизм массового развёртывания: один манифест - и ваш код работает на всех нодах одновременно. На практике используется как для закрепления (persistence), так и для криптомайнинга - Compute Hijacking (T1496.001).
Инструменты для пентеста контейнеров Docker и Kubernetes (https://forum.antichat.xyz/threads/592230/)
Полноценный пентест контейнерной инфраструктуры требует специализированного инструментария. Вот что я использую и в каком порядке:

CDK (Container Penetration Toolkit) - основной инструмент для работы изнутри скомпрометированного контейнера. Один статический бинарник, никаких зависимостей. Команда

./cdk evaluate

автоматически проверяет: доступен ли Docker socket, какие capabilities, есть ли "чувствительные монтирования", доступен ли kubelet и K8s API. Для побега -

./cdk run

поддерживает docker-sock-escape, mount-cgroup, service-account-check и другие. Лично у меня это первое, что загружается в контейнер после получения шелла.

kube-hunter от Aqua Security - сканер уязвимостей в Kubernetes. Три режима: удалённо (по IP), изнутри пода и active hunting (пытается эксплуатировать найденное). На проектах запускаю его из скомпрометированного пода в active-режиме - автоматически находит открытые kubelet, незащищённый etcd и мисконфигурации API-сервера.

Trivy - для анализа образов на известные CVE. Если есть доступ к приватному Docker Registry (порт 5000), скачиваю образы и прогоняю через Trivy: часто нахожу уязвимые версии runtime-компонентов или забытые секреты в слоях образа.

Peirates - постэксплуатация в Kubernetes. Автоматизирует сбор secrets с нод, перебор service accounts, проверку RBAC-прав. Незаменим, когда нужно быстро оценить масштаб компрометации в большом кластере.

kubesploit (CyberArk) - пост-эксплуатационный фреймворк, что-то вроде Metasploit для контейнерных сред. Модули для container escape, lateral movement и persistence в Kubernetes.

Для аудита конфигурации (когда нужно показать заказчику масштаб проблем): kube-bench (проверка CIS Kubernetes Benchmark) и kubescape (линтер YAML-манифестов на безопасность).
Безопасность Kubernetes и Docker - что реально останавливает пентестера
За годы работы я составил свой список защитных мер, которые действительно осложняют жизнь атакующему - не «галочки в чеклисте», а реальные препятствия.

Pod Security Standards / Pod Security Admission (замена устаревших PodSecurityPolicies) в режиме enforce с профилем

restricted

- реально блокирует создание привилегированных подов, запуск от root и монтирование hostPath. На проектах, где это настроено корректно, путь от пода к ноде через Kubernetes-примитивы закрыт. Я упирался в эту стену несколько раз - приходилось искать обходные пути через runtime-CVE.

Network Policies - по умолчанию в Kubernetes все поды общаются друг с другом. Грамотные Network Policies с deny-all default и точечными разрешениями резко ограничивают lateral movement. Без них атакующий из одного пода через внутреннюю сеть кластера добирается до etcd, kubelet и API-сервера.

Минимальный securityContext для каждого контейнера -

runAsNonRoot: true

,

allowPrivilegeEscalation: false

,

readOnlyRootFilesystem: true

,

capabilities: drop: ALL

с добавлением только необходимых. По данным Aikido Security, именно такая конфигурация предотвращает эксплуатацию CVE-2022-0492 даже на непатченных ядрах.

YAML:



# securityContext, который реально останавливает container breakout
securityContext
:
runAsUser
:
1000
runAsNonRoot
:
true
allowPrivilegeEscalation
:
false
readOnlyRootFilesystem
:
true
capabilities
:
drop
:
[
"ALL"
]
add
:
[
"NET_BIND_SERVICE"
]
seccompProfile
:
type
:
RuntimeDefault


OPA Gatekeeper / Kyverno - admission controllers, которые перехватывают запросы к API-серверу и валидируют их перед выполнением. Правильно настроенная политика не даст создать под без securityContext, с hostPath-монтированием или с docker.sock.

gVisor и Kata Containers - hardened runtimes с дополнительным слоем изоляции. gVisor перехватывает системные вызовы контейнера через собственное ядро (Sentry), Kata запускает каждый контейнер в лёгкой виртуальной машине. Container escape через ядерные уязвимости вроде Dirty Pipe становится бесполезным - атакующий попадает в ядро gVisor/Kata VM, а не хоста. Ощущение, как будто ломишь стену, а за ней ещё одна стена.

Своевременный патчинг runtime - банально, но критически важно. Между публикацией CVE-2024-21626 и массовым обновлением runc на продуктивных кластерах прошли месяцы. Всё это время среды были уязвимы. Мониторинг версий через Trivy и автоматизация обновлений - необходимость, а не рекомендация.

И наконец - не монтируйте Docker socket в контейнеры. Если CI/CD требует сборки образов - используйте Kaniko (сборка без Docker daemon) или rootless Docker-in-Docker. Каждый смонтированный docker.sock на пентесте превращается в container breakout за 30 секунд. Тридцать. Секунд.
Чеклист для пентеста контейнерной инфраструктуры
Пентест контейнеров Docker и Kubernetes - последовательное движение по цепочке, где каждый этап опирается на предыдущий:

Разведка - обнаружение Docker API, K8s API, kubelet, etcd, Registry (T1613)

Начальный доступ - RCE в приложении, утёкшие credentials, открытый API

Ориентирование - определение контекста: capabilities, volumes, service account, namespace

Container breakout - Docker socket, привилегированный контейнер, CVE runtime (T1611)

Credential access - извлечение SA tokens, secrets, etcd credentials (T1552.007)

Privilege escalation - RBAC abuse, clusterrolebinding, ядерные CVE (T1068, T1098.006)

Lateral movement - перемещение между namespace через tokens, DaemonSet-развёртывание

Захват кластера - получение cluster-admin, доступ ко всем нодам и секретам
Контейнерная изоляция - это не стена, а набор заборов разной высоты. Задача пентестера - найти самый низкий. Задача защитников - убедиться, что все заборы одинаково высоки. На практике хватает одного пропущенного docker.sock, одного SA с правом

create pods

или одного непатченного runc, чтобы цепочка дошла до финала.

Проверьте свои кластеры:

kubectl auth can-i --list --as=system:serviceaccount:default:default

- если в ответе что-то кроме

get

на базовые ресурсы, у вас та же проблема, что и у большинства.

ArdeOS
14.06.2026, 04:00
Очень крутой разбор, спасибо за наводки! Часто падают именно на docker.sock – кажется удобным, а потом бац, полный контроль у врага. Главное — не расслабляться и не думать, что контейнеры сами изолируют, потому что контейнерная безопасность — это всегда несколько слоёв и ошибок достаточно, чтоб пробить.

Valeka
15.06.2026, 12:20
Отличный разбор, особенно по docker.sock – действительно, это классика ошибок в безопасности. Часто забывают, что контейнеры — не ВМ, и изоляция хлипкая, если правильно не настроить. Круто, что перечислил и CVE, и инструменты — для пентеста реально полезно. Главное, не думать, что стандартный сетап защитит от побега и эскалации, всегда стоит проверять и жестко ограничивать права.