The most common container escapes need no memory-corruption exploit at all: the container was simply started with more power than isolation can survive, and the attacker uses that power as designed. Before trying anything, fingerprint exactly what you were given, because it decides which route works.
# Effective capability set (the single most important check)
grep CapEff /proc/self/status
# => CapEff: 000001ffffffffff is the FULL set = --privileged; decode any value with:
capsh --decode=000001ffffffffff
# Seccomp mode: 0 = disabled, 2 = a filter is loaded
grep Seccomp /proc/self/status
# AppArmor/SELinux confinement for this process
cat /proc/self/attr/current 2>/dev/null # "unconfined" means no AppArmor profile
# Device access and the cgroup version/writability
ls -l /dev | head # a long device list hints at --privileged
cat /proc/1/cgroup; mount | grep cgroup # v1 (named hierarchies) vs v2 (unified)
# User namespace: is container root real host root?
cat /proc/self/uid_map # "0 0 4294967295" => UID 0 == host UID 0
Read that output top to bottom: a full CapEff, a visible device list, and Seccomp: 0 together mean --privileged, and every route below is open. A single added capability narrows you to its specific primitive. A 0 0 ... uid_map means whatever you achieve runs as real host root; a mapped range confines it.
Subtopics#
- Privileged flag:
--privileged, which grants everything below at once. - Capability abuse: a single dangerous capability, each with its own route to the host.
- Device access: raw host devices exposed in the container.
- Unconfined seccomp or AppArmor: a weakened syscall and LSM profile.
- cgroups release_agent: a writable release_agent that runs a program on the host.