List and Compare Accounts¶
Every account is a line in /etc/passwd. The job is the same as on Windows: list them, compare to the README, and find the ones that don't belong.
List the people¶
awk -F: '$3 >= 1000 && $1 != "nobody" {print $1, $3, $7}' /etc/passwd
Each line is name UID shell. Accounts with UID 1000 and up are the human users; that's the list to compare with the README's authorized users. nobody (UID 65534) is a system account that happens to have a high UID.
Find hidden accounts¶
An attacker who adds an account often gives it a low UID so it hides among the system accounts, or gives it UID 0 so it is root under another name.
awk -F: '$3 == 0 {print $1}' /etc/passwd
Should print root and nothing else (CIS 5.3.2.1). A second UID 0 account is a second root: remove it (next page). The same check for group: awk -F: '$4 == 0 {print $1}' /etc/passwd prints only root (5.3.2.2), and awk -F: '$3 == 0 {print $1}' /etc/group prints only root (5.3.2.3).
awk -F: '$3 > 0 && $3 < 1000 && $7 !~ /(nologin|false|sync|halt|shutdown)$/ {print $1, $3, $7}' /etc/passwd
System accounts don't get a login shell (CIS 5.3.2.7). One that has /bin/bash or /bin/sh was made or modified by someone; the README won't list it. Either remove it or set its shell to /usr/sbin/nologin, depending on whether a service needs the account to exist. Any account that has no valid shell should also be locked (5.3.2.8): sudo usermod -L name.
Duplicates and consistency¶
The benchmark's account sanity checks (7.2.x) catch the tricks that hide in a hand-edited /etc/passwd:
cut -d: -f3 /etc/passwd | sort | uniq -d # duplicate UIDs (7.2.5)
cut -d: -f1 /etc/passwd | sort | uniq -d # duplicate names (7.2.7)
cut -d: -f3 /etc/group | sort | uniq -d # duplicate GIDs (7.2.6)
cut -d: -f1 /etc/group | sort | uniq -d # duplicate group names (7.2.8)
awk -F: '$2 != "x" {print $1}' /etc/passwd # password not shadowed (7.2.1)
for g in $(cut -d: -f4 /etc/passwd | sort -u); do getent group $g > /dev/null || echo "GID $g missing from /etc/group"; done # 7.2.3
getent group shadow | cut -d: -f4 # must be empty (7.2.4)
Two accounts sharing a UID are the same account with two names. A password hash sitting in /etc/passwd instead of x is world-readable. A member of the shadow group can read every hash.
Find who can use sudo¶
getent group sudo
getent group admin
getent group wheel
On Mint and Debian the sudo group is what makes an administrator. Compare its members to the README's administrator list. admin and wheel usually don't exist on these systems, so a member in one of them is worth a look. The Sudoers page covers the other way to grant sudo, which is a line in /etc/sudoers.
Check password state¶
sudo awk -F: '{ if ($2 == "") print $1, "BLANK"; else if ($2 ~ /^[!*]/) print $1, "locked"; else print $1, "set" }' /etc/shadow
Field 2 of /etc/shadow is the password hash. Empty means the account logs in with no password at all (CIS 7.2.2). ! or * at the start means locked or never set (normal for system accounts). Something starting with $ is a real hash. A human user shown as BLANK gets a password now; a human user shown as locked who is in the README gets unlocked (Passwords and Shells).
Verify¶
awk -F: '$3 >= 1000 && $1 != "nobody" {print $1}' /etc/passwd
awk -F: '$3 == 0 {print $1}' /etc/passwd
getent group sudo
The first matches the README's users, the second is root alone, the third matches the README's administrators.
Example¶
The README lists alice (administrator), bob, and carol. /etc/passwd shows alice, bob, carol, dave (UID 1004), sysbackup (UID 999, shell /bin/bash), and toor (UID 0). getent group sudo shows alice, bob. So: dave isn't authorized and goes; sysbackup is a fake system account with a shell and goes; toor is a second root and goes; bob comes out of the sudo group.
Try it¶
- Create a user with UID 0 (
useradd -o -u 0 toor) and one with UID 999 and a bash shell on a practice image, then find both with theawkcommands. Remove them. - Write a fake README with three users and compare it to
/etc/passwdby hand.
Build it¶
An accounts.sh that reads authorized.txt (one username per line) and prints local users not in it, listed users that don't exist, UID 0 accounts other than root, and sudo group members not marked admin. Report only.