Articles / Privileged Containers and Container Escapes
Containers · Isolation

Privileged Containers and Container Escapes

Containers isolate with namespaces and cgroups, not with the hardware-enforced boundary a virtual machine gets. That isolation is real but thin, and a container escape is what happens when a process crosses it to reach the host or a sibling container. Escapes come in two flavours: exploits of genuine kernel or runtime bugs, and abuse of configurations that were never isolating in the first place. The second flavour is far more common, and it is entirely on you.

The most dangerous line in any pod spec is privileged: true. A privileged container runs with all Linux capabilities, an unrestricted device list, and most kernel-enforced protections stripped away. It can access host devices under /dev, manipulate the host's cgroups and kernel modules, and mount filesystems. Reaching the host from a privileged container is not an exploit — it is intended behaviour, misapplied. Some workloads legitimately need it (certain CNI and storage plugins); the vast majority that request it do not.

The self-inflicted escapes

Attacker with codeexecution in a containerprivileged / hostPath /mounted runtime socketBreak outmount host, drive daemonRoot on the nodeSibling containers& their secretsThese configs werenever isolation
Most escapes need no exploit — just a config that was never a boundary.
  • Mounting the host filesystem. A hostPath volume of / — or just /etc or the container runtime socket — hands the container the host's files to read and rewrite.
  • Mounting the Docker/CRI socket. /var/run/docker.sock inside a container lets it instruct the daemon to start a new privileged container mounting the host. It is root-on-host by API call.
  • Host namespaces. hostPID, hostNetwork, and hostIPC punch through the very isolation namespaces provide, exposing host processes and interfaces.
  • Dangerous capabilities. CAP_SYS_ADMIN is a near-superset of root; CAP_SYS_PTRACE, CAP_SYS_MODULE, and CAP_DAC_READ_SEARCH each enable well-documented breakout techniques.
Why it matters: Most container escapes in the wild are not zero-days — they are configurations that were never a boundary. privileged: true, a host mount, or a mounted runtime socket removes the wall and then acts surprised when someone walks through.

The exploit-driven escapes

The rarer, headline class exploits real flaws in the runtime or kernel. The 2019 runc vulnerability (CVE-2019-5736) let a malicious image overwrite the host's runc binary during container startup, executing on the host on the next run. The 2022 release_agent cgroups escape (CVE-2022-0492) allowed breakout when certain protections were absent. The 2024 'Leaky Vessels' set (CVE-2024-21626 and related) again put runc-based escapes in the spotlight. Kernel bugs like Dirty Pipe (CVE-2022-0847) are shared by every container on the host because they all share one kernel. The defence against these is unglamorous: patch the runtime and the host kernel promptly, because a shared kernel means a shared exposure.

Building real containment

  • Ban privileged, host namespaces, and host mounts at admission. The Baseline/Restricted Pod Security Standards block these by default — enforce them.
  • Never mount the container runtime socket into a workload. If a pod needs to manage containers, that is an architecture to rethink, not a mount to grant.
  • Drop all capabilities and add back the few needed. Explicitly refuse CAP_SYS_ADMIN unless a workload cannot function without it — and then isolate that workload.
  • Apply seccomp and an LSM. The RuntimeDefault seccomp profile blocks obscure syscalls exploits rely on; AppArmor or SELinux adds mandatory access control on top.
  • Use user namespaces. Remapping container root to an unprivileged host UID means even 'root' in the container is nobody on the host — now stabilising as a native Kubernetes feature.
  • For genuinely hostile multi-tenancy, add a sandbox. gVisor or Kata Containers interpose a user-space kernel or a lightweight VM, so a kernel exploit inside the container never reaches the real host kernel.
The pod spec that is one field away from owning the nodesecurityContext:
  privileged: true
volumes:
- name: host
  hostPath: { path: / }  # host root, writable, from inside the container

Defence in depth here is layered on purpose: admission control stops the dangerous spec, capability dropping and seccomp shrink the exploit surface, patching closes the known bugs, and sandboxing contains the unknown ones. But every layer assumes the workload is one you govern. A privileged container running on a node nobody is watching is the escape you will read about in someone else's incident report — you can't contain what you haven't discovered.

Test for it — in practice
  • Skim running pods for privileged: true, host namespaces, or a hostPath mount of / or the runtime socket — each is a boundary that was never really closed.
  • Spot-check whether workloads drop capabilities to ALL, or quietly carry CAP_SYS_ADMIN.

Finding the obvious ones by eye is a start; systematically hunting every dangerous mount and capability across the fleet is what our assessment runs for you.

Keep reading
Runtime Security and Threat Detection for Containers