The kernel can request that a kernel module is loaded at runtime. If it does so, it will try to call request_module, which will spawn the modprobe tool using call_modprobe. modprobe is a userspace program that runs with root privileges, finds the required kernel module binary on filesystem and loads it.
The path to modprobe is in modprobe_path, a global variable in the kernel. We can read the value as a non-root user through /proc/sys/kernel/modprobe, with the default value being /sbin/modprobe.
If we can overwrite modprobe_path with another binary, e.g. /tmp/exec, this will be run with root privileges! That makes it very easy. To trigger modprobe, the easiest way is to execute a binary with an unknown signature:
To identify what program should be run to handle the signature, the kernel uses (code is slightly different in newer versions). This is run by request_module, but the signature .
The approach, therefore is simple. First compile a /tmp/hijack with source:
There are lots of possible payloads, but the end result is the same. This will copy /bin/sh to /tmp/sh and make it SUID. Now we create a file with an unknown signature:
Finally, overwrite modprobe_path to /tmp/hijack. When we execute /tmp/fake as a regular user, the kernel will spawn /tmp/hijack with root privileges and execute it!