1. Accounts & identity
Who exists on the system, and whether any of them should not.
-
No unauthorized accounts in
/etc/passwdVerifycut -d: -f1 /etc/passwd | sortGood: only the accounts on your approved roster.
-
No account other than
roothas UID 0Verifyawk -F: '($3==0){print $1}' /etc/passwdGood: prints only
root. Any other name is a backdoor. -
No account has an empty password
Verifysudo awk -F: '($2==""){print $1}' /etc/shadowGood: prints nothing.
-
The
sudogroup holds only intended adminsVerifygetent group sudoGood: only authorized administrators are listed.
-
Service / system accounts cannot log in
Verifyawk -F: '$3<1000{print $1": "$7}' /etc/passwdGood: system accounts use
/usr/sbin/nologinor/bin/false. -
Accounts map to their expected UID ranges
Verifyawk -F: '{print $3": "$1" ("$7")"}' /etc/passwd | sort -nGood: only
rootat UID 0; system accounts below 1000 usenologin/false; every UID 1000+ is a person who belongs on the box.
2. Password & PAM policy
Aging, strength, reuse, and lockout — the rules that make a credential expensive to guess.
-
Password maximum age is set
Verifygrep ^PASS_MAX_DAYS /etc/login.defsGood: a sane maximum such as
90— never99999. -
Minimum password length enforced
Verifygrep pam_unix /etc/pam.d/common-passwordGood: the line includes
minlen=10(or higher). -
Recent password reuse is blocked
Verifygrep pam_unix /etc/pam.d/common-passwordGood: the line includes
remember=5(or higher). -
Account lockout after failed logins
Verifygrep pam_faillock /etc/pam.d/common-authGood:
deny=5(or fewer) with anunlock_timeset. -
Existing users have aging applied
Verifysudo chage -l user1Good: "Maximum number of days" is a real value, not
99999.
3. sudo & privilege
The rules that decide who can become root, and whether any of them are too generous.
-
No
NOPASSWDgrants in sudoersVerifysudo grep -R NOPASSWD /etc/sudoers /etc/sudoers.dGood: nothing — or only a single, documented, justified entry.
-
sudoers syntax is valid
Verifysudo visudo -cGood: reports "parsed OK".
-
/etc/sudoers.ddrop-ins reviewedVerifysudo ls -la /etc/sudoers.dGood: only files you recognize; no stray rules.
4. Permissions & special bits
World-writable files and unexpected SUID/SGID binaries are classic footholds.
-
SUID / SGID binaries match a known baseline
Verifysudo find / -perm -4000 -type f 2>/dev/nullGood: only expected system binaries; nothing in home directories.
-
No unexpected world-writable files
Verifysudo find / -xdev -type f -perm -0002 2>/dev/nullGood: nothing outside scratch areas like
/tmp. -
World-writable directories keep the sticky bit
Verifysudo find / -xdev -type d -perm -0002 ! -perm -1000 2>/dev/nullGood: prints nothing.
-
/etc/shadowis not world-readableVerifysudo ls -l /etc/shadowGood:
-rw-r-----, owned byroot:shadow. -
File capabilities match a known baseline
Verifysudo getcap -r / 2>/dev/nullGood: only expected entries (e.g.
ping); no capability on a shell or on any file in a home directory.
5. Software & patching
Every installed package is attack surface; every unpatched one is an open door.
-
System is fully patched
Verifysudo apt update && apt list --upgradableGood: nothing left to upgrade.
-
No suspicious applications installed
Verifydpkg -l | grep -Ei 'nmap|zenmap|netcat|ncat|steghide|hydra|john|aircrack|telnetd'Good: only tools with a clear reason to be on this box; scanners, reverse-shell tools, and stego utilities are gone.
-
Automatic security updates enabled
Verifysystemctl is-enabled unattended-upgradesGood:
enabled. -
Installed package files are unmodified
Verifysudo debsums -cGood: no system binaries flagged (config files under
/etcmay differ and are expected). Install first if missing:sudo apt install debsums.
6. Services & startup
Fewer running services means fewer doors to try.
-
Only necessary services start at boot
Verifysystemctl list-unit-files --type=service --state=enabledGood: every enabled service can be justified.
-
No unexpected listening ports
Verifysudo ss -tlnpGood: every listener maps to a service you intend to run.
-
Unneeded network daemons disabled
Verifysystemctl status cups avahi-daemonGood: disabled unless the role actually needs them.
-
Init system is what's expected (PID 1 not substituted)
Verifyls -l /proc/1/exeGood: points at systemd (or your distro's expected init). A mismatch is worth investigating immediately.
-
Journal reviewed for boot and service errors
Verifyjournalctl -b -p errGood: no unexplained failures at the current boot.
-
No services masked or unmasked without a known reason and location
Verifysystemctl list-unit-files --type=service --state=masked systemctl status <name> # "Loaded:" line shows whether the mask is in /etc or /runGood: every masked entry can be justified, and you know whether it's a persistent admin mask (
/etc/), a temporary one (/run/, gone at reboot), or vendor-shipped. "Masked" alone isn't enough — locate it. -
Preset policy reviewed for security-relevant defaults
Verifyls /usr/lib/systemd/system-preset/*.preset /etc/systemd/system-preset/*.preset 2>/dev/nullGood: preset files reviewed at least once; nothing security-relevant is enabled by policy that you didn't intend. Never run
systemctl preset-allwithout diffing enabled units before and after — it silently resets manual hardening back to policy defaults.
7. Scheduled tasks
A job that recreates a backdoor on a timer is a favorite persistence trick.
-
System cron reviewed
Verifycat /etc/crontab; ls -la /etc/cron.d /etc/cron.*Good: no jobs you did not put there.
-
Per-user crontabs reviewed
Verifyfor u in $(cut -d: -f1 /etc/passwd); do echo "== $u"; sudo crontab -l -u "$u" 2>/dev/null; doneGood: nothing unexpected under any user.
-
systemd timers reviewed
Verifysystemctl list-timers --allGood: only legitimate timers — the modern equivalent of cron jobs.
8. Network & firewall
Exposure is the sum of what listens and what you let in.
-
Firewall active, default-deny inbound
Verifysudo ufw status verboseGood:
Status: active, defaultdeny (incoming). -
Only required ports are allowed
Verifysudo ufw status numberedGood: the allow-list matches exactly the services you run.
-
IP forwarding is off (unless a router)
Verifysysctl net.ipv4.ip_forwardGood:
= 0on a workstation. -
SYN-cookie protection is on
Verifysysctl net.ipv4.tcp_syncookiesGood:
= 1.
9. Remote access (SSH)
The front door from the network — confirm the daemon's effective settings, not just the file.
-
Root SSH login disabled
Verifysudo sshd -T | grep -i permitrootloginGood:
permitrootlogin no. -
Password auth disabled (keys only)
Verifysudo sshd -T | grep -i passwordauthenticationGood:
passwordauthentication no. -
Empty passwords rejected
Verifysudo sshd -T | grep -i permitemptypasswordsGood:
permitemptypasswords no. -
SSH access is allow-listed
Verifysudo sshd -T | grep -iE 'allowusers|allowgroups'Good: only named users or groups may connect.
10. Logging & audit
You cannot audit the past — turn recording on before you need it.
-
Audit daemon installed and enabled
Verifysystemctl is-enabled auditdGood:
enabled(installauditdif it is missing). -
Logs are being recorded
Verifyjournalctl -p err -b | tailGood: the journal is populated and readable.