1. System Architecture & Workflow
The diagram below highlights the security domains for OS Hardening. Mitigating risk on Linux hosts focuses on strict permission bits (like masking /etc/shadow), auditing open ports, and disabling insecure SSH login paths, while Windows hardening relies on Group Policy objects to control password complexities, audit parameters, and firewall rules.
2. Part 1: Step-by-Step Action Items & Key Execution Steps
STEP 1
Configure Windows Password Policy in Group Policy Editor
Launch the Local Group Policy Editor in Windows and enforce strict password parameters to resist brute-force exploitation.
- Log in to your Windows VM using an administrator account.
- On your keyboard, press the Windows Key + R to display the Run dialog.
- Type
gpedit.msc in the Open text box and press Enter. This launches the Local Group Policy Editor GUI.
- In the left folder tree, navigate through the folders:
- Computer Configuration
- Double-click Windows Settings
- Double-click Security Settings
- Double-click Account Policies
- Select the Password Policy folder.
- In the right-hand details pane, double-click the policy named Minimum password length.
- In the Properties dialog box, change the character limit value to 12 characters.
- Click Apply, then click OK.
- Double-click the policy named Password must meet complexity requirements.
- Select the Enabled option, then click Apply and OK. This forces accounts to use mixed-case letters, digits, and special characters.
STEP 2
Configure SSH Server Access Controls on Linux
Edit the OpenSSH service configuration files on Linux to restrict root sessions and enforce key-based access authentication.
- Log in to your Kali Linux VM.
- Open a terminal window.
- Execute the commands below to backup and edit the OpenSSH configurations.
$ sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
This command copies the active OpenSSH configuration to sshd_config.bak to serve as a backup fallback if a mistake is made during editing.
4. Open the configuration file in the nano editor under administrator privilege:
$ sudo nano /etc/ssh/sshd_config
This command opens the sshd_config file in nano to modify default remote access paths.
| Part | What It Does |
|---|
nano | A simple, beginner-friendly terminal text editor for creating and editing files |
- Inside the editor window, locate the line that reads:
#PermitRootLogin prohibit-password or PermitRootLogin yes.
- Remove the starting comment symbol (
#) if present, and change the value to: PermitRootLogin no. This prevents direct brute-forcing attempts on the administrative account.
- Find the line that reads
#PubkeyAuthentication yes, and uncomment it by deleting the # character.
- Press Ctrl + O and then press Enter to save the configuration changes. Press Ctrl + X to exit the nano editor.
- Execute the command below to reload the daemon settings:
$ sudo systemctl restart ssh
This command restarts the SSH server service to apply the PermitRootLogin restrictions to active listeners.
| Part | What It Does |
|---|
systemctl | System Control — manages systemd services (start, stop, enable, disable, check status) |
restart | Stops and then starts the service (applies configuration changes) |
STEP 3
Audit and Fix File System Permissions on Linux
Identify files that are world-writable and restrict access permissions on sensitive credential storage locations.
- In the Kali terminal, search for any world-writable files in public directories by running:
$ find /home/student -perm -o+w -type f 2>/dev/null
This command scans the user's home directory for files where others possess write access, helping to identify unauthorized modification paths.
2. Audit the permissions of the shadow passwords file using the command below:
$ ls -l /etc/shadow
This command displays read/write permissions for the hashed credentials database. The mask must restrict access strictly to root users.
3. If permissions allow others to read or modify the file, enforce the secure mask by executing:
$ sudo chmod 600 /etc/shadow
This command applies read/write permissions to the owner (root) and removes all read/write/execute rights for groups and other system accounts.
| Part | What It Does |
|---|
chmod | Change Mode — modifies file permissions (read, write, execute) |
600 | Owner can read and write; no access for group or others (secure for private keys) |
3. Automation Architecture
The diagram below highlights the scheduled auditing lifecycle. System tasks run daily via cron on Linux or Task Scheduler on Windows, running checking scripts, identifying configurations that deviate from baselines, logging warnings, and locking inactive user nodes.
4. Part 2: Complete Deliverable Assets & Production Templates
To automate OS hardening audits across your deployment infrastructure, you will write two separate automation scripts: one bash script for your Linux VM and one PowerShell script for your Windows VM.
Linux Hardening Audit Script
Save the script below inside your Kali Linux VM as /home/student/Scripts/audit_linux.sh, make it executable, and run the audit scanner:
$ chmod +x /home/student/Scripts/audit_linux.sh && /home/student/Scripts/audit_linux.sh
This command updates file execution properties and starts the Linux vulnerability audit script.
Code Breakdown — Line by Line
Copy
Line 1: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
#!/usr/bin/env bash
Line 2: This is a comment that describes what the code does: "!/usr/bin/env bash". Comments start with # and are ignored by Python.
# audit_linux.sh - Automated Linux Hardening Auditor
Line 3: This is a comment that describes what the code does: "audit_linux.sh - Automated Linux Hardening Auditor". Comments start with # and are ignored by Python.
set -euo pipefail
Line 4: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
REPORT_LOG="./linux_hardening_report.log"
Line 5: Creates a variable called REPORT_LOG and assigns a value to it. Variables store data for use later in the program.
echo "=== Linux Hardening Audit Report ===" > "${REPORT_LOG}"
Line 6: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
echo "Execution Time: $(date)" >> "${REPORT_LOG}"
Line 7: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
echo "------------------------------------" >> "${REPORT_LOG}"
Line 8: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
# 1. Check for Shadow File Permissions
Line 9: This is a comment that describes what the code does: "1. Check for Shadow File Permissions". Comments start with # and are ignored by Python.
SHADOW_PERMS=$(stat -c "%a" /etc/shadow)
Line 10: Creates a variable called SHADOW_PERMS and assigns a value to it. Variables store data for use later in the program.
if [ "${SHADOW_PERMS}" -eq 600 ] || [ "${SHADOW_PERMS}" -eq 000 ]; then
Line 11: A conditional check — the code inside this block only runs if the condition evaluates to True.
echo "[OK] Shadow file permissions are secure: ${SHADOW_PERMS}" >> "${REPORT_LOG}"
Line 12: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
else
Line 13: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
echo "[FAIL] Shadow file permissions are weak: ${SHADOW_PERMS} (Expected 600)" >> "${REPORT_LOG}"
Line 14: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
fi
Line 15: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
# 2. Verify SSH Root Login Status
Line 16: This is a comment that describes what the code does: "2. Verify SSH Root Login Status". Comments start with # and are ignored by Python.
if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config; then
Line 17: A conditional check — the code inside this block only runs if the condition evaluates to True.
echo "[OK] SSH Remote Root Login is disabled." >> "${REPORT_LOG}"
Line 18: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
else
Line 19: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
echo "[FAIL] SSH configuration allows Remote Root Login." >> "${REPORT_LOG}"
Line 20: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
✓ Complete Combined Script: All lines explained above are combined into the full script shown below. Copy and paste the entire script into your file.
Copy
#!/usr/bin/env bash
# audit_linux.sh - Automated Linux Hardening Auditor
set -euo pipefail
REPORT_LOG="./linux_hardening_report.log"
echo "=== Linux Hardening Audit Report ===" > "${REPORT_LOG}"
echo "Execution Time: $(date)" >> "${REPORT_LOG}"
echo "------------------------------------" >> "${REPORT_LOG}"
# 1. Check for Shadow File Permissions
SHADOW_PERMS=$(stat -c "%a" /etc/shadow)
if [ "${SHADOW_PERMS}" -eq 600 ] || [ "${SHADOW_PERMS}" -eq 000 ]; then
echo "[OK] Shadow file permissions are secure: ${SHADOW_PERMS}" >> "${REPORT_LOG}"
else
echo "[FAIL] Shadow file permissions are weak: ${SHADOW_PERMS} (Expected 600)" >> "${REPORT_LOG}"
fi
# 2. Verify SSH Root Login Status
if grep -q "^PermitRootLogin no" /etc/ssh/sshd_config; then
echo "[OK] SSH Remote Root Login is disabled." >> "${REPORT_LOG}"
else
echo "[FAIL] SSH configuration allows Remote Root Login." >> "${REPORT_LOG}"
fi
# 3. Check for Insecure Listening Daemons
if ss -tulpn 2>/dev/null | grep -E -q ":(21|23) "; then
echo "[FAIL] Insecure active daemons detected (FTP port 21 / Telnet port 23)" >> "${REPORT_LOG}"
else
echo "[OK] No insecure legacy listener daemons active on FTP/Telnet ports." >> "${REPORT_LOG}"
fi
echo "[*] Hardening Audit complete. Output written to ${REPORT_LOG}."
cat "${REPORT_LOG}"
Windows Hardening Audit Script
Save the script below inside your Windows VM as C:\Scripts\AuditHardening.ps1 and run it inside an Administrator PowerShell prompt window:
PS> C:\Scripts\AuditHardening.ps1
This runs the Windows security compliance verification script, parsing service states and registry locks.
Code Breakdown — Line by Line
Copy
Line 1: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
# AuditHardening.ps1 - Automated Windows Hardening Compliance Inspector
Line 2: This is a comment that describes what the code does: "AuditHardening.ps1 - Automated Windows Hardening Compliance Inspector". Comments start with # and are ignored by Python.
set-strictmode -version 2.0
Line 3: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$ReportPath = ".\WindowsHardeningReport.txt"
Line 4: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$Report = @()
Line 5: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$Report += "=== Windows System Hardening Report ==="
Line 6: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$Report += "Execution Time: $(Get-Date)"
Line 7: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$Report += "---------------------------------------"
Line 8: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
# 1. Audit Password Complexity Registry Settings
Line 9: This is a comment that describes what the code does: "1. Audit Password Complexity Registry Settings". Comments start with # and are ignored by Python.
$ComplexityPath = "HKLM:\SAM\SAM\Domains\Account"
Line 10: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
if (Test-Path $ComplexityPath) {
Line 11: A conditional check — the code inside this block only runs if the condition evaluates to True.
# SAM folder is protected; read standard SecEdit metrics instead
Line 12: This is a comment that describes what the code does: "SAM folder is protected; read standard SecEdit metrics instead". Comments start with # and are ignored by Python.
$Report += "[INFO] Checking local account database baselines..."
Line 13: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
}
Line 14: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
# 2. Check for Insecure Listening Services (e.g. Remote Registry, Telnet if any)
Line 15: This is a comment that describes what the code does: "2. Check for Insecure Listening Services (e.g. Remote Registry, Telnet if any)". Comments start with # and are ignored by Python.
$UnsecureServices = @("RemoteRegistry", "TlntSvr", "SessionEnv")
Line 16: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
foreach ($service in $UnsecureServices) {
Line 17: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
$srv = Get-Service -Name $service -ErrorAction SilentlyContinue
Line 18: This line performs an operation as part of the script logic. It contributes to the overall functionality of the program.
if ($srv) {
Line 19: A conditional check — the code inside this block only runs if the condition evaluates to True.
if ($srv.Status -eq "Running") {
Line 20: A conditional check — the code inside this block only runs if the condition evaluates to True.
✓ Complete Combined Script: All lines explained above are combined into the full script shown below. Copy and paste the entire script into your file.
Copy
# AuditHardening.ps1 - Automated Windows Hardening Compliance Inspector
set-strictmode -version 2.0
$ReportPath = ".\WindowsHardeningReport.txt"
$Report = @()
$Report += "=== Windows System Hardening Report ==="
$Report += "Execution Time: $(Get-Date)"
$Report += "---------------------------------------"
# 1. Audit Password Complexity Registry Settings
$ComplexityPath = "HKLM:\SAM\SAM\Domains\Account"
if (Test-Path $ComplexityPath) {
# SAM folder is protected; read standard SecEdit metrics instead
$Report += "[INFO] Checking local account database baselines..."
}
# 2. Check for Insecure Listening Services (e.g. Remote Registry, Telnet if any)
$UnsecureServices = @("RemoteRegistry", "TlntSvr", "SessionEnv")
foreach ($service in $UnsecureServices) {
$srv = Get-Service -Name $service -ErrorAction SilentlyContinue
if ($srv) {
if ($srv.Status -eq "Running") {
$Report += "[FAIL] Insecure service running: $($srv.DisplayName)"
} else {
$Report += "[OK] Insecure service $($service) is disabled or stopped."
}
} else {
$Report += "[OK] Service $($service) is not present on system."
}
}
# 3. Verify Local Firewall Profile Status
$FirewallProfiles = Get-NetFirewallProfile -Profile Domain,Private,Public
foreach ($profile in $FirewallProfiles) {
if ($profile.Enabled -eq $True) {
$Report += "[OK] Firewall Profile $($profile.Name) is Enabled."
} else {
$Report += "[FAIL] Firewall Profile $($profile.Name) is Disabled!"
}
}
# Output Report
$Report | Out-File -FilePath $ReportPath -Encoding utf8
$Report
Write-Host "Hardening report compiled: $ReportPath" -ForegroundColor Green
5. Deliverables Summary
Students must produce and submit the following artifacts to verify completion of this training lab.
Created Files / Configs
- Linux script:
/home/student/Scripts/audit_linux.sh
- Windows script:
C:\Scripts\AuditHardening.ps1
- Saved audit log outputs:
linux_hardening_report.log and WindowsHardeningReport.txt
Verification Artifacts / Execution Proof
- Windows screenshot showing GPO password length rules set to 12.
- Linux screenshot verifying
PermitRootLogin no in /etc/ssh/sshd_config.
- Command line output from the validation script showing no insecure legacy services running.
6. Closing Explanation: Why We Did This & What It Accomplishes
Architectural Intent & Operational Impact
Why We Did This
- Reducing user privileges and enforcing account policy bounds limits the impact of credential leakage, preventing automated brute-force tools from obtaining domain rights easily.
- Disabling legacy daemons (like FTP, Telnet, or Remote Registry) decreases system exposure. Attackers cannot target services that are not running.
- Automating configuration audits via PowerShell and Bash creates a baseline validation suite, helping system administrators discover unauthorized changes.
What This Accomplishes
- Introduces fundamental system hardening concepts on both Windows and Linux OS.
- Familiarizes students with editing registry flags, editing text configurations, and navigating the GPO editor GUI.
- Provides baseline automation templates to support server provisioning security in enterprise deployments.