Vulnerability/Get-SecurityMisconfigurations.ps1
|
function Get-SecurityMisconfigurations { <# .SYNOPSIS Detects common security misconfigurations in Windows systems .DESCRIPTION Scans for security misconfigurations including weak passwords policies, insecure services, disabled security features, and compliance violations. Provides detailed findings with remediation steps. .PARAMETER Category Specific category to scan: All, Authentication, Network, Services, FileSystem, Registry .PARAMETER SeverityLevel Minimum severity level to report: All, Critical, High, Medium, Low .PARAMETER OutputPath Path to save misconfiguration report .PARAMETER Format Report format: HTML, JSON, CSV, or All .EXAMPLE Get-SecurityMisconfigurations Get-SecurityMisconfigurations -Category Authentication -SeverityLevel High .OUTPUTS PSCustomObject with misconfiguration findings #> [CmdletBinding()] param( [Parameter()] [ValidateSet('All', 'Authentication', 'Network', 'Services', 'FileSystem', 'Registry')] [string]$Category = 'All', [Parameter()] [ValidateSet('All', 'Critical', 'High', 'Medium', 'Low')] [string]$SeverityLevel = 'All', [Parameter()] [string]$OutputPath, [Parameter()] [ValidateSet('HTML', 'JSON', 'CSV', 'All')] [string]$Format = 'HTML' ) begin { Write-Host "=== SECURITY MISCONFIGURATION SCAN ===" -ForegroundColor Cyan Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow Write-Host "Scan Category: $Category" -ForegroundColor Yellow Write-Host "Severity Filter: $SeverityLevel" -ForegroundColor Yellow $osCaption = $null try { $osCaption = (Get-CimInstance Win32_OperatingSystem -ErrorAction Stop).Caption } catch { $osCaption = $null } $findings = [PSCustomObject]@{ ScanDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ComputerName = $env:COMPUTERNAME OSVersion = $osCaption Misconfigurations = @() Summary = @{ TotalFindings = 0 Critical = 0 High = 0 Medium = 0 Low = 0 } Recommendations = @() } } process { try { # 1. AUTHENTICATION & PASSWORD POLICIES if ($Category -in @('All', 'Authentication')) { Write-Host "`n[*] Checking authentication configurations..." -ForegroundColor Cyan # Password Policy try { $secpol = @{} # Randomized temp name so concurrent runs cannot clobber each other (matches Test-CISBenchmark) $secpolPath = "$env:TEMP\secpol_$(Get-Random).cfg" secedit /export /cfg $secpolPath /quiet | Out-Null Get-Content $secpolPath | Where-Object { $_ -match '=' } | ForEach-Object { $parts = $_ -split '=', 2 $secpol[$parts[0].Trim()] = $parts[1].Trim() } Remove-Item $secpolPath -Force -ErrorAction SilentlyContinue # Check minimum password length $minPwdLength = [int]$secpol['MinimumPasswordLength'] if ($minPwdLength -lt 14) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Authentication' Finding = 'Weak Password Length Policy' Severity = 'High' CurrentValue = "$minPwdLength characters" RecommendedValue = '14+ characters' Description = 'Minimum password length is below recommended security standard' Remediation = 'Set minimum password length to 14: secpol.msc > Password Policy > Minimum password length' Risk = 'Weak passwords are vulnerable to brute-force attacks' } $findings.Summary.High++ Write-Host " [!] HIGH: Minimum password length is $minPwdLength (recommend 14+)" -ForegroundColor Yellow } # Check password complexity $pwdComplexity = $secpol['PasswordComplexity'] if ($pwdComplexity -eq '0') { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Authentication' Finding = 'Password Complexity Disabled' Severity = 'Critical' CurrentValue = 'Disabled' RecommendedValue = 'Enabled' Description = 'Password complexity requirements are not enforced' Remediation = 'Enable password complexity: secpol.msc > Password Policy > Password must meet complexity requirements' Risk = 'Users can set simple, easily guessable passwords' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: Password complexity is disabled" -ForegroundColor Red } # Check password history $pwdHistory = [int]$secpol['PasswordHistorySize'] if ($pwdHistory -lt 12) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Authentication' Finding = 'Insufficient Password History' Severity = 'Medium' CurrentValue = "$pwdHistory passwords" RecommendedValue = '12+ passwords' Description = 'Password history size is below recommended value' Remediation = 'Set password history to 12: secpol.msc > Password Policy > Enforce password history' Risk = 'Users can quickly reuse old passwords' } $findings.Summary.Medium++ } # Check account lockout threshold $lockoutThreshold = $secpol['LockoutBadCount'] if ($lockoutThreshold -eq '0' -or [int]$lockoutThreshold -gt 10) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Authentication' Finding = 'Weak Account Lockout Policy' Severity = 'High' CurrentValue = if($lockoutThreshold -eq '0'){'Disabled'}else{"$lockoutThreshold attempts"} RecommendedValue = '5-10 attempts' Description = 'Account lockout threshold is too high or disabled' Remediation = 'Set lockout threshold: secpol.msc > Account Lockout Policy > Account lockout threshold' Risk = 'Vulnerable to brute-force password attacks' } $findings.Summary.High++ Write-Host " [!] HIGH: Account lockout threshold: $lockoutThreshold" -ForegroundColor Yellow } } catch { Write-Warning "Could not check password policies: $_" } # Check for blank passwords try { $users = Get-LocalUser | Where-Object { $_.Enabled -eq $true } foreach ($user in $users) { if ($null -eq $user.PasswordLastSet) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Authentication' Finding = "User with No Password Set: $($user.Name)" Severity = 'Critical' CurrentValue = 'No password' RecommendedValue = 'Strong password required' Description = "User account '$($user.Name)' has no password set" Remediation = "Set password: net user $($user.Name) *" Risk = 'Account can be accessed without authentication' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: User '$($user.Name)' has no password" -ForegroundColor Red } } } catch { Write-Warning "Could not check user passwords: $_" } } # 2. NETWORK SECURITY if ($Category -in @('All', 'Network')) { Write-Host "`n[*] Checking network configurations..." -ForegroundColor Cyan # Check Windows Firewall try { $fwProfiles = Get-NetFirewallProfile -ErrorAction SilentlyContinue foreach ($profile in $fwProfiles) { if (-not $profile.Enabled) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Network' Finding = "Windows Firewall Disabled: $($profile.Name)" Severity = 'Critical' CurrentValue = 'Disabled' RecommendedValue = 'Enabled' Description = "Windows Firewall is disabled for $($profile.Name) profile" Remediation = "Enable firewall: Set-NetFirewallProfile -Name $($profile.Name) -Enabled True" Risk = 'System is exposed to network-based attacks' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: Firewall disabled for $($profile.Name) profile" -ForegroundColor Red } } } catch { Write-Warning "Could not check firewall status: $_" } # Check for public network profile try { $publicProfile = Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Public' } if (-not $publicProfile) { $privateCount = @(Get-NetConnectionProfile | Where-Object { $_.NetworkCategory -eq 'Private' }).Count if ($privateCount -gt 0) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Network' Finding = 'Network Set as Private Instead of Public' Severity = 'Medium' CurrentValue = 'Private' RecommendedValue = 'Public (for untrusted networks)' Description = 'Network is set to Private which allows more network discovery' Remediation = 'Set network to Public in Settings > Network & Internet' Risk = 'Increased network exposure on untrusted networks' } $findings.Summary.Medium++ } } } catch { Write-Warning "Could not check network profiles: $_" } # Check for SMBv1 try { $smbv1 = Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -ErrorAction SilentlyContinue if ($smbv1 -and $smbv1.State -eq 'Enabled') { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Network' Finding = 'SMBv1 Protocol Enabled' Severity = 'Critical' CurrentValue = 'Enabled' RecommendedValue = 'Disabled' Description = 'Insecure SMBv1 protocol is enabled (vulnerable to WannaCry/NotPetya)' Remediation = 'Disable SMBv1: Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol' Risk = 'Vulnerable to ransomware and remote exploits' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: SMBv1 is enabled (security risk)" -ForegroundColor Red } } catch { Write-Warning "Could not check SMBv1 status: $_" } # Check RDP settings try { $rdpEnabled = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name fDenyTSConnections).fDenyTSConnections if ($rdpEnabled -eq 0) { # RDP is enabled, check NLA $nlaEnabled = (Get-ItemProperty 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name UserAuthentication).UserAuthentication if ($nlaEnabled -eq 0) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Network' Finding = 'RDP Network Level Authentication Disabled' Severity = 'High' CurrentValue = 'Disabled' RecommendedValue = 'Enabled' Description = 'Remote Desktop has NLA disabled, allowing unauthenticated connections' Remediation = 'Enable NLA: Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name UserAuthentication -Value 1' Risk = 'RDP vulnerable to brute-force attacks' } $findings.Summary.High++ Write-Host " [!] HIGH: RDP NLA is disabled" -ForegroundColor Yellow } } } catch { Write-Warning "Could not check RDP settings: $_" } } # 3. SERVICES if ($Category -in @('All', 'Services')) { Write-Host "`n[*] Checking service configurations..." -ForegroundColor Cyan # Check for unnecessary running services $riskyServices = @( @{Name='RemoteRegistry'; Severity='High'; Risk='Allows remote registry access'}, @{Name='TlntSvr'; Severity='Critical'; Risk='Unencrypted remote access'}, @{Name='SNMP'; Severity='Medium'; Risk='Information disclosure'}, @{Name='RpcLocator'; Severity='Low'; Risk='Rarely needed service'} ) foreach ($svc in $riskyServices) { $service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue if ($service -and $service.Status -eq 'Running') { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Services' Finding = "Risky Service Running: $($svc.Name)" Severity = $svc.Severity CurrentValue = 'Running' RecommendedValue = 'Stopped/Disabled' Description = "Service '$($svc.Name)' is running" Remediation = "Stop and disable: Stop-Service -Name $($svc.Name); Set-Service -Name $($svc.Name) -StartupType Disabled" Risk = $svc.Risk } switch ($svc.Severity) { 'Critical' { $findings.Summary.Critical++ } 'High' { $findings.Summary.High++ } 'Medium' { $findings.Summary.Medium++ } 'Low' { $findings.Summary.Low++ } } Write-Host " [!] $($svc.Severity): Service '$($svc.Name)' is running" -ForegroundColor $( switch ($svc.Severity) { 'Critical' { 'Red' } 'High' { 'Yellow' } default { 'Gray' } } ) } } } # 4. FILE SYSTEM if ($Category -in @('All', 'FileSystem')) { Write-Host "`n[*] Checking file system configurations..." -ForegroundColor Cyan # Check for world-writable folders in system paths $systemPaths = @('C:\Windows', 'C:\Program Files', 'C:\Program Files (x86)') foreach ($path in $systemPaths) { if (Test-Path $path) { try { $acl = Get-Acl $path -ErrorAction SilentlyContinue $everyoneWrite = $acl.Access | Where-Object { (Test-IdentityReferenceIsSid -IdentityReference $_.IdentityReference -Sid 'S-1-1-0') -and $_.FileSystemRights -match 'Write|FullControl|Modify' } if ($everyoneWrite) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'FileSystem' Finding = "World-Writable System Folder: $path" Severity = 'Critical' CurrentValue = 'Everyone has write access' RecommendedValue = 'Restricted permissions' Description = "System folder has insecure permissions allowing 'Everyone' to write" Remediation = "Review and restrict permissions using icacls or File Explorer" Risk = 'Attackers can modify system files or plant malware' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: World-writable folder: $path" -ForegroundColor Red } } catch { # Skip if access denied } } } # Check BitLocker status try { $volumes = Get-BitLockerVolume -ErrorAction SilentlyContinue $unencrypted = $volumes | Where-Object { $_.VolumeType -eq 'OperatingSystem' -and $_.ProtectionStatus -eq 'Off' } if ($unencrypted) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'FileSystem' Finding = 'BitLocker Not Enabled on OS Drive' Severity = 'High' CurrentValue = 'Not encrypted' RecommendedValue = 'Encrypted with BitLocker' Description = 'Operating system drive is not encrypted with BitLocker' Remediation = 'Enable BitLocker: Control Panel > BitLocker Drive Encryption' Risk = 'Data vulnerable to physical theft or offline attacks' } $findings.Summary.High++ Write-Host " [!] HIGH: BitLocker not enabled on OS drive" -ForegroundColor Yellow } } catch { # BitLocker cmdlets might not be available } } # 5. REGISTRY SECURITY if ($Category -in @('All', 'Registry')) { Write-Host "`n[*] Checking registry configurations..." -ForegroundColor Cyan # Check UAC settings try { $uacEnabled = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -Name EnableLUA).EnableLUA if ($uacEnabled -eq 0) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Registry' Finding = 'User Account Control (UAC) Disabled' Severity = 'Critical' CurrentValue = 'Disabled' RecommendedValue = 'Enabled' Description = 'User Account Control is completely disabled' Remediation = 'Enable UAC: Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name EnableLUA -Value 1' Risk = 'Malware can gain administrative privileges without prompting' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: UAC is disabled" -ForegroundColor Red } else { # Check UAC level $uacLevel = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -Name ConsentPromptBehaviorAdmin).ConsentPromptBehaviorAdmin if ($uacLevel -eq 0) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Registry' Finding = 'UAC Set to Never Notify' Severity = 'High' CurrentValue = 'Never notify' RecommendedValue = 'Always notify' Description = 'UAC is set to never notify for administrative actions' Remediation = 'Increase UAC level in User Account Control Settings' Risk = 'Reduced visibility of privilege escalation attempts' } $findings.Summary.High++ Write-Host " [!] HIGH: UAC set to never notify" -ForegroundColor Yellow } } } catch { Write-Warning "Could not check UAC settings: $_" } # Check Windows Defender try { $defenderDisabled = (Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' -Name DisableAntiSpyware -ErrorAction SilentlyContinue).DisableAntiSpyware if ($defenderDisabled -eq 1) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Registry' Finding = 'Windows Defender Disabled via Registry' Severity = 'Critical' CurrentValue = 'Disabled' RecommendedValue = 'Enabled' Description = 'Windows Defender antivirus is disabled via Group Policy' Remediation = 'Remove registry value or enable via Group Policy' Risk = 'No real-time malware protection' } $findings.Summary.Critical++ Write-Host " [!] CRITICAL: Windows Defender is disabled" -ForegroundColor Red } } catch { # Key might not exist, which is fine } # Check auto-run entries try { $autoRunKeys = @( 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' ) $suspiciousCount = 0 foreach ($key in $autoRunKeys) { $entries = Get-ItemProperty $key -ErrorAction SilentlyContinue if ($entries) { $entries.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' -and $_.Value -match '(temp|appdata\\local\\temp|downloads)' } | ForEach-Object { $suspiciousCount++ } } } if ($suspiciousCount -gt 0) { $findings.Misconfigurations += [PSCustomObject]@{ Category = 'Registry' Finding = "Suspicious Autorun Entries: $suspiciousCount" Severity = 'High' CurrentValue = "$suspiciousCount suspicious entries" RecommendedValue = 'Review and remove unauthorized entries' Description = 'Autorun entries pointing to temporary or suspicious locations detected' Remediation = 'Review entries using: Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' Risk = 'Potential malware persistence mechanism' } $findings.Summary.High++ Write-Host " [!] HIGH: $suspiciousCount suspicious autorun entries" -ForegroundColor Yellow } } catch { Write-Warning "Could not check autorun entries: $_" } } # Filter by severity level if specified if ($SeverityLevel -ne 'All') { $severityOrder = @('Critical', 'High', 'Medium', 'Low') $minIndex = $severityOrder.IndexOf($SeverityLevel) $findings.Misconfigurations = @($findings.Misconfigurations | Where-Object { $severityOrder.IndexOf($_.Severity) -le $minIndex }) # Recompute the per-severity counters so the summary matches the filtered list $findings.Summary.Critical = @($findings.Misconfigurations | Where-Object { $_.Severity -eq 'Critical' }).Count $findings.Summary.High = @($findings.Misconfigurations | Where-Object { $_.Severity -eq 'High' }).Count $findings.Summary.Medium = @($findings.Misconfigurations | Where-Object { $_.Severity -eq 'Medium' }).Count $findings.Summary.Low = @($findings.Misconfigurations | Where-Object { $_.Severity -eq 'Low' }).Count } # Calculate total findings (after the severity filter so the count matches the list) $findings.Summary.TotalFindings = $findings.Misconfigurations.Count # Generate recommendations Write-Host "`n[*] Generating recommendations..." -ForegroundColor Cyan if ($findings.Summary.Critical -gt 0) { $findings.Recommendations += "CRITICAL: Address $($findings.Summary.Critical) critical misconfiguration(s) immediately" } if ($findings.Summary.High -gt 0) { $findings.Recommendations += "HIGH: Fix $($findings.Summary.High) high-severity misconfiguration(s) within 7 days" } if ($findings.Summary.Medium -gt 0) { $findings.Recommendations += "MEDIUM: Remediate $($findings.Summary.Medium) medium-severity issue(s) within 30 days" } if ($findings.Summary.TotalFindings -eq 0) { $findings.Recommendations += "No significant misconfigurations detected - maintain current security posture" } $findings.Recommendations += "Implement security baseline using Group Policy" $findings.Recommendations += "Run monthly security configuration audits" $findings.Recommendations += "Use Microsoft Security Compliance Toolkit" $findings.Recommendations += "Enable security audit logging for configuration changes" # Export reports if ($OutputPath) { Write-Host "`n[*] Exporting misconfiguration reports..." -ForegroundColor Cyan if (-not (Test-Path $OutputPath)) { New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null } $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $reportName = "SecurityMisconfigurations_$timestamp" # Export JSON if ($Format -in @('JSON', 'All')) { $jsonPath = Join-Path $OutputPath "$reportName.json" $findings | ConvertTo-Json -Depth 10 | Out-File $jsonPath -Encoding UTF8 Write-Host " JSON report saved: $jsonPath" -ForegroundColor Green } # Export CSV if ($Format -in @('CSV', 'All')) { $csvPath = Join-Path $OutputPath "$reportName.csv" $findings.Misconfigurations | Export-Csv $csvPath -NoTypeInformation Write-Host " CSV report saved: $csvPath" -ForegroundColor Green } # Export HTML if ($Format -in @('HTML', 'All')) { $htmlPath = Join-Path $OutputPath "$reportName.html" $html = Generate-MisconfigurationHTML -Findings $findings $html | Out-File $htmlPath -Encoding UTF8 Write-Host " HTML report saved: $htmlPath" -ForegroundColor Green } } } catch { Write-Error "Error during misconfiguration scan: $_" throw } } end { Write-Host "`n=== MISCONFIGURATION SCAN COMPLETE ===" -ForegroundColor $( if ($findings.Summary.Critical -gt 0) { 'Red' } elseif ($findings.Summary.High -gt 0) { 'Yellow' } else { 'Green' } ) Write-Host "`nSummary:" -ForegroundColor Cyan Write-Host " Total Findings: $($findings.Summary.TotalFindings)" -ForegroundColor White Write-Host " Critical: $($findings.Summary.Critical)" -ForegroundColor $( if ($findings.Summary.Critical -gt 0) { 'Red' } else { 'Gray' } ) Write-Host " High: $($findings.Summary.High)" -ForegroundColor $( if ($findings.Summary.High -gt 0) { 'Yellow' } else { 'Gray' } ) Write-Host " Medium: $($findings.Summary.Medium)" -ForegroundColor Gray Write-Host " Low: $($findings.Summary.Low)" -ForegroundColor Gray if ($findings.Misconfigurations.Count -gt 0) { Write-Host "`nTop Issues:" -ForegroundColor Cyan $findings.Misconfigurations | Sort-Object { switch ($_.Severity) { 'Critical' { 0 } 'High' { 1 } 'Medium' { 2 } 'Low' { 3 } } } | Select-Object -First 10 | ForEach-Object { $color = switch ($_.Severity) { 'Critical' { 'Red' } 'High' { 'Yellow' } default { 'Gray' } } Write-Host " [$($_.Severity)] $($_.Finding)" -ForegroundColor $color } } if ($findings.Recommendations.Count -gt 0) { Write-Host "`nRecommendations:" -ForegroundColor Cyan $findings.Recommendations | ForEach-Object { Write-Host " ! $_" -ForegroundColor Yellow } } return $findings } } # Helper function to generate HTML report function Generate-MisconfigurationHTML { param($Findings) $riskColor = if($Findings.Summary.Critical -gt 0){'critical'} elseif($Findings.Summary.High -gt 0){'high'} else{'safe'} $html = @" <!DOCTYPE html> <html> <head> <title>Security Misconfigurations - $($Findings.ComputerName)</title> <style> body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; } .container { max-width: 1400px; margin: 0 auto; background: white; padding: 30px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } h1 { color: #2c3e50; border-bottom: 3px solid #e74c3c; padding-bottom: 10px; } h2 { color: #34495e; margin-top: 30px; } .summary { background: #ecf0f1; padding: 20px; border-radius: 5px; margin: 20px 0; } .metric { display: inline-block; margin: 10px 20px 10px 0; } .metric-label { font-weight: bold; color: #7f8c8d; } .metric-value { font-size: 24px; font-weight: bold; } .critical { color: #e74c3c; } .high { color: #e67e22; } .medium { color: #f39c12; } .low { color: #95a5a6; } .safe { color: #27ae60; } table { width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 14px; } th { background: #e74c3c; color: white; padding: 12px; text-align: left; } td { padding: 10px; border-bottom: 1px solid #ddd; vertical-align: top; } tr:hover { background: #f8f9fa; } .severity-badge { padding: 4px 8px; border-radius: 3px; color: white; font-weight: bold; font-size: 12px; } .severity-critical { background: #e74c3c; } .severity-high { background: #e67e22; } .severity-medium { background: #f39c12; } .severity-low { background: #95a5a6; } .footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; color: #7f8c8d; font-size: 12px; } .no-issues { color: #27ae60; font-weight: bold; font-size: 16px; padding: 20px; text-align: center; } </style> </head> <body> <div class="container"> <h1>Security Misconfiguration Report</h1> <p><strong>Computer:</strong> $($Findings.ComputerName) | <strong>Scan Date:</strong> $($Findings.ScanDate)</p> <p><strong>OS:</strong> $($Findings.OSVersion)</p> <div class="summary"> <h2>Summary</h2> <div class="metric"> <div class="metric-label">Total Findings</div> <div class="metric-value">$($Findings.Summary.TotalFindings)</div> </div> <div class="metric"> <div class="metric-label">Critical</div> <div class="metric-value critical">$($Findings.Summary.Critical)</div> </div> <div class="metric"> <div class="metric-label">High</div> <div class="metric-value high">$($Findings.Summary.High)</div> </div> <div class="metric"> <div class="metric-label">Medium</div> <div class="metric-value medium">$($Findings.Summary.Medium)</div> </div> <div class="metric"> <div class="metric-label">Low</div> <div class="metric-value low">$($Findings.Summary.Low)</div> </div> </div> <h2>Detected Misconfigurations</h2> "@ if ($Findings.Misconfigurations.Count -gt 0) { $html += @" <table> <tr> <th style="width: 80px;">Severity</th> <th style="width: 120px;">Category</th> <th style="width: 200px;">Finding</th> <th style="width: 150px;">Current Value</th> <th style="width: 150px;">Recommended</th> <th>Remediation</th> </tr> "@ foreach ($item in ($Findings.Misconfigurations | Sort-Object { switch ($_.Severity) { 'Critical' { 0 } 'High' { 1 } 'Medium' { 2 } 'Low' { 3 } } })) { $severityClass = "severity-$($item.Severity.ToLower())" $finding = $item.Finding -replace '<', '<' -replace '>', '>' $current = $item.CurrentValue -replace '<', '<' -replace '>', '>' $recommended = $item.RecommendedValue -replace '<', '<' -replace '>', '>' $remediation = $item.Remediation -replace '<', '<' -replace '>', '>' $html += @" <tr> <td><span class="severity-badge $severityClass">$($item.Severity)</span></td> <td>$($item.Category)</td> <td><strong>$finding</strong></td> <td>$current</td> <td>$recommended</td> <td><small>$remediation</small></td> </tr> "@ } $html += " </table>`n" } else { $html += " <p class=`"no-issues`">No security misconfigurations detected</p>`n" } $html += " `n <h2>Recommendations</h2>`n <ul>`n" foreach ($rec in $Findings.Recommendations) { $recEscaped = $rec -replace '<', '<' -replace '>', '>' $html += " <li>$recEscaped</li>`n" } $currentDate = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' $html += @" </ul> <div class="footer"> Generated by WindowsSecurityAudit Module | Security Misconfiguration Scan | $currentDate </div> </div> </body> </html> "@ return $html } |