Unit 4: System Hardening — Reducing Attack Surfaces

Introduction

Every computing system, from a single workstation to a sprawling enterprise network, begins its life in a default state that prioritises compatibility and ease-of-use over security. Manufacturers and vendors ship operating systems with numerous services running, ports open, accounts active, and features enabled — most of which the average deployment will never require. Each of these unnecessary components is a potential foothold for an attacker.

System hardening is the disciplined process of eliminating that excess: disabling unneeded services, enforcing strong authentication, applying security patches, restricting permissions, configuring audit logging, and tuning the operating system itself to resist exploitation. The goal is to shrink the attack surface — the sum of all points where an unauthorised user could attempt to interact with or compromise the system — to the smallest practical footprint.

This chapter covers the principles, methodologies, and practical techniques of system hardening across common platforms. By the end you will be able to assess a system’s current posture, apply baseline hardening controls, verify your work, and understand how hardening fits into the broader security lifecycle.

Note

All command examples in this chapter target Linux (Ubuntu/RHEL family) and Windows Server 2019/2022 unless otherwise stated. Equivalent concepts apply to macOS, BSD, and cloud-native environments.


Core Concepts

The Attack Surface

The attack surface of a system comprises every avenue through which a threat actor might gain unauthorised access, escalate privilege, or cause damage. It can be categorised into three dimensions:

Attack Surface Dimensions

Dimension

Examples

Hardening Response

Network

Open TCP/UDP ports, running services, network protocols

Firewall rules, disabling services, protocol restrictions

Software

Installed applications, libraries, OS components, unpatched CVEs

Patch management, software removal, application allow-listing

Human / Configuration

Default credentials, excessive permissions, misconfigured policies

Password policies, least privilege, configuration baselines

The Principle of Least Privilege

The single most important principle in system hardening is least privilege: every user, process, and service should operate with the minimum set of permissions required to perform its legitimate function — nothing more.

Violations of least privilege compound over time. A service account granted local administrator rights for convenience, a developer left in the Domain Admins group after a project ends, a web server process that can write to /etc — each represents an opportunity for an attacker who compromises that identity to escalate impact dramatically.

Example — Over-Privileged Web Application

A web application runs as root on a Linux server. An attacker exploits a Remote Code Execution (RCE) vulnerability in a third-party library. Because the process runs as root they immediately have full system control: they can read /etc/shadow, install a rootkit, pivot to other hosts, and exfiltrate data.

Had the application run as a dedicated low-privilege account (e.g., www-data) confined to /var/www, the attacker’s foothold would be severely restricted. They could not read sensitive system files, could not write to system directories, and privilege escalation would require an additional exploit.

Defence in Depth

No single control is infallible. Hardening is one layer in a defence-in- depth strategy: a security architecture where multiple overlapping controls ensure that the failure of any one layer does not result in total compromise.

┌─────────────────────────────────────────────────┐
│                   Perimeter                     │  ← Firewalls, IDS/IPS
│  ┌───────────────────────────────────────────┐  │
│  │              Network Segment               │  │  ← VLANs, ACLs
│  │  ┌─────────────────────────────────────┐  │  │
│  │  │           Host Hardening             │  │  │  ← This chapter
│  │  │  ┌───────────────────────────────┐  │  │  │
│  │  │  │      Application Security     │  │  │  │  ← Input validation, WAF
│  │  │  │  ┌───────────────────────┐   │  │  │  │
│  │  │  │  │      Data Layer        │  │  │  │  │  ← Encryption, DLP
│  │  │  │  └───────────────────────┘  │  │  │  │
│  │  │  └───────────────────────────────┘  │  │  │
│  │  └─────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

Hardening Benchmarks and Standards

Rather than reinventing controls for every deployment, practitioners leverage established hardening benchmarks:

CIS Benchmarks

The Center for Internet Security publishes detailed, community-reviewed configuration guides for virtually every major OS, application, and cloud platform. Each benchmark item is categorised as Level 1 (essential, minimal operational impact) or Level 2 (defence-in-depth, may impact usability).

DISA STIGs

The Defense Information Systems Agency Security Technical Implementation Guides are mandatory within the US Department of Defense and widely adopted in government and regulated industries. STIGs are more prescriptive and generally stricter than CIS benchmarks.

NIST SP 800-123

NIST’s Guide to General Server Security provides vendor-neutral hardening principles applicable to any platform.

Vendor Security Guides

Microsoft Secure Baselines (via Microsoft Security Compliance Toolkit), Red Hat Security Guide, and Apple Platform Security are platform-specific references that complement the above.


Patch Management

Why Patching Is Hardening

Unpatched vulnerabilities are the leading cause of successful breaches. Applying security patches is arguably the single highest-ROI hardening activity because it directly eliminates known, exploited weaknesses rather than merely raising the bar for attackers.

Patch management encompasses:

  1. Inventory — knowing what software and versions are installed

  2. Monitoring — tracking vendor advisories and CVE databases

  3. Testing — validating patches in a non-production environment

  4. Deployment — applying patches within defined SLA windows

  5. Verification — confirming patches are applied and systems are compliant

Patch SLA Tiers

Risk-based patch SLAs allow organisations to prioritise effort proportional to severity:

Common Patch SLA Framework

CVSS Score

Severity

Target Remediation

Notes

9.0 – 10.0

Critical

24 – 72 hours

Particularly if public exploit exists (CISA KEV)

7.0 – 8.9

High

7 – 14 days

Expedite if actively exploited

4.0 – 6.9

Medium

30 days

Standard patch cycle

0.1 – 3.9

Low

90 days / next major cycle

Bundle with routine maintenance

Linux Patch Management

# Ubuntu / Debian — update package lists and apply security patches
sudo apt update
sudo apt upgrade --only-upgrade   # upgrade all installed packages
sudo apt-get dist-upgrade          # also handles dependency changes

