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.

Steps to audit local user accounts with getent and passwd in Linux:

  1. 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
  2. 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/bash

    Replace 1000 and 60000 with the values from the previous step when they differ.

  3. 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.

  4. 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/sync

    Service accounts typically use /usr/sbin/nologin or /bin/false to block interactive logins.

  5. 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/bash

    Any account other than root with UID 0 has full root privileges and should be treated as a high-severity finding.

  6. 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.

  7. List accounts granted administrative access via the wheel group.
    $ getent group wheel

    wheel is commonly used on RHEL and CentOS.

  8. 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.

  9. Find accounts with an empty password field in /etc/shadow.
    $ sudo awk -F: '($2 == "") {print $1}' /etc/shadow

    No output indicates no empty password fields were found.

    An empty password field can allow passwordless authentication depending on PAM configuration and login method.

  10. 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).

  11. 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.

  12. 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.