Safeguard
Container Security

Container Escape Vulnerabilities: How They Work and How to Stop Them

A container is a process with boundaries, not a virtual machine. When those boundaries fail, an attacker lands on the host. Here is the anatomy of real container escapes — runc, Leaky Vessels, Dirty Pipe — and how to defend against them.

Marcus Chen
Cloud Security Engineer
5 min read

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 vectorExamplePrimary defense
Runtime binary tamperingrunc CVE-2019-5736Patch runc; read-only rootfs
File-descriptor leakLeaky Vessels CVE-2024-21626Patch runc & BuildKit
Kernel vulnerabilityDirty Pipe CVE-2022-0847Patch node kernel; seccomp
Privileged / hostPathMisconfigurationPod Security restricted; IaC scanning
Excess capabilitiesSYS_ADMIN, SYS_PTRACEDrop 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.