Browse CTFs New CTF Sign in

Tracing Unauthorized Shadow File Access Using Auditd Event Log Forensics

web_auth_sessions Difficulty 1–5 30 min certifiable

Theory

Why This Matters

/etc/shadow stores hashed passwords for all local Linux accounts. An attacker who reads this file obtains hashes for every user, including root, which can be cracked offline or used directly in pass-the-hash attacks against services accepting these credentials. An attacker who writes to it can replace any password hash with a known value, gaining instant access. Because /etc/shadow is typically readable only by root, any non-root process accessing it represents a definitive privilege violation — and because auditd can watch this file at the kernel level, the access is logged even if the attacker clears shell history and other artefacts.

Core Concept

/etc/shadow (and its group equivalent /etc/gshadow) contains one line per user account, with fields including username, hashed password (in $algo$salt$hash format), and password aging data. Default permissions are 640 root shadow or 000 root root depending on distribution. Only shadow-group processes (passwd, sudo, login) and root should access it.

auditd file watch rules (-w /etc/shadow -p rwa -k shadow_access) trigger an audit record for every read (r), write (w), or attribute change (a) on the file. The audit record includes: the calling process's UID, EUID, AUID (audit UID — the original logged-in user), PID, PPID, and the operation type.

Suspicious access patterns: - Any process with EUID != 0 and not in the shadow group reading /etc/shadow - Write access by any process other than /usr/bin/passwd, chpasswd, or similar legitimate tools - Access from a shell spawned by a web server process (apache, nginx, www-data parent chain) - Access immediately following a SUID binary execution (SUID escalation → shadow read)

Technical Deep-Dive

# Audit rule: watch /etc/shadow and /etc/gshadow for read/write/attr
# /etc/audit/rules.d/shadow.rules:
# -w /etc/shadow -p rwa -k shadow_access
# -w /etc/gshadow -p rwa -k shadow_access
# -w /etc/passwd -p wa -k passwd_change
# Reload: augenrules --load

# Search audit log for shadow access events
ausearch -k shadow_access --interpret | grep -E "(type=PATH|type=SYSCALL)"

# Show process details for shadow access
ausearch -k shadow_access --interpret -i 
  | awk '/type=SYSCALL/{print}' | head -20
# Parse ausearch output to extract key fields
ausearch -k shadow_access --interpret --format csv 
  | awk -F, '''{print $1, $6, $7, $10, $11}''' 
  | head -20
# Fields: timestamp, uid, auid, exe, key

# Detect web server processes reading shadow
ausearch -k shadow_access --interpret 
  | grep -E "(apache|nginx|www-data|httpd|php)" | head -10
# Parse raw auditd log for shadow access events
import re, sys

SYSCALL_RE = re.compile(
    r'type=SYSCALL.*?pid=(d+).*?uid=(d+).*?euid=(d+).*?auid=(d+).*?exe="([^"]+)"'
)
PATH_RE = re.compile(r'type=PATH.*?name="([^"]+)"')

events = []
current = {}

for line in open("/var/log/audit/audit.log"):
    if "shadow" in line.lower() or current:
        if "type=SYSCALL" in line:
            m = SYSCALL_RE.search(line)
            if m:
                current = {
                    "pid": m.group(1), "uid": m.group(2),
                    "euid": m.group(3), "auid": m.group(4),
                    "exe": m.group(5)
                }
        elif "type=PATH" in line and current:
            m = PATH_RE.search(line)
            if m and "shadow" in m.group(1):
                current["file"] = m.group(1)
                events.append(dict(current))
                current = {}

for ev in events:
    if ev.get("euid") not in ("0",):
        print(f"SUSPICIOUS: {ev}")

Analytical Methodology

  1. Verify that auditd shadow watch rules are present on the host: auditctl -l | grep shadow. If rules are not loaded, there will be no auditd evidence — but filesystem metadata (ctime of /etc/shadow) can still confirm access.
  2. Run ausearch -k shadow_access --interpret. Review all hits. For each, identify: calling process executable path, UID, EUID, and AUID.
  3. Legitimate accesses: passwd, chpasswd, useradd, usermod, sshd (with UsePAM), and sudo (when changing passwords). Flag everything else.
  4. Identify the process tree: use the PID from the auditd record to reconstruct the parent chain. A shadow read from python3 whose parent is apache2 confirms web shell to shadow escalation.
  5. Determine the operation type: read (information disclosure) vs write (credential replacement). A write is more serious — check whether any account's password hash changed by comparing /etc/shadow to a backup or by looking for subsequent successful logins from unexpected accounts.
  6. If a read is confirmed, assess whether offline cracking is likely. MD5crypt ($1$) and SHA-512crypt ($6$) hashes can be cracked with hashcat. Report the exposed accounts and recommend immediate password rotation.
  7. Check for SELinux or AppArmor denials that preceded the successful access. An attacker may have disabled or bypassed MAC policy as a precursor step.
  8. Correlate with network logs: was there an outbound connection from the compromised host immediately after the shadow read? This suggests hash exfiltration.

Common Analytical Errors

  • Assuming absence of auditd rules means no access occurred: Even without auditd, /etc/shadow ctime reflects the last inode change. If ctime is newer than expected, access or modification occurred. Cross-reference with backup hashes.
  • Ignoring AUID vs UID: AUID is the audit UID assigned at login and carried across su/sudo transitions. It identifies the original human user even if the process has changed UID to root. A shadow read with AUID != 0 means a non-root user escalated to access shadow.
  • Missing procfs evidence on live systems: On a live host, /proc/<pid>/exe, /proc/<pid>/cmdline, and /proc/<pid>/status expose the running process that accessed shadow. Check immediately if the host is still live.
  • Not checking /etc/shadow for modifications: An attacker who writes to shadow may add a new root-equivalent account with a known password. Always diff /etc/shadow against the most recent backup after a suspected write event.

NICE Framework Alignment

Code Work Role Knowledge / Skill / Task Relevance
K0046 Knowledge of intrusion detection methodologies File integrity monitoring of /etc/shadow is a Linux detection control baseline requirement
K0145 Knowledge of security event correlation tools auditd event correlation with process tree analysis is a SIEM-adjacent Linux forensics technique
K0187 Knowledge of file type abuse by adversaries /etc/shadow is a plain-text system file whose format enables password hash extraction for offline cracking
S0047 Skill in preserving evidence integrity Collecting /etc/shadow with checksums and preserving auditd logs before incident response activities
T0049 Decrypt seized data / analyze forensic artifacts Cracking extracted SHA-512crypt or MD5crypt hashes from /etc/shadow using hashcat or John

Further Reading

  • Linux man page: shadow(5) — /etc/shadow field definitions and hash format
  • auditd documentation: audit.rules(7) — watch rule syntax and field interpretation
  • Hashcat documentation: hash modes 1800 (sha512crypt) and 500 (md5crypt)
  • NSA/CISA Linux Hardening Guide — /etc/shadow permission and auditing recommendations
  • MITRE ATT&CK T1003.008: /etc/passwd and /etc/shadow — OS Credential Dumping sub-technique

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.