A container is not a security boundary in the way a virtual machine is. It is a normal Linux process that the kernel has fenced off with namespaces, cgroups, capabilities, and seccomp — sharing the same kernel as every other container and the host itself. That shared kernel is the crux of container escape: if an attacker who controls a container can subvert one of those fencing mechanisms or exploit a kernel bug, they break out of the container and gain code execution on the host node. In Kubernetes that is close to game over, because the node runs the kubelet, holds other tenants' pods, and often carries cloud credentials. Understanding how real escapes have worked is the fastest way to understand which defenses actually matter. Let us walk through the anatomy and then the layered mitigations.
The classic: overwriting runc (CVE-2019-5736)
runc is the low-level runtime that actually starts containers underneath Docker, containerd, and CRI-O. CVE-2019-5736 exploited how runc handled the /proc/self/exe symlink when executing a process inside a container. A malicious image or a container the attacker had exec access to could overwrite the host's runc binary from inside the container. The next time anyone started a container, the tampered runc executed with host root privileges. One compromised container became host compromise for every future container on the node.
The 2024 class: Leaky Vessels (CVE-2024-21626)
Leaky Vessels, disclosed by Snyk in January 2024, was a set of related flaws. The headline runc issue, CVE-2024-21626, was an order-of-operations bug: a file descriptor referencing the host filesystem was leaked into the container's process, so a working directory set to /proc/self/fd/<n> could resolve to the host root. An attacker could craft a Dockerfile or container config whose WORKDIR pointed through that leaked descriptor and read or write the host filesystem during build or run. The companion CVEs (CVE-2024-23651, CVE-2024-23652, CVE-2024-23653) hit BuildKit, showing that the build pipeline is an escape surface too, not just running workloads.
The kernel path: Dirty Pipe (CVE-2022-0847)
Not every escape lives in the container runtime. Because containers share the host kernel, a kernel vulnerability is a container-escape vulnerability. Dirty Pipe (CVE-2022-0847) was a flaw in the Linux kernel's pipe handling that let an unprivileged process overwrite data in read-only files — including files backing the page cache — enabling privilege escalation. From inside a container, a Dirty Pipe primitive could be used to overwrite files the container should never be able to touch. The lesson: patching your images is necessary but not sufficient; the node kernel is part of your attack surface.
The self-inflicted escape: privileged and hostPath
Most escapes in the wild do not require a CVE at all — they require a misconfiguration that hands the attacker the keys. A container running with privileged: true, with hostPath mounting /, with hostPID, or with the SYS_ADMIN capability has been given the ability to reach the host. There is no exploit to write; the escape is the intended (mis)configuration.
# Everything wrong in one pod: do not ship this
spec:
hostPID: true
containers:
- name: danger
image: alpine
securityContext:
privileged: true # full host access
capabilities:
add: ["SYS_ADMIN"] # mount, etc.
volumeMounts:
- name: host
mountPath: /host
volumes:
- name: host
hostPath:
path: / # the entire node filesystem
Defense in depth: no single control is enough
Because escapes come from runtimes, kernels, and misconfiguration alike, the defense is layered.
Patch fast, everywhere. Keep runc, containerd, and BuildKit current, and patch node kernels on a real cadence — not just when convenient. The runc and Leaky Vessels fixes shipped promptly; the clusters that got hit were the ones running months-old runtimes.
Run non-root, drop capabilities, and disallow escalation. Every capability an attacker does not have is an escape primitive they cannot use.
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
Enforce a seccomp profile. RuntimeDefault blocks dozens of rarely used syscalls that escapes rely on. Running with seccomp: Unconfined throws away one of your best defenses.
Consider stronger isolation for untrusted workloads. Where you run genuinely untrusted code — multi-tenant SaaS, CI runners executing customer builds — a shared-kernel container is the wrong boundary. gVisor (a user-space kernel) or Kata Containers (lightweight VMs) give each workload real kernel isolation, at a performance cost that is worth it for that risk tier.
Detect at runtime. Assume prevention will occasionally fail and watch for the behaviors an escape produces: a shell spawning in a container that should have none, writes to /proc/self/exe, or a mount syscall from an application container.
Escape surface at a glance
| Escape vector | Example | Primary defense |
|---|---|---|
| Runtime binary tampering | runc CVE-2019-5736 | Patch runc; read-only rootfs |
| File-descriptor leak | Leaky Vessels CVE-2024-21626 | Patch runc & BuildKit |
| Kernel vulnerability | Dirty Pipe CVE-2022-0847 | Patch node kernel; seccomp |
| Privileged / hostPath | Misconfiguration | Pod Security restricted; IaC scanning |
| Excess capabilities | SYS_ADMIN, SYS_PTRACE | Drop ALL; add back minimally |
How Safeguard helps
Container escape is a two-front problem — vulnerable components and dangerous configuration — and Safeguard addresses both. Container security scanning inventories the runtime and OS packages in every image so an outdated runc, containerd, or vulnerable libc surfaces before deploy, and software composition analysis tracks those components across your fleet so a newly disclosed runtime CVE maps instantly to every affected image. Griffin AI prioritizes with reachability and exploit intelligence, so a container that is both privileged and running an exploitable runtime flaw is escalated above background noise. If you are comparing runtime and image-focused platforms, see Safeguard vs Aqua and Safeguard vs Trivy.
Containers share a kernel — treat every layer as breakable and defend accordingly. Create a free Safeguard account or read the documentation to find escape-enabling flaws before they are exploited.