Exploit the runtime or the kernel underneath¶
Leave the container by breaking something shared with the host rather than something configured:
Identify the kernel and the runtime, both of which are the host’s rather than the container’s
Match them against known escapes
uname -a
cat /proc/version
cat /etc/os-release
ls -la /proc/self/exe
Example: the shared kernel¶
A container image can be years newer than the kernel it runs on, because the kernel is not part of the image. A local privilege escalation that works inside the container works against the host, since there is only one kernel and namespaces do not contain a bug in it.
# the container reports the host's kernel, not the image's
uname -r
grep -i seccomp /proc/self/status
Runtime escapes work on the boundary itself. The 2019 flaw in runc allowed a container process to overwrite the
runtime binary on the host through /proc/self/exe, and the 2024 leaked file descriptor flaw let a working
directory reach the host filesystem. Both are patched, and both illustrate that the code enforcing the boundary
runs on the other side of it.
Notes¶
The container boundary is a set of kernel features, not a hypervisor. Namespaces, cgroups, capabilities and seccomp are all enforced by the kernel the container is trying to escape.
Seccomp narrows the syscall surface substantially and is the control most often disabled for convenience. Checking whether it is active is usually more informative than enumerating kernel versions.
Unprivileged user namespaces are a double edge. They exist to reduce the need for privileged containers, and they also widen the kernel surface reachable without privilege, which is why some distributions restrict them.
Where the workload runs on a virtualised runtime, the escape lands in a guest kernel rather than the host’s, which changes the value of the whole route considerably.