# Apply ONLY security updates (Ubuntu)
sudo unattended-upgrades --dry-run   # preview
sudo unattended-upgrades             # apply

# RHEL / CentOS Stream / Rocky Linux
sudo dnf check-update
sudo dnf update --security           # security updates only
sudo dnf update                      # all updates

# Check which packages have available security updates
sudo dnf updateinfo list security

# Identify packages with known CVEs (requires subscription on RHEL)
sudo dnf updateinfo list cves

Configuring Automatic Security Updates (Ubuntu)

# Install unattended-upgrades
sudo apt install unattended-upgrades

# Edit /etc/apt/apt.conf.d/50unattended-upgrades
# Ensure this origin is uncommented:
#   "${distro_id}:${distro_codename}-security";

# Configure update schedule in /etc/apt/apt.conf.d/20auto-upgrades:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";

Windows Patch Management

# Check for missing updates via PSWindowsUpdate module
Install-Module PSWindowsUpdate -Force
Get-WindowsUpdate

# Install all available security updates
Install-WindowsUpdate -Category "Security Updates" -AcceptAll -AutoReboot

# WSUS integration — set update server via registry
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" `
    -Name "UseWUServer" -Value 1

# Query installed hotfixes
Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 20

# Check for updates using Windows Update API (no module required)
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$SearchResult = $UpdateSearcher.Search("IsInstalled=0 and Type='Software'")
$SearchResult.Updates | Select-Object Title, MsrcSeverity

Warning

Always test patches in a representative staging environment before deploying to production. While rare, patches can introduce regressions. Maintain a documented rollback procedure for critical systems.


Service and Port Management

Identifying Running Services

The first step in service hardening is a complete inventory of what is running and why. Every service listening on a network port is a potential attack vector.

# Linux — list all listening ports and associated processes
sudo ss -tlnp          # TCP listening, numeric, show process
sudo ss -ulnp          # UDP listening
sudo netstat -tlnp     # alternative (deprecated but widely available)

# List all running services (systemd)
systemctl list-units --type=service --state=running

# List ALL installed services (running and stopped)
systemctl list-unit-files --type=service

# Check if a specific service is enabled at boot
systemctl is-enabled bluetooth
# Windows — list listening ports with process IDs
netstat -ano | Where-Object { $_ -match "LISTENING" }

# Map ports to process names
Get-NetTCPConnection -State Listen | Select-Object LocalAddress, LocalPort,
    @{Name="Process";Expression={(Get-Process -Id $_.OwningProcess).Name}}

# List all services
Get-Service | Sort-Object Status -Descending

# List services set to start automatically
Get-Service | Where-Object { $_.StartType -eq "Automatic" }

Disabling Unnecessary Services

Once you have an inventory, disable everything not required by the system’s documented purpose. Apply the principle: if in doubt, disable.

Common Services to Evaluate for Disabling

Service

Platform

Risk if Unnecessary

Telnet

Linux/Windows

Plaintext credentials, deprecated by SSH

FTP (vsftpd, IIS FTP)

Linux/Windows

Plaintext credentials; use SFTP/FTPS

SNMP v1/v2

Both

Community string brute-force, information disclosure

Remote Registry

Windows

Remote registry access by attackers

Print Spooler

Windows

PrintNightmare (CVE-2021-1675) and related exploits

Bluetooth

Both

BLUEBORNE and proximity attacks on servers

NFS (without auth)

Linux

Unauthenticated file system access

X11 forwarding

Linux

GUI exposure; rarely needed on servers

Avahi/mDNS

Linux

Network discovery; unnecessary on servers

# Linux — disable and stop a service immediately
sudo systemctl stop bluetooth
sudo systemctl disable bluetooth
sudo systemctl mask bluetooth     # prevent re-enabling even by other services

# Verify it will not start at boot
sudo systemctl is-enabled bluetooth    # should output "masked"

# Remove a package entirely (Ubuntu example)
sudo apt purge telnet xinetd rsh-server nis yp-tools
sudo apt autoremove
# Windows — disable a service
Set-Service -Name "Spooler" -StartupType Disabled
Stop-Service -Name "Spooler" -Force

# Disable via sc.exe (works in older environments)
sc.exe config "RemoteRegistry" start= disabled
sc.exe stop "RemoteRegistry"

# Disable multiple unnecessary services (example hardening script)
$servicesToDisable = @(
    "Fax", "XblAuthManager", "XblGameSave",
    "XboxGipSvc", "XboxNetApiSvc", "RemoteRegistry",
    "SharedAccess", "SNMP"
)
foreach ($svc in $servicesToDisable) {
    if (Get-Service -Name $svc -ErrorAction SilentlyContinue) {
        Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
        Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
        Write-Host "Disabled: $svc"
    }
}

Host-Based Firewall Configuration

Even after disabling services, a host-based firewall provides an independent layer of protection, blocking inbound connections to any port regardless of what is listening.

# UFW (Uncomplicated Firewall) — Ubuntu
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment "SSH"
sudo ufw allow 443/tcp comment "HTTPS"
sudo ufw enable
sudo ufw status verbose

# firewalld — RHEL/CentOS/Rocky
sudo firewall-cmd --set-default-zone=drop
sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

# iptables — direct (lower-level)
# Flush existing rules
sudo iptables -F
sudo iptables -X
# Default deny
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Allow established connections
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Allow SSH
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Save rules
sudo iptables-save | sudo tee /etc/iptables/rules.v4
# Windows Defender Firewall — enable all profiles
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True

# Set default inbound action to block
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Block

# Allow RDP only from management subnet
New-NetFirewallRule -DisplayName "Allow RDP from Mgmt" `
    -Direction Inbound -Protocol TCP -LocalPort 3389 `
    -RemoteAddress "10.10.0.0/24" -Action Allow

