Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
@@ -0,0 +1,520 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Windows Security Baseline Audit Script
|
||||
.DESCRIPTION
|
||||
Audits a Windows Server or Workstation against Microsoft Security Baselines
|
||||
and CIS Benchmarks. Checks account policies, audit policies, user rights,
|
||||
security options, firewall configuration, Windows Update, SMB hardening,
|
||||
TLS settings, and service configuration. Produces a summary report with
|
||||
pass/fail/warning counts and an HTML report.
|
||||
.PARAMETER OutputPath
|
||||
Path for the HTML report (default: C:\SecurityAudit\baseline-audit.html)
|
||||
.PARAMETER Format
|
||||
Output format: 'html' (default), 'csv', or 'json'
|
||||
.PARAMETER ChecksFile
|
||||
Path to a custom checks JSON file (optional)
|
||||
.NOTES
|
||||
Author: Phil Connor
|
||||
Contact: contact@mylinux.work
|
||||
Website: https://mylinux.work
|
||||
License: MIT
|
||||
Version: 1.0
|
||||
#>
|
||||
|
||||
param(
|
||||
[string]$OutputPath = "C:\SecurityAudit\baseline-audit-$(Get-Date -Format 'yyyyMMdd-HHmmss').html",
|
||||
|
||||
[ValidateSet('html', 'csv', 'json')]
|
||||
[string]$Format = 'html',
|
||||
|
||||
[string]$ChecksFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
# ============================================================================
|
||||
# RESULTS COLLECTION
|
||||
# ============================================================================
|
||||
|
||||
$script:Results = [System.Collections.ArrayList]::new()
|
||||
$script:PassCount = 0
|
||||
$script:FailCount = 0
|
||||
$script:WarnCount = 0
|
||||
$script:InfoCount = 0
|
||||
|
||||
function Add-AuditResult {
|
||||
param(
|
||||
[string]$Category,
|
||||
[string]$Check,
|
||||
[ValidateSet('Pass', 'Fail', 'Warn', 'Info')]
|
||||
[string]$Status,
|
||||
[string]$Expected,
|
||||
[string]$Actual,
|
||||
[string]$Description
|
||||
)
|
||||
|
||||
switch ($Status) {
|
||||
'Pass' { $script:PassCount++ }
|
||||
'Fail' { $script:FailCount++ }
|
||||
'Warn' { $script:WarnCount++ }
|
||||
'Info' { $script:InfoCount++ }
|
||||
}
|
||||
|
||||
[void]$script:Results.Add([PSCustomObject]@{
|
||||
Category = $Category
|
||||
Check = $Check
|
||||
Status = $Status
|
||||
Expected = $Expected
|
||||
Actual = $Actual
|
||||
Description = $Description
|
||||
})
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# ACCOUNT POLICIES
|
||||
# ============================================================================
|
||||
|
||||
function Test-AccountPolicies {
|
||||
Write-Host "Checking account policies..." -ForegroundColor Cyan
|
||||
|
||||
# Export security policy
|
||||
$tempFile = "$env:TEMP\secpol_export.cfg"
|
||||
secedit /export /cfg $tempFile /quiet 2>$null
|
||||
|
||||
if (-not (Test-Path $tempFile)) {
|
||||
Add-AuditResult "Account Policies" "Security Policy Export" "Fail" "Exported" "Failed" "Cannot export security policy"
|
||||
return
|
||||
}
|
||||
|
||||
$secpol = Get-Content $tempFile
|
||||
|
||||
# Password length
|
||||
$minLen = ($secpol | Select-String 'MinimumPasswordLength\s*=\s*(\d+)' | ForEach-Object { $_.Matches.Groups[1].Value }) -as [int]
|
||||
$expected = 14
|
||||
if ($minLen -ge $expected) {
|
||||
Add-AuditResult "Account Policies" "Minimum Password Length" "Pass" ">= $expected" "$minLen" "CIS: Minimum password length"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Minimum Password Length" "Fail" ">= $expected" "$minLen" "CIS: Minimum password length should be $expected+"
|
||||
}
|
||||
|
||||
# Password history
|
||||
$history = ($secpol | Select-String 'PasswordHistorySize\s*=\s*(\d+)' | ForEach-Object { $_.Matches.Groups[1].Value }) -as [int]
|
||||
if ($history -ge 24) {
|
||||
Add-AuditResult "Account Policies" "Password History" "Pass" ">= 24" "$history" "CIS: Enforce password history"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Password History" "Fail" ">= 24" "$history" "CIS: Should remember 24+ passwords"
|
||||
}
|
||||
|
||||
# Maximum password age
|
||||
$maxAge = ($secpol | Select-String 'MaximumPasswordAge\s*=\s*(\d+)' | ForEach-Object { $_.Matches.Groups[1].Value }) -as [int]
|
||||
if ($maxAge -le 365 -and $maxAge -gt 0) {
|
||||
Add-AuditResult "Account Policies" "Maximum Password Age" "Pass" "<= 365 days" "$maxAge days" "CIS: Maximum password age"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Maximum Password Age" "Fail" "<= 365 days" "$maxAge days" "Password age too high or unlimited"
|
||||
}
|
||||
|
||||
# Account lockout threshold
|
||||
$lockout = ($secpol | Select-String 'LockoutBadCount\s*=\s*(\d+)' | ForEach-Object { $_.Matches.Groups[1].Value }) -as [int]
|
||||
if ($lockout -ge 1 -and $lockout -le 5) {
|
||||
Add-AuditResult "Account Policies" "Account Lockout Threshold" "Pass" "1-5 attempts" "$lockout" "CIS: Account lockout threshold"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Account Lockout Threshold" "Fail" "1-5 attempts" "$lockout" "Should lock after 1-5 failed attempts"
|
||||
}
|
||||
|
||||
# Lockout duration
|
||||
$lockDuration = ($secpol | Select-String 'LockoutDuration\s*=\s*(\d+)' | ForEach-Object { $_.Matches.Groups[1].Value }) -as [int]
|
||||
if ($lockDuration -ge 15) {
|
||||
Add-AuditResult "Account Policies" "Account Lockout Duration" "Pass" ">= 15 min" "$lockDuration min" "CIS: Lockout duration"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Account Lockout Duration" "Fail" ">= 15 min" "$lockDuration min" "Should be 15+ minutes"
|
||||
}
|
||||
|
||||
# Password complexity
|
||||
$complexity = $secpol | Select-String 'PasswordComplexity\s*=\s*1'
|
||||
if ($complexity) {
|
||||
Add-AuditResult "Account Policies" "Password Complexity" "Pass" "Enabled" "Enabled" "CIS: Password must meet complexity requirements"
|
||||
} else {
|
||||
Add-AuditResult "Account Policies" "Password Complexity" "Fail" "Enabled" "Disabled" "Enable password complexity"
|
||||
}
|
||||
|
||||
Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# AUDIT POLICIES
|
||||
# ============================================================================
|
||||
|
||||
function Test-AuditPolicies {
|
||||
Write-Host "Checking audit policies..." -ForegroundColor Cyan
|
||||
|
||||
$auditPolicies = @{
|
||||
"Credential Validation" = "Success and Failure"
|
||||
"Logon" = "Success and Failure"
|
||||
"Logoff" = "Success"
|
||||
"Account Lockout" = "Failure"
|
||||
"User Account Management" = "Success and Failure"
|
||||
"Security Group Management" = "Success"
|
||||
"Process Creation" = "Success"
|
||||
"Audit Policy Change" = "Success and Failure"
|
||||
"Authentication Policy Change" = "Success"
|
||||
"Sensitive Privilege Use" = "Success and Failure"
|
||||
"System Integrity" = "Success and Failure"
|
||||
"Security State Change" = "Success"
|
||||
}
|
||||
|
||||
$auditpolOutput = auditpol /get /category:* 2>$null
|
||||
|
||||
foreach ($policy in $auditPolicies.GetEnumerator()) {
|
||||
$line = $auditpolOutput | Select-String $policy.Key | Select-Object -First 1
|
||||
if ($line) {
|
||||
$currentSetting = $line.ToString().Trim()
|
||||
if ($currentSetting -match $policy.Value -or $currentSetting -match "Success and Failure") {
|
||||
Add-AuditResult "Audit Policies" $policy.Key "Pass" $policy.Value "Configured" "Advanced audit policy"
|
||||
} else {
|
||||
Add-AuditResult "Audit Policies" $policy.Key "Fail" $policy.Value "Not configured" "Enable in Advanced Audit Policy"
|
||||
}
|
||||
} else {
|
||||
Add-AuditResult "Audit Policies" $policy.Key "Warn" $policy.Value "Not found" "Cannot determine audit policy status"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# FIREWALL
|
||||
# ============================================================================
|
||||
|
||||
function Test-FirewallConfiguration {
|
||||
Write-Host "Checking firewall configuration..." -ForegroundColor Cyan
|
||||
|
||||
$profiles = @('Domain', 'Private', 'Public')
|
||||
foreach ($profile in $profiles) {
|
||||
$fw = Get-NetFirewallProfile -Name $profile -ErrorAction SilentlyContinue
|
||||
if ($fw) {
|
||||
if ($fw.Enabled) {
|
||||
Add-AuditResult "Firewall" "$profile Profile Enabled" "Pass" "Enabled" "Enabled" "Windows Firewall $profile profile"
|
||||
} else {
|
||||
Add-AuditResult "Firewall" "$profile Profile Enabled" "Fail" "Enabled" "Disabled" "Enable Windows Firewall for $profile"
|
||||
}
|
||||
|
||||
if ($fw.DefaultInboundAction -eq 'Block') {
|
||||
Add-AuditResult "Firewall" "$profile Inbound Default" "Pass" "Block" "Block" "Default inbound action"
|
||||
} else {
|
||||
Add-AuditResult "Firewall" "$profile Inbound Default" "Fail" "Block" "$($fw.DefaultInboundAction)" "Set default inbound to Block"
|
||||
}
|
||||
|
||||
if ($fw.LogFileName) {
|
||||
Add-AuditResult "Firewall" "$profile Logging" "Pass" "Enabled" $fw.LogFileName "Firewall logging path"
|
||||
} else {
|
||||
Add-AuditResult "Firewall" "$profile Logging" "Warn" "Enabled" "Not configured" "Enable firewall logging"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SMB HARDENING
|
||||
# ============================================================================
|
||||
|
||||
function Test-SMBHardening {
|
||||
Write-Host "Checking SMB hardening..." -ForegroundColor Cyan
|
||||
|
||||
# SMBv1
|
||||
$smb1 = Get-SmbServerConfiguration -ErrorAction SilentlyContinue
|
||||
if ($smb1) {
|
||||
if (-not $smb1.EnableSMB1Protocol) {
|
||||
Add-AuditResult "SMB" "SMBv1 Disabled" "Pass" "Disabled" "Disabled" "SMBv1 should be disabled (security risk)"
|
||||
} else {
|
||||
Add-AuditResult "SMB" "SMBv1 Disabled" "Fail" "Disabled" "Enabled" "Disable SMBv1: Set-SmbServerConfiguration -EnableSMB1Protocol $false"
|
||||
}
|
||||
|
||||
if ($smb1.RequireSecuritySignature) {
|
||||
Add-AuditResult "SMB" "SMB Signing Required" "Pass" "Required" "Required" "SMB signing prevents MITM attacks"
|
||||
} else {
|
||||
Add-AuditResult "SMB" "SMB Signing Required" "Fail" "Required" "Not required" "Enable SMB signing"
|
||||
}
|
||||
|
||||
if ($smb1.EncryptData) {
|
||||
Add-AuditResult "SMB" "SMB Encryption" "Pass" "Enabled" "Enabled" "SMB 3.0+ encryption"
|
||||
} else {
|
||||
Add-AuditResult "SMB" "SMB Encryption" "Warn" "Enabled" "Disabled" "Consider enabling SMB encryption"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TLS CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
function Test-TLSConfiguration {
|
||||
Write-Host "Checking TLS configuration..." -ForegroundColor Cyan
|
||||
|
||||
$protocols = @{
|
||||
'SSL 2.0' = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server'; Expected = 'Disabled' }
|
||||
'SSL 3.0' = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server'; Expected = 'Disabled' }
|
||||
'TLS 1.0' = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server'; Expected = 'Disabled' }
|
||||
'TLS 1.1' = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server'; Expected = 'Disabled' }
|
||||
'TLS 1.2' = @{ Path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server'; Expected = 'Enabled' }
|
||||
}
|
||||
|
||||
foreach ($proto in $protocols.GetEnumerator()) {
|
||||
$regPath = $proto.Value.Path
|
||||
$expected = $proto.Value.Expected
|
||||
$enabled = $true
|
||||
|
||||
if (Test-Path $regPath) {
|
||||
$val = Get-ItemProperty -Path $regPath -Name 'Enabled' -ErrorAction SilentlyContinue
|
||||
if ($val -and $val.Enabled -eq 0) { $enabled = $false }
|
||||
}
|
||||
|
||||
if ($expected -eq 'Disabled') {
|
||||
if (-not $enabled -or (Test-Path $regPath)) {
|
||||
$disabledCheck = Get-ItemProperty -Path $regPath -Name 'Enabled' -ErrorAction SilentlyContinue
|
||||
if ($disabledCheck -and $disabledCheck.Enabled -eq 0) {
|
||||
Add-AuditResult "TLS" "$($proto.Key) Disabled" "Pass" "Disabled" "Disabled" "Legacy protocol disabled"
|
||||
} else {
|
||||
Add-AuditResult "TLS" "$($proto.Key) Disabled" "Warn" "Disabled" "Not explicitly disabled" "Disable $($proto.Key) via registry"
|
||||
}
|
||||
} else {
|
||||
Add-AuditResult "TLS" "$($proto.Key) Disabled" "Warn" "Disabled" "Registry key not set" "Explicitly disable $($proto.Key)"
|
||||
}
|
||||
} else {
|
||||
Add-AuditResult "TLS" "$($proto.Key) Enabled" "Info" "Enabled" "Default" "TLS 1.2 enabled by default on modern Windows"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# WINDOWS UPDATE
|
||||
# ============================================================================
|
||||
|
||||
function Test-WindowsUpdate {
|
||||
Write-Host "Checking Windows Update status..." -ForegroundColor Cyan
|
||||
|
||||
try {
|
||||
$lastUpdate = Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 1
|
||||
if ($lastUpdate) {
|
||||
$daysSince = ((Get-Date) - $lastUpdate.InstalledOn).Days
|
||||
if ($daysSince -le 30) {
|
||||
Add-AuditResult "Windows Update" "Last Update" "Pass" "<= 30 days" "$daysSince days ago" "HotFix: $($lastUpdate.HotFixID)"
|
||||
} elseif ($daysSince -le 90) {
|
||||
Add-AuditResult "Windows Update" "Last Update" "Warn" "<= 30 days" "$daysSince days ago" "Updates may be overdue"
|
||||
} else {
|
||||
Add-AuditResult "Windows Update" "Last Update" "Fail" "<= 30 days" "$daysSince days ago" "System needs patching"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Add-AuditResult "Windows Update" "Last Update" "Warn" "<= 30 days" "Unknown" "Cannot determine update status"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# SERVICES
|
||||
# ============================================================================
|
||||
|
||||
function Test-ServiceConfiguration {
|
||||
Write-Host "Checking service configuration..." -ForegroundColor Cyan
|
||||
|
||||
$riskyServices = @{
|
||||
'RemoteRegistry' = 'Remote Registry - should be disabled'
|
||||
'Spooler' = 'Print Spooler - disable on servers not used for printing'
|
||||
'SNMP' = 'SNMP - legacy protocol, disable if unused'
|
||||
'TelnetClient' = 'Telnet - insecure, use SSH instead'
|
||||
'W3SVC' = 'IIS - disable if not serving web content'
|
||||
}
|
||||
|
||||
foreach ($svc in $riskyServices.GetEnumerator()) {
|
||||
$service = Get-Service -Name $svc.Key -ErrorAction SilentlyContinue
|
||||
if ($service) {
|
||||
if ($service.Status -eq 'Running') {
|
||||
Add-AuditResult "Services" "$($svc.Key)" "Warn" "Stopped/Disabled" "Running" $svc.Value
|
||||
} elseif ($service.StartType -eq 'Automatic') {
|
||||
Add-AuditResult "Services" "$($svc.Key)" "Warn" "Disabled" "Auto-start" $svc.Value
|
||||
} else {
|
||||
Add-AuditResult "Services" "$($svc.Key)" "Pass" "Disabled" "$($service.StartType)" $svc.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# ADDITIONAL CHECKS
|
||||
# ============================================================================
|
||||
|
||||
function Test-AdditionalSecurity {
|
||||
Write-Host "Checking additional security settings..." -ForegroundColor Cyan
|
||||
|
||||
# RDP NLA
|
||||
$nla = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'UserAuthentication' -ErrorAction SilentlyContinue
|
||||
if ($nla -and $nla.UserAuthentication -eq 1) {
|
||||
Add-AuditResult "RDP" "Network Level Authentication" "Pass" "Enabled" "Enabled" "NLA required for RDP"
|
||||
} else {
|
||||
Add-AuditResult "RDP" "Network Level Authentication" "Fail" "Enabled" "Disabled" "Enable NLA for RDP connections"
|
||||
}
|
||||
|
||||
# BitLocker
|
||||
$bitlocker = Get-BitLockerVolume -MountPoint 'C:' -ErrorAction SilentlyContinue
|
||||
if ($bitlocker -and $bitlocker.ProtectionStatus -eq 'On') {
|
||||
Add-AuditResult "Encryption" "BitLocker C: Drive" "Pass" "On" "On" "System drive encrypted"
|
||||
} else {
|
||||
Add-AuditResult "Encryption" "BitLocker C: Drive" "Warn" "On" "Off or N/A" "Consider enabling BitLocker"
|
||||
}
|
||||
|
||||
# Windows Defender
|
||||
$defender = Get-MpComputerStatus -ErrorAction SilentlyContinue
|
||||
if ($defender) {
|
||||
if ($defender.RealTimeProtectionEnabled) {
|
||||
Add-AuditResult "Defender" "Real-Time Protection" "Pass" "Enabled" "Enabled" "Windows Defender real-time protection"
|
||||
} else {
|
||||
Add-AuditResult "Defender" "Real-Time Protection" "Fail" "Enabled" "Disabled" "Enable real-time protection"
|
||||
}
|
||||
|
||||
$sigAge = $defender.AntivirusSignatureAge
|
||||
if ($sigAge -le 3) {
|
||||
Add-AuditResult "Defender" "Signature Age" "Pass" "<= 3 days" "$sigAge days" "AV signatures up to date"
|
||||
} else {
|
||||
Add-AuditResult "Defender" "Signature Age" "Fail" "<= 3 days" "$sigAge days" "Update AV signatures"
|
||||
}
|
||||
}
|
||||
|
||||
# Guest account
|
||||
$guest = Get-LocalUser -Name 'Guest' -ErrorAction SilentlyContinue
|
||||
if ($guest -and -not $guest.Enabled) {
|
||||
Add-AuditResult "Accounts" "Guest Account Disabled" "Pass" "Disabled" "Disabled" "Guest account should be disabled"
|
||||
} elseif ($guest) {
|
||||
Add-AuditResult "Accounts" "Guest Account Disabled" "Fail" "Disabled" "Enabled" "Disable the Guest account"
|
||||
}
|
||||
|
||||
# Administrator account renamed
|
||||
$admin = Get-LocalUser | Where-Object { $_.SID -match 'S-1-5-21-.*-500$' }
|
||||
if ($admin -and $admin.Name -ne 'Administrator') {
|
||||
Add-AuditResult "Accounts" "Administrator Renamed" "Pass" "Renamed" $admin.Name "Built-in admin account renamed"
|
||||
} else {
|
||||
Add-AuditResult "Accounts" "Administrator Renamed" "Warn" "Renamed" "Administrator" "Consider renaming the built-in admin account"
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# REPORT GENERATION
|
||||
# ============================================================================
|
||||
|
||||
function New-HTMLReport {
|
||||
$total = $script:PassCount + $script:FailCount + $script:WarnCount + $script:InfoCount
|
||||
$scorePercent = if (($script:PassCount + $script:FailCount) -gt 0) {
|
||||
[math]::Round($script:PassCount / ($script:PassCount + $script:FailCount) * 100, 1)
|
||||
} else { 0 }
|
||||
|
||||
$html = @"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Windows Security Baseline Audit</title>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', Tahoma, sans-serif; margin: 20px; background: #1a1a2e; color: #e0e0e0; }
|
||||
h1 { color: #ff6a00; }
|
||||
h2 { color: #ffffff; border-bottom: 1px solid #333; padding-bottom: 5px; }
|
||||
.summary { display: flex; gap: 20px; margin: 20px 0; }
|
||||
.stat { background: #2a2a3e; padding: 15px 25px; border-radius: 8px; text-align: center; }
|
||||
.stat .number { font-size: 2em; font-weight: bold; }
|
||||
.pass { color: #4caf50; }
|
||||
.fail { color: #f44336; }
|
||||
.warn { color: #ff9800; }
|
||||
.info { color: #2196f3; }
|
||||
table { border-collapse: collapse; width: 100%; margin: 15px 0; }
|
||||
th, td { padding: 8px 12px; text-align: left; border: 1px solid #333; }
|
||||
th { background: #2a2a3e; color: #ff6a00; }
|
||||
tr:nth-child(even) { background: #222233; }
|
||||
.status-pass { color: #4caf50; font-weight: bold; }
|
||||
.status-fail { color: #f44336; font-weight: bold; }
|
||||
.status-warn { color: #ff9800; font-weight: bold; }
|
||||
.status-info { color: #2196f3; }
|
||||
.score { font-size: 1.5em; color: #ff6a00; margin: 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Windows Security Baseline Audit</h1>
|
||||
<p>Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | Host: $env:COMPUTERNAME | OS: $((Get-CimInstance Win32_OperatingSystem).Caption)</p>
|
||||
<div class="score">Compliance Score: ${scorePercent}%</div>
|
||||
<div class="summary">
|
||||
<div class="stat"><div class="number pass">$($script:PassCount)</div>Pass</div>
|
||||
<div class="stat"><div class="number fail">$($script:FailCount)</div>Fail</div>
|
||||
<div class="stat"><div class="number warn">$($script:WarnCount)</div>Warn</div>
|
||||
<div class="stat"><div class="number info">$($script:InfoCount)</div>Info</div>
|
||||
<div class="stat"><div class="number">$total</div>Total</div>
|
||||
</div>
|
||||
"@
|
||||
|
||||
$categories = $script:Results | Group-Object Category
|
||||
foreach ($cat in $categories) {
|
||||
$html += "<h2>$($cat.Name)</h2>`n<table><tr><th>Check</th><th>Status</th><th>Expected</th><th>Actual</th><th>Description</th></tr>`n"
|
||||
foreach ($result in $cat.Group) {
|
||||
$statusClass = "status-$($result.Status.ToLower())"
|
||||
$html += "<tr><td>$($result.Check)</td><td class='$statusClass'>$($result.Status)</td><td>$($result.Expected)</td><td>$($result.Actual)</td><td>$($result.Description)</td></tr>`n"
|
||||
}
|
||||
$html += "</table>`n"
|
||||
}
|
||||
|
||||
$html += "</body></html>"
|
||||
|
||||
$outputDir = Split-Path $OutputPath -Parent
|
||||
if (-not (Test-Path $outputDir)) {
|
||||
New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
$html | Out-File -FilePath $OutputPath -Encoding utf8
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MAIN
|
||||
# ============================================================================
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "===== Windows Security Baseline Audit =====" -ForegroundColor Green
|
||||
Write-Host "Host: $env:COMPUTERNAME" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
Test-AccountPolicies
|
||||
Test-AuditPolicies
|
||||
Test-FirewallConfiguration
|
||||
Test-SMBHardening
|
||||
Test-TLSConfiguration
|
||||
Test-WindowsUpdate
|
||||
Test-ServiceConfiguration
|
||||
Test-AdditionalSecurity
|
||||
|
||||
# Generate report
|
||||
switch ($Format) {
|
||||
'html' { New-HTMLReport }
|
||||
'csv' {
|
||||
$outputDir = Split-Path $OutputPath -Parent
|
||||
if (-not (Test-Path $outputDir)) { New-Item -Path $outputDir -ItemType Directory -Force | Out-Null }
|
||||
$csvPath = $OutputPath -replace '\.html$', '.csv'
|
||||
$script:Results | Export-Csv -Path $csvPath -NoTypeInformation
|
||||
$OutputPath = $csvPath
|
||||
}
|
||||
'json' {
|
||||
$outputDir = Split-Path $OutputPath -Parent
|
||||
if (-not (Test-Path $outputDir)) { New-Item -Path $outputDir -ItemType Directory -Force | Out-Null }
|
||||
$jsonPath = $OutputPath -replace '\.html$', '.json'
|
||||
$script:Results | ConvertTo-Json -Depth 3 | Out-File -FilePath $jsonPath -Encoding utf8
|
||||
$OutputPath = $jsonPath
|
||||
}
|
||||
}
|
||||
|
||||
# Summary
|
||||
$total = $script:PassCount + $script:FailCount + $script:WarnCount + $script:InfoCount
|
||||
$scorePercent = if (($script:PassCount + $script:FailCount) -gt 0) {
|
||||
[math]::Round($script:PassCount / ($script:PassCount + $script:FailCount) * 100, 1)
|
||||
} else { 0 }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "===== Audit Summary =====" -ForegroundColor Green
|
||||
Write-Host " Pass: $($script:PassCount)" -ForegroundColor Green
|
||||
Write-Host " Fail: $($script:FailCount)" -ForegroundColor Red
|
||||
Write-Host " Warn: $($script:WarnCount)" -ForegroundColor Yellow
|
||||
Write-Host " Info: $($script:InfoCount)" -ForegroundColor Cyan
|
||||
Write-Host " Total: $total"
|
||||
Write-Host ""
|
||||
Write-Host " Compliance Score: ${scorePercent}%" -ForegroundColor $(if ($scorePercent -ge 80) { 'Green' } elseif ($scorePercent -ge 60) { 'Yellow' } else { 'Red' })
|
||||
Write-Host ""
|
||||
Write-Host " Report: $OutputPath" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Reference in New Issue
Block a user