Linux kernel exploits: PwnKit, Dirty Pipe and OverlayFS
When a kernel exploit makes sense, how to get the exact version, which exploits work in each range (PwnKit, Dirty Pipe, Baron Samedit, OverlayFS) and how to compile them.
A kernel exploit is the last bullet, not the first: it can hang the machine and it is almost never the path the creator intended. But a handful of vulnerabilities are so reliable and so widespread that they are always worth checking, especially on older machines. This guide is about when to try them and which ones.
Warning
On a real pentest, do not run a kernel exploit without explicit permission: a failure can take down a production server. On HTB, at worst you reset the box.
First: is this the way?
If the machine is from 2020 or later and you have not checked sudo -l, SUID, capabilities, cron and credentials, it is not time yet (see sudo, SUID and capabilities and cron, PATH and groups). Signs that it is:
- The kernel or the OS is very old for the machine's release date.
- There is nothing else after a full enumeration.
- The box hints at it (an email about "patching the kernel" on TwoMillion).
Getting the exact versions
uname -a # kernel version
cat /etc/os-release # distribution and release
sudo --version | head -1 # sudo version
pkexec --version # polkit (PwnKit)
dpkg -l | grep -E 'linux-image|polkit|sudo' # packages and their exact patch level
Careful: an Ubuntu with kernel 5.15.0-56 may be patched even if the number "sounds" vulnerable. Distributions backport fixes without changing the base version; compare against the distro's security notice (USN on Ubuntu, DSA on Debian).
Tools that cross-check it for you:
./linux-exploit-suggester.sh # lists candidate CVEs with their likelihood
./linpeas.sh -s # the "Linux Exploit Suggester" section
The ones that work most often
| Name | CVE | Affects | Quick check |
|---|---|---|---|
| PwnKit | CVE-2021-4034 | pkexec (polkit) from 2009 to January 2022, almost every distro |
pkexec exists and is SUID |
| Baron Samedit | CVE-2021-3156 | sudo 1.8.2 to 1.8.31p2 and 1.9.0 to 1.9.5p1 | sudoedit -s '\' $(python3 -c 'print("A"*1000)') gives a malloc error instead of usage |
| Dirty Pipe | CVE-2022-0847 | Kernel 5.8 to 5.16.10 / 5.15.24 / 5.10.101 | Overwrites read-only files such as /etc/passwd |
| OverlayFS / GameOver(lay) | CVE-2023-0386, CVE-2023-2640 + CVE-2023-32629 | Ubuntu with 2023 5.x/6.x kernels | Unpatched Ubuntu 22.04 or 23.04 |
| Dirty COW | CVE-2016-5195 | Kernel 2.6.22 to 4.8.3 | Very old machines |
| nf_tables | CVE-2024-1086 | Unpatched 5.14 to 6.6 kernels | Very reliable; needs user namespaces |
| Looney Tunables | CVE-2023-4911 | glibc 2.34 to 2.38 (Fedora 37-38, Ubuntu 22.04-23.04, Debian 12) | ldd --version |
PwnKit and Baron Samedit are not kernel bugs strictly speaking, but you look for them at the same time and they are the first to try: they work without compiling anything exotic and almost never break the box. Baron Samedit is the privesc on RouterSpace; Dirty COW, an alternative path on Valentine.
Compiling without the headache
The exploit has to be built for the same architecture and a compatible glibc. In order of preference:
- If the target has
gcc, compile there:gcc exploit.c -o exp && ./exp. - Otherwise compile on your machine statically:
gcc -static exploit.c -o exp -lpthread. It works even if the target's glibc is older. - For 32-bit:
gcc -m32 -static ...(you needgcc-multilib). - Some exploits (PwnKit, several OverlayFS ones) have Python or shell versions that need no compiling.
If the binary complains about GLIBC_2.34 not found, you did not build it statically or your glibc is newer: use option 2 or compile inside a container of the same distro release (docker run -it ubuntu:18.04).
Using them carefully
- Read the code first. Many old Exploit-DB exploits modify
/etc/passwdor leave the system unstable; back it up (cp /etc/passwd /tmp/passwd.bak) so you can restore it. - Race-condition exploits (Dirty COW) can take a while or hang the box: try the variants that write a file before the ones that patch memory.
- If the exploit needs it, run from
/tmpor/dev/shmand check they are not mountednoexec(mount | grep shm).
If none of this pans out, go back to the Linux privesc cheat sheet: there is almost always something you have not looked at.