# Block a specific port
New-NetFirewallRule -DisplayName "Block Telnet" `
    -Direction Inbound -Protocol TCP -LocalPort 23 -Action Block

# Export current firewall rules for documentation
Get-NetFirewallRule | Where-Object { $_.Enabled -eq "True" } |
    Select-Object DisplayName, Direction, Action, Profile |
    Export-Csv -Path "firewall_rules_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation

User Account and Authentication Hardening

Default Account Elimination

Default accounts — particularly those with well-known credentials — are among the most exploited weaknesses in deployed systems. The CISA Known Exploited Vulnerabilities (KEV) catalogue is littered with entries that trace back to unchanged default passwords.

# Linux — disable root login (use sudo instead)
sudo passwd -l root               # lock the root account
sudo usermod -s /sbin/nologin root # set shell to nologin

# List all accounts with UID 0 (should only be root, or empty if root locked)
awk -F: '($3 == "0") { print $1 }' /etc/passwd

# Find accounts with no password set
sudo awk -F: '($2 == "" ) { print $1 }' /etc/shadow

# Lock inactive accounts
sudo passwd -l olduser
sudo usermod --expiredate 1 olduser   # expire immediately

# List users who can log in (have a valid shell)
grep -vE '(/nologin|/false)$' /etc/passwd
# Windows — rename the built-in Administrator account
Rename-LocalUser -Name "Administrator" -NewName "SvcAdmin_Renamed"

# Disable the built-in Guest account
Disable-LocalUser -Name "Guest"

# List all local users and their status
Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet

# Disable stale accounts (not logged in for 90 days)
$cutoff = (Get-Date).AddDays(-90)
Get-LocalUser | Where-Object {
    $_.LastLogon -lt $cutoff -and $_.Enabled -eq $true
} | Disable-LocalUser

Password Policies

Strong password policies reduce the effectiveness of credential-based attacks, including brute force, password spraying, and credential stuffing.

Recommended Minimum Password Policy (NIST SP 800-63B aligned)

  • Minimum length: 12 characters (15+ for privileged accounts)

  • Complexity: Not required if length ≥ 15 (length is more valuable)

  • History: Prevent reuse of last 12 passwords

  • Maximum age: 365 days for standard accounts; consider no expiry with MFA

  • Lockout: 5–10 failed attempts triggers 15–30 minute lockout or admin unlock

  • Breached password check: Reject passwords found in known breach databases

# Linux — configure PAM password quality (Ubuntu/Debian)
sudo apt install libpam-pwquality

# Edit /etc/security/pwquality.conf
minlen = 14
minclass = 3
maxrepeat = 3
maxsequence = 4
gecoscheck = 1
dictcheck = 1

# Configure account lockout via pam_faillock
# Edit /etc/pam.d/common-auth — add before pam_unix:
auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900

# Configure password aging via /etc/login.defs
sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS   365/' /etc/login.defs
sudo sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS   1/'   /etc/login.defs
sudo sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE   14/'  /etc/login.defs

# Apply aging to existing accounts
sudo chage -M 365 -m 1 -W 14 username
# Windows — configure local password policy
# (Use Group Policy for domain environments)

# Set minimum password length to 14
net accounts /minpwlen:14

# Set password history to 24 passwords
net accounts /uniquepw:24

# Set maximum password age to 365 days
net accounts /maxpwage:365

# Set lockout threshold to 5 attempts, lockout duration 30 min
net accounts /lockoutthreshold:5 /lockoutduration:30 /lockoutwindow:30

# View current policy
net accounts

Multi-Factor Authentication (MFA)

Even strong passwords can be stolen via phishing, keyloggers, or database breaches. MFA requires attackers to compromise a second factor — significantly raising the cost of account takeover.

Linux — Google Authenticator PAM (TOTP-based MFA for SSH)

# Install
sudo apt install libpam-google-authenticator

# Configure for each user (run as the target user)
google-authenticator

# Edit /etc/pam.d/sshd — add at the top:
auth required pam_google_authenticator.so

# Edit /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive

sudo systemctl restart sshd

SSH Hardening

SSH is the primary remote administration protocol for Linux systems. Its default configuration is functional but not secure.

# /etc/ssh/sshd_config — recommended hardening settings

# Disable root login over SSH
PermitRootLogin no

# Require SSH key authentication; disable password auth
PasswordAuthentication no
PubkeyAuthentication yes

# Disable legacy protocol and weak ciphers
Protocol 2
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org

# Restrict access to specific users or groups
AllowUsers alice bob
# AllowGroups sshusers

# Set idle timeout (seconds) — disconnect idle sessions after 15 minutes
ClientAliveInterval 300
ClientAliveCountMax 3

# Disable X11 and agent forwarding unless explicitly needed
X11Forwarding no
AllowAgentForwarding no

# Change default port to reduce automated scanning (security through obscurity)
# Port 2222    # Optional; note this is not a substitute for proper controls

# Enable strict mode
StrictModes yes

# Limit authentication attempts
MaxAuthTries 3
MaxSessions 4

# Log level
LogLevel VERBOSE

# Apply and verify
sudo sshd -t && sudo systemctl restart sshd

Filesystem and Permission Hardening

File Permission Principles

Improper file permissions are a common pathway for privilege escalation. An attacker who compromises a low-privilege account can use world-writable files, SUID/SGID binaries, and misconfigured directories to move toward root.

Linux Permission Reference

Permission Bits:
  r (4) = read
  w (2) = write
  x (1) = execute

Example: -rwxr-x--- = owner:rwx, group:r-x, others:---

Special bits:
  SUID (4000): Execute as file owner (e.g., passwd, sudo)
  SGID (2000): Execute as file group
  Sticky (1000): Only owner can delete files in directory (e.g., /tmp)
# Find world-writable files (potential risk)
sudo find / -xdev -perm -002 -type f 2>/dev/null | grep -v /proc

# Find world-writable directories
sudo find / -xdev -perm -002 -type d 2>/dev/null

# Find SUID/SGID binaries (audit these carefully)
sudo find / -xdev \( -perm -4000 -o -perm -2000 \) -type f 2>/dev/null

# Find files with no owner (orphaned — potential indicator of compromise)
sudo find / -xdev \( -nouser -o -nogroup \) -type f 2>/dev/null

# Correct permissions on sensitive files
sudo chmod 600 /etc/shadow        # readable only by root
sudo chmod 644 /etc/passwd        # readable by all, writable only by root
sudo chmod 600 /etc/ssh/sshd_config
sudo chmod 700 /root              # root home directory

# Remove SUID from unnecessary binaries
sudo chmod u-s /usr/bin/at        # if the 'at' scheduler is not needed

# Set immutable flag on critical config files
sudo chattr +i /etc/passwd /etc/shadow /etc/group

Filesystem Mount Options

Linux mount options can restrict what is permitted within specific filesystems, adding defence-in-depth even if an attacker gains write access.

# /etc/fstab hardening examples
#
# noexec  — prevent execution of binaries on this filesystem
# nosuid  — ignore SUID/SGID bits
# nodev   — prevent creation/use of device files
#
# Apply to /tmp (writable by all users)
tmpfs   /tmp         tmpfs   defaults,noexec,nosuid,nodev,size=2G  0 0
#
# Apply to /var/tmp
/var/tmp  none  bind  defaults,noexec,nosuid,nodev,bind  0 0
#
# Apply to removable media
/dev/sdb1  /media/usb  vfat  defaults,noexec,nosuid,nodev,noauto,user  0 0

# Remount /tmp with restrictions immediately (without rebooting)
sudo mount -o remount,noexec,nosuid,nodev /tmp

NTFS Permissions and Windows Hardening

On Windows, the NTFS permission model provides granular access control. Hardening focuses on removing excessive permissions from the filesystem, particularly on system directories.

# List NTFS permissions on a directory
Get-Acl -Path "C:\inetpub\wwwroot" | Format-List

# Restrict write access on a sensitive directory
$acl = Get-Acl -Path "C:\Windows\System32\drivers\etc"
# Remove all non-inherited access for Users group
$acl.Access | Where-Object { $_.IdentityReference -match "Users" } |
    ForEach-Object { $acl.RemoveAccessRule($_) }
Set-Acl -Path "C:\Windows\System32\drivers\etc" -AclObject $acl

# Audit access to sensitive files (enable auditing on object)
$auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
    "Everyone",
    "FullControl",
    "ContainerInherit,ObjectInherit",
    "None",
    "Success,Failure"
)
$acl = Get-Acl "C:\SensitiveData"
$acl.SetAuditRule($auditRule)
Set-Acl -Path "C:\SensitiveData" -AclObject $acl

Operating System Kernel and Core Hardening

Linux Kernel Parameters (sysctl)

The Linux kernel exposes hundreds of tunable parameters via /proc/sys, configurable persistently through /etc/sysctl.conf or /etc/sysctl.d/*.conf. Many of these directly affect security.

# /etc/sysctl.d/99-hardening.conf

# ─── Network Hardening ───────────────────────────────────────────────────
# Disable IP forwarding (unless this host is a router)
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Disable packet acceptance from hosts not in routing table (source routing)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Ignore ICMP redirects (prevents MITM via fake routing updates)
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Enable reverse path filtering (drop packets with impossible source IPs)
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5

# Ignore broadcast ICMP (Smurf attack prevention)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Log martian packets (packets with impossible source addresses)
net.ipv4.conf.all.log_martians = 1

# ─── Memory and Execution Hardening ─────────────────────────────────────
# Enable ASLR (Address Space Layout Randomisation) — full randomisation
kernel.randomize_va_space = 2

# Restrict kernel pointer exposure in /proc (prevents info leaks)
kernel.kptr_restrict = 2

# Restrict access to kernel logs to root only
kernel.dmesg_restrict = 1

# Restrict ptrace (prevent process inspection by other processes)
kernel.yama.ptrace_scope = 1

# Disable magic SysRq key (can be used to bypass security controls)
kernel.sysrq = 0

# ─── File System Hardening ───────────────────────────────────────────────
# Prevent hardlinks to files you don't own
fs.protected_hardlinks = 1
fs.protected_symlinks = 1

# Prevent core dumps from SUID processes
fs.suid_dumpable = 0

# Apply all settings immediately
sudo sysctl -p /etc/sysctl.d/99-hardening.conf

Mandatory Access Control (MAC)

Traditional Linux uses Discretionary Access Control (DAC): owners set permissions, and root overrides all. MAC frameworks enforce security policies even on processes running as root, confining them to only what the policy explicitly permits.

SELinux (Security-Enhanced Linux — RHEL, Fedora, Rocky)

# Check SELinux status
sestatus
getenforce

# Set to enforcing mode (production target)
sudo setenforce 1                                      # immediate
sudo sed -i 's/SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config  # persistent

# View denial logs
sudo ausearch -m avc -ts recent | audit2why

# Generate a policy module for a legitimate application denial
sudo ausearch -m avc -ts recent | audit2allow -M myapp
sudo semodule -i myapp.pp

# List contexts for a file
ls -Z /var/www/html

# Restore default context on a file
sudo restorecon -Rv /var/www/html

AppArmor (Ubuntu, Debian, SUSE)

# Check AppArmor status
sudo aa-status

# Set a profile to enforce mode
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

# Set a profile to complain mode (log but don't block — useful for testing)
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx

# View AppArmor denials
sudo dmesg | grep "apparmor=\"DENIED\""

# Generate a profile for a new application
sudo aa-genprof /usr/bin/myapp

Windows Security Policies

Group Policy (or Local Security Policy on standalone systems) is the primary mechanism for enforcing OS-level security controls on Windows.

# Audit Policy — enable auditing of key security events
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Account Lockout" /failure:enable
auditpol /set /subcategory:"Privilege Use" /success:enable /failure:enable
auditpol /set /subcategory:"Process Creation" /success:enable
auditpol /set /subcategory:"Object Access" /success:enable /failure:enable

# Verify audit policy
auditpol /get /category:*

# Disable LLMNR (Link-Local Multicast Name Resolution — exploited by Responder)
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" `
    -Name "EnableMulticast" -Value 0

