Auditing local user accounts confirms who can authenticate on a system and highlights unexpected privilege changes during security reviews or incident response. Extra logins, unusual shells, or additional UID 0 entries are common red flags worth investigating.
Local account identity data is stored in /etc/passwd and /etc/group, while password hashes and lock state are stored in /etc/shadow. Administrative access is commonly granted through the sudo or wheel groups, or via explicit rules in /etc/sudoers and /etc/sudoers.d.
Regular users typically fall within the UID_MIN to UID_MAX range from /etc/login.defs, but the exact values depend on distribution and policy. Reading /etc/shadow and sudoers files requires root access, and copying those files into tickets or chat logs can leak password hashes or privilege mappings.
Related: How to investigate a Linux intrusion
Related: How to list logged-in users in Linux
Steps to audit local user accounts with getent and passwd in Linux:
- Identify the regular-user UID range defined in /etc/login.defs.
$ awk '/^UID_MIN|^UID_MAX/ {print}' /etc/login.defs UID_MIN 1000 UID_MAX 60000 - List local accounts with UID in the regular-user range.
$ awk -F: '$3 >= 1000 && $3 <= 60000 {printf "%s:%s:%s:%s\n", $1, $3, $6, $7}' /etc/passwd ubuntu:1000:/home/ubuntu:/bin/bash user:1001:/home/user:/bin/bash backupuser:1002:/home/backupuser:/bin/bash audituser:1003:/home/audituser:/bin/bashReplace 1000 and 60000 with the values from the previous step when they differ.
- Review ownership and permissions for home directories under /home.
$ sudo ls -ld /home/* drwxr-x--- 2 audituser audituser 4096 Jan 13 00:44 /home/audituser drwxr-x--- 2 backupuser backupuser 4096 Jan 13 00:43 /home/backupuser drwxr-x--- 2 ubuntu ubuntu 4096 Oct 13 14:12 /home/ubuntu drwxr-x--- 2 user user 4096 Jan 13 00:45 /home/user
World-writable home directories can allow other local users to drop scripts, keys, or config files for persistence.
- Identify accounts outside the regular-user UID range that still have an interactive shell.
$ awk -F: '($3 < 1000 || $3 > 60000) && ($7 !~ /(nologin|false)$/) {printf "%s:%s:%s:%s\n", $1, $3, $6, $7}' /etc/passwd root:0:/root:/bin/bash sync:4:/bin:/bin/syncService accounts typically use /usr/sbin/nologin or /bin/false to block interactive logins.
- Identify all UID 0 accounts in /etc/passwd.
$ awk -F: '($3 == 0) {printf "%s:%s:%s\n", $1, $6, $7}' /etc/passwd root:/root:/bin/bashAny account other than root with UID 0 has full root privileges and should be treated as a high-severity finding.
- List accounts granted administrative access via the sudo group.
$ getent group sudo sudo:x:27:ubuntu,user
No output commonly indicates the sudo group is not used on this system.
- List accounts granted administrative access via the wheel group.
$ getent group wheel
wheel is commonly used on RHEL and CentOS.
- Search /etc/sudoers and /etc/sudoers.d for active sudo rules.
$ sudo grep --recursive --line-number --no-messages --extended-regexp '^[[:space:]]*[^#[:space:]]' /etc/sudoers /etc/sudoers.d 2>/dev/null /etc/sudoers:9:Defaults env_reset /etc/sudoers:10:Defaults mail_badpass /etc/sudoers:11:Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" /etc/sudoers:15:Defaults use_pty /etc/sudoers:47:root ALL=(ALL:ALL) ALL /etc/sudoers:50:%admin ALL=(ALL) ALL /etc/sudoers:53:%sudo ALL=(ALL:ALL) ALL /etc/sudoers:57:@includedir /etc/sudoers.d /etc/sudoers.d/90-user-nopasswd:1:user ALL=(ALL) NOPASSWD: ALL
Unexpected user entries or NOPASSWD rules can grant passwordless administrative access.
- Find accounts with an empty password field in /etc/shadow.
$ sudo awk -F: '($2 == "") {print $1}' /etc/shadowNo output indicates no empty password fields were found.
An empty password field can allow passwordless authentication depending on PAM configuration and login method.
- Review password status codes for accounts in /etc/shadow.
$ sudo passwd --status --all | head root L 2025-10-13 0 99999 7 -1 daemon L 2025-10-13 0 99999 7 -1 bin L 2025-10-13 0 99999 7 -1 sys L 2025-10-13 0 99999 7 -1 sync L 2025-10-13 0 99999 7 -1 games L 2025-10-13 0 99999 7 -1 man L 2025-10-13 0 99999 7 -1 lp L 2025-10-13 0 99999 7 -1 mail L 2025-10-13 0 99999 7 -1 news L 2025-10-13 0 99999 7 -1
Common status codes include P (password set), L (locked), and NP (no password).
- Review password aging and expiry policy for a specific account.
$ sudo chage --list --iso8601 user Last password change : 2026-01-13 Password expires : never Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 99999 Number of days of warning before password expires : 7
Replace user with the target account name.
- Review recent account logins using lastlog.
$ lastlog -u user Username Port From Latest user **Never logged in**
Accounts showing Never logged in can still be valid, but unexpected entries are a useful triage signal.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
