Docker container escape
The phrase “container escape” suggests an exploit. In practice, most escapes are configuration — the container was handed the means to leave before anyone attacked it.
Worth being precise about, because it changes what you defend against. There are two categories and they need different responses.
Category one: it was never a boundary
Section titled “Category one: it was never a boundary”These need no vulnerability. Each is documented behaviour.
The socket. A container with /var/run/docker.sock mounted can ask the
daemon — which is root — to start another container with / mounted:
# from inside the container that has the socketdocker run -v /:/host --privileged -it alpine chroot /host shThat’s host root. The “escape” is an API call.
--privileged. No capability restrictions, no seccomp, no AppArmor, and host
devices visible. The container mounts the host’s disk and writes to it:
fdisk -l && mount /dev/sda1 /mnt && echo key >> /mnt/root/.ssh/authorized_keysThe docker group. Same as the socket, from a shell instead of a container.
Membership is root.
Host namespaces. --pid=host lets a container see and signal host processes;
--net=host puts it on the host’s network stack, past your firewall rules.
None of these are bugs. They are why the pages on the socket and privileged mode sit at the top of this cluster — patching cannot help you here, and no CVE will ever be filed.
Category two: actual vulnerabilities
Section titled “Category two: actual vulnerabilities”These are real bugs, and they are why runc matters as much as docker.
Leaky Vessels (January 2024) — a set of four:
| CVE | Component | Effect |
|---|---|---|
| CVE-2024-21626 | runc | File descriptor leak → access to the host filesystem |
| CVE-2024-23651 | BuildKit | Race condition → container breakout |
| CVE-2024-23652 | BuildKit | Arbitrary file deletion on the host during build |
| CVE-2024-23653 | BuildKit | Breakout during image build |
Fixed in runc 1.1.12 and BuildKit v0.12.5.
The point isn’t these specific CVEs — they’re patched everywhere by now. It’s
where they lived: runc is the thing that actually creates containers, and a
current Docker with a stale runc is a current Docker with an escape. Two of the
four were in the build path, which means a hostile Dockerfile — from a
dependency, a fork, a PR — was enough. If you build untrusted images in CI,
BuildKit is part of your attack surface.
Check runc separately. Nothing about docker version tells you it’s current.
Related
Section titled “Related”- Hardening Docker — the full checklist.
- Protect the Docker socket — the one that matters most.