# Disable NetBIOS over TCP/IP
$adapters = Get-WmiObject Win32_NetworkAdapterConfiguration
foreach ($adapter in $adapters) {
    $adapter.SetTcpipNetbios(2)  # 2 = Disable NetBIOS over TCP/IP
}

# Enable Windows Defender Credential Guard (requires UEFI + Virtualisation)
# This protects LSASS memory from credential dumping tools like Mimikatz
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
    -Name "EnableVirtualizationBasedSecurity" -Value 1
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
    -Name "LsaCfgFlags" -Value 1   # 1 = Credential Guard without UEFI lock

# Disable SMBv1 (exploited by EternalBlue/WannaCry)
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol

# Require SMB signing (prevents relay attacks)
Set-SmbServerConfiguration -RequireSecuritySignature $true -Force
Set-SmbClientConfiguration -RequireSecuritySignature $true -Force

Logging, Auditing, and Monitoring

Why Logging Is a Hardening Control

Hardening reduces the probability of compromise; logging and monitoring reduce the dwell time — the interval between initial compromise and detection. Studies consistently show that the median attacker dwell time before detection is measured in days to weeks. Comprehensive, centralised logging is the foundation of any detection capability.

Security-relevant log sources include:

  • Authentication events (successful and failed logins)

  • Privilege use and escalation

  • Process creation and termination

  • Network connection establishment

  • File access on sensitive paths

  • Configuration changes

  • Service start/stop

Linux Logging Configuration

# Install and configure auditd (Linux Audit Framework)
sudo apt install auditd audispd-plugins   # Ubuntu
sudo dnf install audit                    # RHEL

# /etc/audit/rules.d/hardening.rules

# Delete all existing rules
-D

# Set buffer size
-b 8192

# Failure mode: 1=log, 2=panic
-f 1

# Monitor authentication files
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudoers

# Monitor login/logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock -p wa -k logins

# Monitor privileged command execution
-a always,exit -F arch=b64 -S execve -F euid=0 -k privileged

# Monitor network configuration changes
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/network -p wa -k system-locale

# Monitor systemd service changes
-w /bin/systemctl -p x -k systemd
-w /etc/systemd -p wa -k systemd

# Monitor cron
-w /etc/cron.d -p wa -k cron
-w /etc/cron.daily -p wa -k cron
-w /var/spool/cron -p wa -k cron

# Monitor kernel module loading (rootkit indicator)
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules

# Apply rules
sudo augenrules --load
sudo systemctl restart auditd

# Query audit log
sudo ausearch -k identity -ts today
sudo ausearch -k privileged -i | tail -50

Configuring rsyslog for Centralised Logging

# Forward all logs to a central syslog server
# /etc/rsyslog.d/50-forward.conf

# TCP forwarding (reliable, encrypted with TLS)
*.* @@siem.example.com:514

# Alternatively, forward via TCP with TLS (recommended):
$DefaultNetstreamDriver gtls
$DefaultNetstreamDriverCAFile /etc/ssl/certs/siem-ca.pem
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer siem.example.com
*.* @@(o)siem.example.com:6514

sudo systemctl restart rsyslog

Windows Event Logging

# Increase Windows Event Log sizes (recommended minimums)
Limit-EventLog -LogName Security -MaximumSize 1GB
Limit-EventLog -LogName System   -MaximumSize 512MB
Limit-EventLog -LogName Application -MaximumSize 512MB

# Key Windows Security Event IDs to monitor:
# 4624 - Successful logon
# 4625 - Failed logon (brute force indicator)
# 4648 - Logon with explicit credentials (pass-the-hash indicator)
# 4672 - Special privileges assigned (admin logon)
# 4720 - User account created
# 4726 - User account deleted
# 4732 - Member added to security-enabled local group
# 4740 - Account locked out
# 4771 - Kerberos pre-authentication failed
# 7045 - New service installed (malware persistence indicator)
# 4698 - Scheduled task created

# Query for failed logon events in the last 24 hours
Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    Id      = 4625
    StartTime = (Get-Date).AddHours(-24)
} | Select-Object TimeCreated,
    @{N="Account";E={$_.Properties[5].Value}},
    @{N="Source IP";E={$_.Properties[19].Value}} |
Format-Table -AutoSize

# Enable PowerShell Script Block Logging (detects malicious PS scripts)
$PSLogging = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $PSLogging -Force
Set-ItemProperty -Path $PSLogging -Name "EnableScriptBlockLogging" -Value 1
Set-ItemProperty -Path $PSLogging -Name "EnableScriptBlockInvocationLogging" -Value 1

Application Hardening

Remove Unnecessary Software

Every installed application is a potential attack vector. Applications that are not in use should be removed, not merely disabled, to eliminate their vulnerability footprint entirely.

# Ubuntu — list explicitly installed packages
apt list --installed 2>/dev/null | wc -l

# Remove packages that are rarely needed on servers
sudo apt purge telnet ftp rsh-client rsh-redone-client \
               talk ntalk nfs-common nis at cups-* avahi-daemon \
               postfix sendmail xinetd

# Remove orphaned dependencies
sudo apt autoremove --purge
# Windows — list installed applications
Get-WmiObject Win32_Product | Select-Object Name, Version |
    Sort-Object Name | Format-Table -AutoSize

# Uninstall via WMIC
$app = Get-WmiObject Win32_Product | Where-Object { $_.Name -like "*Telnet*" }
$app.Uninstall()

# Disable Windows features that are not required
$features = @(
    "TelnetClient", "TFTP", "SMB1Protocol",
    "MicrosoftWindowsPowerShellV2Root",
    "WindowsMediaPlayer"
)
foreach ($f in $features) {
    Disable-WindowsOptionalFeature -Online -FeatureName $f -NoRestart
}

Web Server Hardening (nginx example)

Web servers require specific hardening beyond the OS baseline because they are directly exposed to untrusted input from the internet.

# /etc/nginx/nginx.conf — security-focused configuration

# Hide nginx version information
server_tokens off;

# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;

# Enable XSS filter in older browsers
add_header X-XSS-Protection "1; mode=block" always;

# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;

# Enforce HTTPS (only add after confirming HTTPS is working)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'" always;

# Restrict HTTP methods
if ($request_method !~ ^(GET|HEAD|POST)$) {
    return 405;
}

# Disable directory listing
autoindex off;

# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=20 nodelay;

# TLS hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_stapling on;
ssl_stapling_verify on;

Database Hardening (PostgreSQL example)

-- Remove default and test databases
DROP DATABASE IF EXISTS test;

-- Create dedicated application user with minimal privileges
CREATE USER appuser WITH PASSWORD 'use_a_strong_generated_password';
GRANT CONNECT ON DATABASE appdb TO appuser;
GRANT USAGE ON SCHEMA public TO appuser;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;

-- Revoke public schema creation from PUBLIC role
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON DATABASE appdb FROM PUBLIC;

-- Enable SSL connections only
-- In postgresql.conf:
-- ssl = on
-- ssl_cert_file = 'server.crt'
-- ssl_key_file  = 'server.key'

-- Log all connection attempts
-- In postgresql.conf:
-- log_connections = on
-- log_disconnections = on
-- log_failed_authentications = on
# pg_hba.conf — restrict connection methods
# Only allow application user from application server subnet, SSL required
# TYPE  DATABASE  USER     ADDRESS          METHOD
local   all       postgres                  peer
host    appdb     appuser  10.0.1.0/24      scram-sha-256
# Reject everything else
host    all       all      0.0.0.0/0        reject

Vulnerability Scanning and Compliance Verification

After applying hardening controls, verification is essential. Manual review does not scale; automated scanning tools are required.

OpenSCAP (Linux)

SCAP (Security Content Automation Protocol) enables automated compliance assessment against CIS and STIG benchmarks.

# Install OpenSCAP
sudo apt install libopenscap8 ssg-base ssg-debderived   # Ubuntu
sudo dnf install openscap-scanner scap-security-guide   # RHEL

# List available profiles
oscap info /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml

# Run CIS Level 1 assessment and generate HTML report
sudo oscap xccdf eval \
    --profile xccdf_org.ssgproject.content_profile_cis_level1_server \
    --results scan-results.xml \
    --report scan-report.html \
    /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml

# View overall score
grep "score" scan-results.xml | head -5

# Remediate failed controls automatically (use with caution in production)
sudo oscap xccdf eval \
    --profile xccdf_org.ssgproject.content_profile_cis_level1_server \
    --remediate \
    /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml

Lynis (Cross-Platform Audit Tool)

Lynis is a lightweight, agentless security audit tool that does not require a specific profile — it performs comprehensive checks and provides recommendations.

# Install
sudo apt install lynis   # or download from cisofy.com

# Run a full system audit
sudo lynis audit system

# Run and save results to a log file
sudo lynis audit system --logfile /var/log/lynis.log \
    --report-file /var/log/lynis-report.dat

# View hardening index score
grep "hardening_index" /var/log/lynis-report.dat

# Focus on a specific category
sudo lynis audit system --tests-category "authentication"

# Non-interactive mode for CI/CD integration
sudo lynis audit system --quick --quiet --no-colors

Microsoft Security Compliance Toolkit (Windows)

# Download and use the Policy Analyser to compare current settings
# to Microsoft Security Baselines

# Example: Check if key baseline settings are applied
function Test-SecurityBaseline {
    $results = @()

    # Check SMBv1
    $smb1 = (Get-SmbServerConfiguration).EnableSMB1Protocol
    $results += [PSCustomObject]@{
        Control = "SMBv1 Disabled"
        Status  = if (-not $smb1) { "PASS" } else { "FAIL" }
    }

    # Check Windows Firewall
    $fw = Get-NetFirewallProfile | Where-Object { $_.Enabled -eq $false }
    $results += [PSCustomObject]@{
        Control = "All Firewall Profiles Enabled"
        Status  = if (-not $fw) { "PASS" } else { "FAIL" }
    }

    # Check Credential Guard
    $cg = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
             -Name "LsaCfgFlags" -ErrorAction SilentlyContinue).LsaCfgFlags
    $results += [PSCustomObject]@{
        Control = "Credential Guard Enabled"
        Status  = if ($cg -ge 1) { "PASS" } else { "FAIL" }
    }

    $results | Format-Table -AutoSize
}

Test-SecurityBaseline

Hardening in the Software Development Lifecycle

Hardening is not only an operational concern; it must be embedded throughout the SDLC to avoid technical debt accumulating in deployed systems.

Infrastructure as Code (IaC) Hardening

When infrastructure is provisioned via IaC (Terraform, Ansible, CloudFormation), security controls should be expressed in code and version-controlled.

# Ansible playbook excerpt — applying hardening to a new Ubuntu host

---
- name: System Hardening Baseline
  hosts: all
  become: yes

  tasks:

    - name: Ensure SSH PermitRootLogin is disabled
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin no'
        validate: 'sshd -t -f %s'
      notify: Restart sshd

    - name: Disable unnecessary services
      systemd:
        name: "{{ item }}"
        state: stopped
        enabled: no
        masked: yes
      loop:
        - bluetooth
        - avahi-daemon
        - cups
      ignore_errors: yes

    - name: Apply sysctl hardening parameters
      sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        state: present
        reload: yes
      loop:
        - { key: "kernel.randomize_va_space", value: "2" }
        - { key: "net.ipv4.tcp_syncookies",   value: "1" }
        - { key: "net.ipv4.conf.all.rp_filter", value: "1" }
        - { key: "fs.protected_hardlinks",     value: "1" }
        - { key: "kernel.kptr_restrict",       value: "2" }

    - name: Set password hashing rounds in /etc/login.defs
      lineinfile:
        path: /etc/login.defs
        regexp: '^#?SHA_CRYPT_MIN_ROUNDS'
        line: 'SHA_CRYPT_MIN_ROUNDS 65536'

  handlers:
    - name: Restart sshd
      systemd:
        name: sshd
        state: restarted

Container Hardening

Containers present a distinct attack surface. While they share the host kernel (unlike VMs), they can be hardened to reduce the blast radius of a container escape or application compromise.

# Dockerfile hardening best practices

# Use a minimal base image (distroless or alpine)
FROM gcr.io/distroless/base-debian12

# Run as a non-root user
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser

# Copy only what is needed (avoid COPY . .)
COPY --chown=appuser:appgroup ./app /app

# Use WORKDIR (never use / as working directory)
WORKDIR /app

# Don't expose unnecessary ports
EXPOSE 8080

# Set a read-only filesystem via runtime flags
# docker run --read-only --tmpfs /tmp myimage
# Runtime hardening flags for Docker
docker run \
  --read-only \                          # read-only root filesystem
  --tmpfs /tmp \                         # writable tmpfs for /tmp only
  --no-new-privileges \                  # prevent privilege escalation
  --cap-drop ALL \                       # drop all Linux capabilities
  --cap-add NET_BIND_SERVICE \           # re-add only what's needed
  --security-opt no-new-privileges:true \
  --security-opt seccomp=./seccomp.json \  # custom seccomp profile
  -u 1000:1000 \                         # run as non-root UID/GID
  --memory 512m \                        # limit memory
  --cpus 1.0 \                           # limit CPU
  myapp:latest

Case Study: Hardening a Newly Provisioned Web Server

This case study walks through applying hardening to a freshly installed Ubuntu 22.04 server designated to run a Python web application behind nginx.

Initial State Assessment

# Run Lynis before hardening to establish baseline
sudo lynis audit system --quick 2>&1 | grep "Hardening index"
# Example output: Hardening index : 55 [###########         ]

# Document listening ports
sudo ss -tlnp
# Before hardening, typical output shows:
# LISTEN 0  128  0.0.0.0:22     (ssh)
# LISTEN 0  128  0.0.0.0:25     (postfix — installed but not needed)
# LISTEN 0  128  0.0.0.0:111    (rpcbind — installed but not needed)

Applying Hardening Steps

# Step 1 — Update everything
sudo apt update && sudo apt full-upgrade -y

# Step 2 — Remove unnecessary packages
sudo apt purge postfix rpcbind avahi-daemon cups telnet nfs-kernel-server -y
sudo apt autoremove --purge -y

# Step 3 — Install and configure UFW firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

# Step 4 — Configure automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades

# Step 5 — SSH hardening (edit /etc/ssh/sshd_config)
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?X11Forwarding.*/X11Forwarding no/' /etc/ssh/sshd_config
echo "MaxAuthTries 3" | sudo tee -a /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshd

# Step 6 — Apply sysctl hardening
sudo tee /etc/sysctl.d/99-hardening.conf <<'EOF'
kernel.randomize_va_space = 2
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.suid_dumpable = 0
EOF
sudo sysctl -p /etc/sysctl.d/99-hardening.conf

# Step 7 — Create dedicated application user
sudo useradd -r -s /usr/sbin/nologin -d /var/www/myapp webappuser
sudo chown -R webappuser:webappuser /var/www/myapp

# Step 8 — Install and enable auditd
sudo apt install auditd -y
sudo systemctl enable --now auditd

# Step 9 — Lock root account
sudo passwd -l root

Post-Hardening Verification

# Verify no unexpected listening ports remain
sudo ss -tlnp
# Expected: only :22, :80, :443

# Re-run Lynis
sudo lynis audit system --quick 2>&1 | grep "Hardening index"
# Expected improvement, e.g.: Hardening index : 76 [###############     ]

# Test firewall
sudo ufw status verbose

# Confirm SSH root login blocked
ssh root@localhost  # should be rejected

Summary and Key Takeaways

System hardening is not a one-time project but an ongoing discipline that must be woven into operational processes, change management, and the software development lifecycle. The following principles underpin every hardening activity covered in this chapter:

  1. Reduce attack surface proactively. Remove what you do not need; disable what you cannot remove; restrict what you cannot disable.

  2. Apply least privilege everywhere. Users, processes, services, and containers should all operate with the minimum permissions required.

  3. Use established benchmarks. CIS Benchmarks and DISA STIGs represent the distilled expertise of large security communities — leverage them.

  4. Patch continuously. Unpatched vulnerabilities are the most reliably exploited weakness; automated patch management is a necessity, not a luxury.

  5. Verify your controls. Apply, then scan. Use tools like Lynis, OpenSCAP, and Windows Security Compliance Toolkit to confirm controls are actually in place.

  6. Log and monitor. Hardening reduces the probability of compromise; comprehensive logging reduces dwell time when compromise occurs.

  7. Automate and codify. Hardening applied manually drifts; hardening expressed in Ansible playbooks, Terraform modules, and Dockerfiles is repeatable, reviewable, and auditable.


Review Questions

  1. Define “attack surface” and explain the three primary dimensions along which it can be categorised. Provide a real-world example of a hardening control that reduces each dimension.

  2. A junior administrator argues that disabling the Guest account and renaming the Administrator account on Windows is “security through obscurity” and therefore not worthwhile. Evaluate this argument.

  3. Explain the difference between Discretionary Access Control (DAC) and Mandatory Access Control (MAC). Why is MAC considered a stronger hardening control, and what are its operational trade-offs?

  4. You discover that a web server’s nginx process is running as root. Describe the steps you would take to remediate this and explain why it matters.

  5. A CIS Benchmark check reports that kernel.randomize_va_space is set to 0. What attack class does this misconfiguration enable, and how would you remediate it?

  6. Design a patch management SLA framework for an organisation that runs a mix of internet-facing web servers, internal HR systems, and manufacturing floor SCADA systems. Justify your SLA choices for each category.

  7. Compare OpenSCAP and Lynis as hardening verification tools. In what scenarios would you choose one over the other?


Practical Exercises

Exercise 1 — Service Inventory and Reduction

Provision a fresh Ubuntu 22.04 virtual machine (or use a provided lab environment). Perform the following:

  1. Document all listening ports using ss -tlnp.

  2. For each service identified, research its purpose and determine whether it is required for a web server role.

  3. Disable all non-essential services.

  4. Confirm the change by re-running the port scan.

  5. Take a screenshot showing before and after states.

Exercise 2 — SSH Hardening

On the same lab system:

  1. Apply all SSH hardening settings from the sshd_config example in this chapter.

  2. Test that root login is blocked.

  3. Configure key-based authentication for your user account.

  4. Test that password authentication is rejected.

  5. Verify the configuration is syntactically valid using sshd -t.

Exercise 3 — Lynis Baseline and Improvement

  1. Run lynis audit system and record the Hardening Index score.

  2. Identify the top five suggestions in the output.

  3. Implement at least three of the recommendations.

  4. Re-run Lynis and document the change in score.

  5. Write a brief justification for any suggestion you chose not to implement.

Exercise 4 — Firewall Configuration

  1. Configure UFW (or iptables) to allow only SSH (port 22) and HTTPS (port 443) inbound.

  2. Use nmap from a second machine (or loopback) to verify only the expected ports respond.

  3. Add a rule to allow SSH only from a specific IP address (use 127.0.0.1 if working on a single machine).

  4. Document your final rule set.

Exercise 5 — Audit Logging

  1. Configure auditd with rules to monitor /etc/passwd, /etc/shadow, and /etc/sudoers for write access.

  2. As root, make a benign change to /etc/sudoers (add and then immediately remove a comment line).

  3. Use ausearch to retrieve the audit events generated.

  4. Identify what information the audit record contains (who, what, when).


Further Reading and References


Note

Chapter Version: 1.0 | Last Reviewed: April 2026

Commands and procedures in this chapter have been tested on Ubuntu 22.04 LTS and Windows Server 2022. Always test hardening changes in a non- production environment before deploying. Security guidance evolves rapidly; verify recommendations against the latest vendor advisories and benchmark versions before implementation.

Unit 4: System Hardening — Multiple Choice Quiz

Instructions

Select the best answer for each of the following ten questions based on the chapter provided. Reference the listed sections if you need help finding the answer.

Questions

1. What is the primary goal of the system hardening process?

  1. To maximize system compatibility and ease-of-use for all users.

  2. To shrink the attack surface to the smallest practical footprint.

  3. To ensure every service account has local administrator rights.

  4. To increase the number of active network ports for better connectivity.

Section: Introduction

2. Which dimension of the attack surface involves “Installed applications, libraries, and unpatched CVEs”?

  1. Network

  2. Human / Configuration

  3. Software

  4. Physical

Section: The Attack Surface

3. According to the Principle of Least Privilege, how should a web server process be configured?

  1. It should run as ‘root’ to ensure it has all necessary permissions.

  2. It should be part of the Domain Admins group.

  3. It should operate with the minimum permissions required for its function.

  4. It should have write access to /etc/shadow for logging purposes.

Section: The Principle of Least Privilege

4. Which hardening benchmark is described as being mandatory for the US Department of Defense?

  1. CIS Benchmarks

  2. DISA STIGs

  3. NIST SP 800-123

  4. Microsoft Secure Baselines

Section: Hardening Benchmarks and Standards

5. Under a standard Risk-based Patch SLA, what is the target remediation window for a Critical vulnerability (CVSS 9.0 – 10.0)?

  1. 24 – 72 hours

  2. 7 – 14 days

  3. 30 days

  4. 90 days

Section: Patch SLA Tiers

6. On a RHEL or Rocky Linux system, which command would you use to apply ONLY security updates?

  1. sudo apt upgrade

  2. sudo dnf update

  3. sudo dnf update --security

  4. sudo systemctl start security-patch

Section: Linux Patch Management

7. Which Windows service is associated with the “PrintNightmare” vulnerability and should be disabled if unnecessary?

  1. Remote Registry

  2. Print Spooler

  3. SNMP v1

  4. Fax

Section: Service and Port Management

8. In Linux service management, what is the effect of the systemctl mask command?

  1. It temporarily stops the service until the next reboot.

  2. It renames the service to hide it from attackers.

  3. It prevents the service from being started, even by other services.

  4. It enables multi-factor authentication for that service.

Section: Service and Port Management

9. What is the NIST SP 800-63B aligned recommendation for minimum password length for standard accounts?

  1. 8 characters

  2. 10 characters

  3. 12 characters

  4. 15 characters

Section: Password Policies

10. Which Linux mount option prevents the execution of binaries on a specific filesystem?

  1. nosuid

  2. nodev

  3. noexec

  4. ro

Section: Filesystem Mount Options


Answer Key

  1. B

  2. C

  3. C

  4. B

  5. A

  6. C

  7. B

  8. C

  9. C

  10. C