v2.2.0 - Complete Security Hardening Framework (632 Settings)

This commit is contained in:
NexusOne23 2025-12-08 10:32:49 +01:00
commit ba364813ed
195 changed files with 43788 additions and 0 deletions

View file

@ -0,0 +1,73 @@
@{
# Module manifest for AdvancedSecurity
# Version
ModuleVersion = '2.2.0'
# Unique ID
GUID = 'e7f5a3d2-8c9b-4f1e-a6d3-9b2c8f4e5a1d'
# Author
Author = 'NexusOne23'
# Company
CompanyName = 'Open Source Project'
# Copyright
Copyright = '(c) 2025 NexusOne23. Licensed under GPL-3.0.'
# Description
Description = 'Advanced Security hardening beyond Microsoft Security Baseline: RDP hardening, WDigest protection, Admin Shares disable, Risky Ports/Services, Legacy TLS/WPAD/PSv2, SRP .lnk protection (CVE-2025-9491), Windows Update (3 simple GUI settings), Finger Protocol block, Wireless Display (Miracast) security. 38+ settings total with profile-based execution (Balanced/Enterprise/Maximum) and domain-safety checks plus full backup/restore.'
# Minimum PowerShell version
PowerShellVersion = '5.1'
# Root module
RootModule = 'AdvancedSecurity.psm1'
# Functions to export
FunctionsToExport = @(
'Invoke-AdvancedSecurity',
'Test-AdvancedSecurity',
'Restore-AdvancedSecuritySettings'
)
# Cmdlets to export
CmdletsToExport = @()
# Variables to export
VariablesToExport = @()
# Aliases to export
AliasesToExport = @()
# Private data
PrivateData = @{
PSData = @{
Tags = @('Security', 'Hardening', 'Windows11', 'Advanced', 'RDP', 'Credentials', 'NetworkSecurity')
LicenseUri = ''
ProjectUri = ''
ReleaseNotes = @'
v2.2.0 (2025-12-08)
- Production release of AdvancedSecurity module
- 49 advanced hardening settings implemented (was 36)
- NEW: Wireless Display (Miracast) security hardening
- Default: Block receiving projections + require PIN (all profiles)
- Optional: Complete disable (blocks sending, mDNS, ports 7236/7250)
- Prevents screen interception attacks from network attackers
- Profile-based execution (Balanced/Enterprise/Maximum)
- RDP NLA enforcement + optional complete disable
- WDigest credential protection (backwards compatible)
- Administrative shares disable (domain-aware)
- Risky firewall ports closure (LLMNR, NetBIOS, UPnP/SSDP)
- Risky network services stop (SSDPSRV, upnphost, lmhosts)
- Legacy TLS 1.0/1.1 disable
- WPAD auto-discovery disable
- PowerShell v2 removal
- Full backup/restore capability
- WhatIf mode and change log export
- Compliance testing function
'@
}
}
}

View file

@ -0,0 +1,65 @@
# AdvancedSecurity Module Loader
# Version: 2.2.0
# Description: Advanced Security Hardening - Beyond Microsoft Security Baseline
# Get module path
$ModulePath = $PSScriptRoot
# Load Private functions
$PrivateFunctions = @(
'Enable-RdpNLA',
'Set-WDigestProtection',
'Disable-AdminShares',
'Disable-RiskyPorts',
'Stop-RiskyServices',
'Disable-WPAD',
'Disable-LegacyTLS',
'Remove-PowerShellV2',
'Block-FingerProtocol',
'Set-SRPRules',
'Set-WindowsUpdate',
'Set-WirelessDisplaySecurity',
'Set-DiscoveryProtocolsSecurity',
'Set-FirewallShieldsUp',
'Set-IPv6Security',
'Test-RdpSecurity',
'Test-WDigest',
'Test-RiskyPorts',
'Test-RiskyServices',
'Test-AdminShares',
'Test-SRPCompliance',
'Test-WindowsUpdate',
'Test-LegacyTLS',
'Test-WPAD',
'Test-PowerShellV2',
'Test-FingerProtocol',
'Test-WirelessDisplaySecurity',
'Test-DiscoveryProtocolsSecurity',
'Test-FirewallShieldsUp',
'Test-IPv6Security',
'Backup-AdvancedSecuritySettings'
)
foreach ($function in $PrivateFunctions) {
$functionPath = Join-Path $ModulePath "Private\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Load Public functions
$PublicFunctions = @(
'Invoke-AdvancedSecurity',
'Test-AdvancedSecurity',
'Restore-AdvancedSecuritySettings'
)
foreach ($function in $PublicFunctions) {
$functionPath = Join-Path $ModulePath "Public\$function.ps1"
if (Test-Path $functionPath) {
. $functionPath
}
}
# Export only Public functions
Export-ModuleMember -Function $PublicFunctions

View file

@ -0,0 +1,116 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Administrative Shares Configuration",
"description": "Configuration for disabling administrative shares (C$, ADMIN$, etc.) to prevent lateral movement",
"version": "2.2.0",
"Administrative_Shares": {
"description": "Disable automatic creation and remove existing administrative shares",
"risk_level": "CRITICAL",
"attack_vectors": [
"Lateral movement in networks (WannaCry, NotPetya propagation)",
"Remote file access by attackers with stolen credentials",
"Pass-the-Hash attacks using admin shares",
"Automated malware propagation"
],
"shares_affected": {
"C$": "Root of C: drive",
"D$": "Root of D: drive (if exists)",
"E$": "Root of E: drive (if exists)",
"ADMIN$": "Windows directory (C:\\Windows)",
"IPC$": "Named pipes - CANNOT be removed (required by Windows)"
},
"registry_settings": {
"path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters",
"AutoShareWks": {
"description": "Disable automatic shares on Workstation (Home/Pro editions)",
"value": 0,
"type": "DWORD",
"default": 1
},
"AutoShareServer": {
"description": "Disable automatic shares on Server editions",
"value": 0,
"type": "DWORD",
"default": 1
}
},
"firewall_protection": {
"description": "Block SMB on Public network profile",
"rule_name": "Block Admin Shares (NoID Privacy)",
"direction": "Inbound",
"protocol": "TCP",
"local_port": 445,
"profile": "Public",
"action": "Block"
},
"domain_safety": {
"enabled": true,
"description": "Automatically detect domain-joined systems and skip unless -Force",
"check": "Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object PartOfDomain",
"warnings": [
"Group Policy management may be affected",
"SCCM/Management tools may require admin shares",
"Remote administration tools may stop working"
],
"force_required": true,
"enterprise_recommendation": "Test in staging environment before deployment"
}
},
"Profiles": {
"Balanced": {
"enabled": true,
"domain_check": true,
"force_required": false
},
"Enterprise": {
"enabled": "conditional",
"domain_check": true,
"force_required": true,
"note": "Auto-disabled for domain-joined systems unless -Force"
},
"Maximum": {
"enabled": true,
"domain_check": false,
"force_required": false,
"note": "Always enabled for maximum security"
}
},
"Impact": {
"positive": [
"Prevents lateral movement in case of credential theft",
"Stops automated ransomware propagation",
"Blocks Pass-the-Hash attack vectors using admin shares"
],
"negative": [
"Remote administration tools may not work",
"Group Policy remote management affected",
"Some enterprise monitoring tools may require admin shares",
"SCCM and similar tools may need explicit shares"
],
"recommendations": {
"home_users": "Recommended - high security benefit",
"enterprise": "Requires testing - may break management tools",
"workaround": "Create explicit shares for required management tools"
}
},
"Important_Notes": [
"REQUIRES REBOOT to prevent share recreation",
"Shares will NOT be recreated after reboot (if registry set)",
"IPC$ cannot be disabled (required by Windows)",
"File sharing via explicit shares still works",
"Can be restored by setting AutoShareWks/AutoShareServer = 1 + reboot"
],
"Compatibility": {
"windows_versions": ["Windows 10", "Windows 11", "Windows Server 2016+"],
"tested": "Windows 11 25H2 (Nov 16, 2025)"
}
}

View file

@ -0,0 +1,78 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Credential Protection Configuration",
"description": "Configuration for credential hardening including WDigest protection",
"version": "2.2.0",
"WDigest_Protection": {
"description": "Prevent WDigest from storing plaintext passwords in LSASS memory",
"enabled": true,
"deprecated_in": "Windows 11 24H2",
"status": "Deprecated in Win11 24H2+ but kept for backwards compatibility and defense-in-depth",
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\WDigest",
"settings": {
"UseLogonCredential": {
"description": "Control whether WDigest stores credentials in memory",
"value": 0,
"type": "DWORD",
"values": {
"0": "Secure - Do NOT store plaintext credentials in memory",
"1": "Insecure - Store plaintext credentials in memory (VULNERABLE!)"
},
"attack_prevention": [
"Prevents Mimikatz from dumping plaintext passwords",
"Prevents Windows Credential Editor (WCE) attacks",
"Prevents other memory-dumping credential theft tools"
],
"impact": "None - Modern systems (Win 8.1+) already default to 0"
}
},
"default_behavior": {
"Windows_7": 1,
"Windows_8": 1,
"Windows_8.1": 0,
"Windows_10": 0,
"Windows_11": 0,
"Windows_11_24H2_plus": "Setting ignored (deprecated)"
},
"rationale": {
"why_set_if_deprecated": [
"Protects older Windows versions (Win7/8/Server 2008/2012)",
"Protects early Win10/11 builds that may not be fully patched",
"Defense-in-depth: Explicit is better than implicit",
"Ensures compatibility in mixed environments",
"No negative impact on Win11 24H2+ (setting is ignored)"
]
},
"microsoft_advisory": {
"kb_article": "KB2871997",
"date": "May 2014",
"title": "Update to improve credentials protection and management",
"url": "https://support.microsoft.com/en-us/topic/microsoft-security-advisory-update-to-improve-credentials-protection-and-management-may-13-2014-93434251-04ac-b7f3-52aa-9f951c14b649",
"baseline_removal": {
"version": "Windows 11 25H2 Security Baseline",
"reason": "Engineering teams deprecated this policy in Windows 11 24H2",
"url": "https://techcommunity.microsoft.com/blog/microsoft-security-baselines/windows-11-version-25h2-security-baseline/4456231"
}
}
},
"Profiles": {
"Balanced": true,
"Enterprise": true,
"Maximum": true
},
"Compatibility": {
"windows_versions": ["All Windows versions"],
"notes": [
"Setting is ignored on Windows 11 24H2+ (deprecated)",
"No compatibility issues or breakage on any Windows version",
"Recommended for all profiles for defense-in-depth"
]
}
}

View file

@ -0,0 +1,20 @@
{
"Description": "Firewall Shields Up - Block all incoming connections on Public network",
"Purpose": "Extra protection in public WiFi networks (airports, cafes, hotels)",
"Note": "This goes BEYOND Microsoft Security Baseline",
"ShieldsUp": {
"description": "Block ALL incoming connections on Public profile, including allowed apps",
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\PublicProfile",
"value_name": "DoNotAllowExceptions",
"enabled_value": 1,
"disabled_value": 0,
"profiles": {
"Balanced": false,
"Enterprise": false,
"Maximum": true
},
"warning": "When enabled, apps like Teams, Discord, Zoom cannot receive incoming calls on Public networks",
"recommendation": "Enable only for maximum security (Maximum / air-gapped profile)"
}
}

View file

@ -0,0 +1,64 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RDP Hardening Configuration",
"description": "Configuration for RDP (Remote Desktop Protocol) hardening including NLA enforcement and optional complete disable",
"version": "2.2.0",
"NLA_Enforcement": {
"description": "Network Level Authentication (NLA) enforcement settings",
"enabled": true,
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp",
"settings": {
"UserAuthentication": {
"description": "Require NLA (Network Level Authentication) before session establishment",
"value": 1,
"type": "DWORD",
"attack_prevention": "Prevents brute-force attacks before login screen appears",
"impact": "Minimal - NLA is Windows 7+ standard. May affect pre-Vista RDP clients."
},
"SecurityLayer": {
"description": "Require SSL/TLS encryption for all RDP connections",
"value": 2,
"type": "DWORD",
"attack_prevention": "Forces SSL/TLS encryption, prevents plaintext RDP traffic",
"impact": "Minimal - SSL/TLS is standard since Windows Vista"
}
}
},
"Complete_Disable": {
"description": "Complete RDP disable for air-gapped/high-security environments",
"enabled_by_default": false,
"profiles": {
"Balanced": false,
"Enterprise": false,
"Maximum": "optional"
},
"registry_path": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server",
"settings": {
"fDenyTSConnections": {
"description": "Completely disable Remote Desktop",
"value": 1,
"type": "DWORD",
"attack_prevention": "Complete RDP attack surface removal",
"impact": "HIGH - Remote administration will not work. Windows automatically adjusts firewall rules."
}
},
"requires": {
"force_parameter": true,
"domain_check": true,
"warning": "This will completely disable RDP. Remote administration will not be possible."
}
},
"Compatibility": {
"windows_versions": ["Windows 10", "Windows 11", "Windows Server 2016+"],
"minimum_rdp_client": "Windows Vista+",
"notes": [
"NLA is standard since Windows Vista / Server 2008",
"Pre-Vista clients will not be able to connect with NLA enforcement",
"Complete disable affects all remote management via RDP",
"Domain-joined systems should NOT disable RDP without explicit -Force"
]
}
}

View file

@ -0,0 +1,85 @@
{
"Description": "Software Restriction Policies (SRP) for CVE-2025-9491 Mitigation",
"Documentation": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-9491",
"CVE": "CVE-2025-9491",
"Threat": "Windows LNK Remote Code Execution",
"Status": "Zero-Day, Actively Exploited since 2017",
"Severity": "High (CVSS 7.0)",
"SRPConfiguration": {
"DefaultLevel": 262144,
"Description": "Unrestricted - Allow all programs except explicitly blocked",
"TransparentEnabled": 1,
"ExecutableTypes": [
".ADE", ".ADP", ".BAS", ".BAT", ".CHM", ".CMD", ".COM", ".CPL", ".CRT",
".EXE", ".HLP", ".HTA", ".INF", ".INS", ".ISP", ".LNK", ".MDB", ".MDE",
".MSC", ".MSI", ".MSP", ".MST", ".OCX", ".PCD", ".PIF", ".REG", ".SCR",
".SHS", ".URL", ".VB", ".WSC", ".WSF", ".WSH"
]
},
"PathRules": [
{
"Name": "Block LNK from Outlook Temp",
"Path": "%LOCALAPPDATA%\\Temp\\*.lnk",
"SecurityLevel": 0,
"Description": "Blocks .lnk files from Outlook email attachments to prevent CVE-2025-9491 exploitation. Outlook saves attachments to %LOCALAPPDATA%\\Temp\\Content.Outlook\\ before execution.",
"SaferFlags": 0,
"Enabled": true,
"AttackVector": "Email attachments (malicious.lnk via Outlook)",
"Impact": "Prevents execution of .lnk files from email attachments. Legitimate shortcuts from Start Menu/Desktop/Taskbar still work (different paths)."
},
{
"Name": "Block LNK from Downloads",
"Path": "%USERPROFILE%\\Downloads\\*.lnk",
"SecurityLevel": 0,
"Description": "Blocks .lnk files from browser Downloads folder to prevent CVE-2025-9491 exploitation from web downloads.",
"SaferFlags": 0,
"Enabled": true,
"AttackVector": "Browser downloads (malicious.lnk from web)",
"Impact": "Prevents execution of .lnk files downloaded from internet. Move .lnk to another location to execute if needed."
}
],
"Windows11BugFix": {
"Description": "Windows 11 has a bug where SRP is disabled by presence of certain keys in HKLM\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp",
"Action": "Remove RuleCount and LastWriteTime keys",
"RegistryPath": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp",
"KeysToRemove": ["RuleCount", "LastWriteTime"],
"Reason": "These keys cause SRP to be ignored on Windows 11. Removing them re-enables SRP functionality."
},
"RegistryPaths": {
"PolicyRoot": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers",
"PathRules": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\Safer\\CodeIdentifiers\\0\\Paths",
"Win11BugFix": "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Srp\\Gp"
},
"SecurityLevels": {
"Disallowed": 0,
"Unrestricted": 262144,
"Description": "0 = Block execution, 262144 = Allow execution"
},
"SafeScenarios": [
"Start Menu shortcuts (C:\\ProgramData\\Microsoft\\Windows\\Start Menu)",
"Desktop shortcuts (C:\\Users\\<user>\\Desktop)",
"Taskbar shortcuts (pinned applications)",
"Program Files shortcuts (C:\\Program Files)",
"System shortcuts (C:\\Windows)"
],
"BlockedScenarios": [
"Outlook email attachments (%LOCALAPPDATA%\\Temp\\Content.Outlook)",
"Browser downloads (%USERPROFILE%\\Downloads)",
"Temporary Internet Files",
"Other Temp locations matching patterns"
],
"Testing": {
"VerifyBlockedPath": "%USERPROFILE%\\Downloads\\test.lnk",
"ExpectedResult": "Execution blocked with 'This program is blocked by group policy' message",
"VerifySafePath": "%USERPROFILE%\\Desktop\\test.lnk",
"ExpectedResult2": "Execution allowed (Desktop not in blocked path list)"
}
}

View file

@ -0,0 +1,57 @@
{
"Description": "Simple Windows Update Configuration - MS Best Practice (GUI Settings Only)",
"Documentation": "Matches Windows Settings > Windows Update > Advanced options",
"Purpose": "Enable immediate updates from Microsoft using Windows built-in settings",
"Settings": {
"1_ReceiveUpdatesImmediately": {
"Name": "Get the latest updates as soon as they're available",
"RegistryPath": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate",
"Values": {
"AllowOptionalContent": {
"Type": "DWord",
"Value": 1,
"Description": "Policy: enable optional content & configuration updates immediately (grays out GUI toggle)"
},
"SetAllowOptionalContent": {
"Type": "DWord",
"Value": 1,
"Description": "Policy: enforce AllowOptionalContent setting (managed by organization)"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Get the latest updates as soon as they're available"
},
"2_MicrosoftUpdate": {
"Name": "Receive updates for other Microsoft products",
"RegistryPath": "HKLM:\\SOFTWARE\\Microsoft\\WindowsUpdate\\UX\\Settings",
"Values": {
"AllowMUUpdateService": {
"Type": "DWord",
"Value": 1,
"Description": "Get updates for Office, drivers, and other Microsoft products with Windows Update"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Receive updates for other Microsoft products"
},
"3_DeliveryOptimization": {
"Name": "Downloads from other devices (DISABLED for privacy)",
"RegistryPath": "HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\DeliveryOptimization",
"Values": {
"DODownloadMode": {
"Type": "DWord",
"Value": 0,
"Description": "0 = HTTP only (Microsoft servers), no P2P, no LAN sharing"
}
},
"GUIPath": "Settings > Windows Update > Advanced options > Delivery Optimization > Allow downloads from other devices = OFF"
}
},
"TotalRegistryKeys": 4,
"MSBestPractice": "These are the EXACT settings shown in Windows Settings GUI - no hidden schedules, no auto-reboot config",
"UserControl": "User keeps full control over installation timing via Windows Settings (except Setting 1 is enforced by policy if enabled)",
"NoInteractivePrompt": "No mode selection needed - simple ON/ON/OFF configuration",
"CRITICAL_NOTE": "Setting 1 uses Policies\\Microsoft\\Windows\\WindowsUpdate (AllowOptionalContent/SetAllowOptionalContent) and will appear as 'managed by organization'. Setting 2 MUST use UX\\Settings path (NOT Policies path) to avoid locking the Microsoft Update toggle."
}

View file

@ -0,0 +1,380 @@
function Backup-AdvancedSecuritySettings {
<#
.SYNOPSIS
Create a comprehensive backup of all Advanced Security settings
.DESCRIPTION
Backs up all registry keys, services, firewall rules, and Windows features
that will be modified by the AdvancedSecurity module.
This is called automatically by Invoke-AdvancedSecurity before applying changes.
.EXAMPLE
Backup-AdvancedSecuritySettings
.NOTES
Uses the Core/Rollback.ps1 backup system
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Creating comprehensive backup of Advanced Security settings..." -Module "AdvancedSecurity"
$backupCount = 0
# Start module backup session
$backupSession = Start-ModuleBackup -ModuleName "AdvancedSecurity"
if (-not $backupSession) {
Write-Log -Level ERROR -Message "Failed to start backup session" -Module "AdvancedSecurity"
return $false
}
# 1. RDP Settings
Write-Log -Level DEBUG -Message "Backing up RDP settings..." -Module "AdvancedSecurity"
$rdpBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -BackupName "RDP_Settings"
if ($rdpBackup) { $backupCount++ }
# CRITICAL: Create JSON backup for RDP (Rollback fallback)
# .reg import often fails for RDP keys due to permissions, so we need values for PowerShell restore
try {
$rdpData = @{}
# System Settings
$systemPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
if (Test-Path $systemPath) {
$val = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($val) { $rdpData["System_fDenyTSConnections"] = $val.fDenyTSConnections }
}
# Policy Settings
$policyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
if (Test-Path $policyPath) {
$val1 = Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue
if ($val1) { $rdpData["Policy_UserAuthentication"] = $val1.UserAuthentication }
$val2 = Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue
if ($val2) { $rdpData["Policy_SecurityLayer"] = $val2.SecurityLayer }
}
if ($rdpData.Count -gt 0) {
$rdpJson = $rdpData | ConvertTo-Json
$rdpJsonBackup = Register-Backup -Type "AdvancedSecurity" -Data $rdpJson -Name "RDP_Hardening"
if ($rdpJsonBackup) {
Write-Log -Level DEBUG -Message "Created RDP JSON backup for rollback fallback" -Module "AdvancedSecurity"
$backupCount++
}
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create RDP JSON backup: $_" -Module "AdvancedSecurity"
}
# 2. WDigest Settings
Write-Log -Level DEBUG -Message "Backing up WDigest settings..." -Module "AdvancedSecurity"
$wdigestBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" -BackupName "WDigest_Settings"
if ($wdigestBackup) { $backupCount++ }
# Discovery Protocol Settings (mDNS resolver)
Write-Log -Level DEBUG -Message "Backing up discovery protocol settings (mDNS)" -Module "AdvancedSecurity"
$mdnsBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters" -BackupName "DiscoveryProtocols_DnscacheParameters"
if ($mdnsBackup) { $backupCount++ }
# 3. Admin Shares Settings
Write-Log -Level DEBUG -Message "Backing up Admin Shares settings..." -Module "AdvancedSecurity"
$adminSharesBackup = Backup-RegistryKey -KeyPath "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" -BackupName "AdminShares_Settings"
if ($adminSharesBackup) { $backupCount++ }
# 4. TLS Settings
Write-Log -Level DEBUG -Message "Backing up TLS settings..." -Module "AdvancedSecurity"
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
$tlsBackup = Backup-RegistryKey -KeyPath $regPath -BackupName "TLS_${version}_${component}".Replace(" ", "_").Replace(".", "")
if ($tlsBackup) { $backupCount++ }
}
}
# 5. WPAD Settings (3 paths: WinHttp for official MS key, Wpad for legacy, Internet Settings for AutoDetect)
Write-Log -Level DEBUG -Message "Backing up WPAD settings..." -Module "AdvancedSecurity"
$wpadPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp", # Official MS DisableWpad key
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad", # Legacy WpadOverride
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" # AutoDetect
)
foreach ($wpadPath in $wpadPaths) {
$pathName = $wpadPath.Split('\')[-1]
$wpadBackup = Backup-RegistryKey -KeyPath $wpadPath -BackupName "WPAD_${pathName}"
if ($wpadBackup) { $backupCount++ }
}
# CRITICAL: Create JSON backup for WPAD (Rollback fallback) - all paths combined
try {
$wpadData = @{}
foreach ($wpadPath in $wpadPaths) {
if (Test-Path $wpadPath) {
$wpadProps = Get-ItemProperty -Path $wpadPath -ErrorAction SilentlyContinue
# Capture all relevant properties in format expected by Rollback.ps1
# Format: "FullPath\ValueName" = Value
foreach ($prop in $wpadProps.PSObject.Properties) {
if ($prop.Name -notin @('PSPath','PSParentPath','PSChildName','PSDrive','PSProvider')) {
$fullKey = "$wpadPath\$($prop.Name)"
$wpadData[$fullKey] = $prop.Value
}
}
}
}
if ($wpadData.Count -gt 0) {
$wpadJson = $wpadData | ConvertTo-Json
$wpadJsonBackup = Register-Backup -Type "AdvancedSecurity" -Data $wpadJson -Name "WPAD"
if ($wpadJsonBackup) {
Write-Log -Level DEBUG -Message "Created WPAD JSON backup for rollback fallback ($($wpadData.Count) values)" -Module "AdvancedSecurity"
$backupCount++
}
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create WPAD JSON backup: $_" -Module "AdvancedSecurity"
}
# 6. Services (including WiFi Direct for Wireless Display and WS-Discovery)
Write-Log -Level DEBUG -Message "Backing up risky services state..." -Module "AdvancedSecurity"
# Note: Computer Browser (Browser) is deprecated in Win10/11 - not included
$services = @("SSDPSRV", "upnphost", "lmhosts", "WFDSConMgrSvc", "FDResPub", "fdPHost")
foreach ($svc in $services) {
$svcBackup = Backup-ServiceConfiguration -ServiceName $svc
if ($svcBackup) { $backupCount++ }
}
# 7. PowerShell v2 Feature State
Write-Log -Level DEBUG -Message "Backing up PowerShell v2 feature state..." -Module "AdvancedSecurity"
# Canonical detection: use Windows Optional Feature state
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature -or $psv2Feature.State -ne 'Enabled') {
# Feature not present or not enabled nothing to back up
Write-Log -Level INFO -Message "PowerShell v2 optional feature not enabled/present - skipping feature backup" -Module "AdvancedSecurity"
}
else {
$psv2Data = @{
FeatureName = $psv2Feature.FeatureName
State = $psv2Feature.State
DetectionMethod = "WindowsOptionalFeature"
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
} | ConvertTo-Json
$psv2Backup = Register-Backup -Type "WindowsFeature" -Data $psv2Data -Name "PowerShellV2"
if ($psv2Backup) { $backupCount++ }
}
# 8. Firewall Rules Snapshot
Write-Host ""
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host " FIREWALL RULES BACKUP - PLEASE WAIT" -ForegroundColor Cyan
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host " Creating snapshot for risky ports..." -ForegroundColor White
Write-Host " Ports: 79, 137-139, 1900, 2869, 5355, 3702, 5353, 5357, 5358" -ForegroundColor Gray
Write-Host ""
Write-Host " [!] This operation takes 60-120 seconds" -ForegroundColor Yellow
Write-Host " System is working - do not interrupt!" -ForegroundColor Yellow
Write-Host " ============================================" -ForegroundColor Cyan
Write-Host ""
Write-Log -Level INFO -Message "Backing up firewall rules snapshot for risky ports (79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358)..." -Module "AdvancedSecurity"
$firewallRules = Get-NetFirewallRule | Where-Object {
$portFilter = $_ | Get-NetFirewallPortFilter
(($portFilter.LocalPort -in @(79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358)) -or
($portFilter.RemotePort -in @(79, 137, 138, 139, 1900, 2869, 5355, 3702, 5353, 5357, 5358))) -and
($_.Direction -eq 'Inbound' -or $_.Direction -eq 'Outbound')
} | Select-Object Name, DisplayName, Enabled, Direction, Action
$firewallData = @{
Rules = $firewallRules
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
RuleCount = $firewallRules.Count
} | ConvertTo-Json -Depth 10
$firewallBackup = Register-Backup -Type "Firewall_Rules" -Data $firewallData -Name "RiskyPorts_Firewall"
if ($firewallBackup) { $backupCount++ }
Write-Host " [OK] Firewall rules backup completed ($($firewallRules.Count) rules processed)" -ForegroundColor Green
Write-Host ""
# 9. SMB Shares Snapshot
Write-Log -Level DEBUG -Message "Backing up SMB shares snapshot..." -Module "AdvancedSecurity"
# Check if LanmanServer service is running (required for Get-SmbShare)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
if (-not $serverService -or $serverService.Status -ne 'Running') {
Write-Log -Level INFO -Message "LanmanServer service is not running - no SMB shares to backup" -Module "AdvancedSecurity"
$adminShares = @()
}
else {
try {
$adminShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' } |
Select-Object Name, Path, Description
}
catch {
Write-Log -Level INFO -Message "Could not query SMB shares: $($_.Exception.Message)" -Module "AdvancedSecurity"
$adminShares = @()
}
}
$sharesData = @{
Shares = $adminShares
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ShareCount = $adminShares.Count
} | ConvertTo-Json -Depth 10
$sharesBackup = Register-Backup -Type "SMB_Shares" -Data $sharesData -Name "AdminShares"
if ($sharesBackup) { $backupCount++ }
$netbiosAdapters = @()
try {
$netbiosAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE" -ErrorAction SilentlyContinue
}
catch {
$netbiosAdapters = @()
}
if ($netbiosAdapters) {
$netbiosSnapshot = @()
foreach ($adapter in $netbiosAdapters) {
$netbiosSnapshot += [PSCustomObject]@{
Description = $adapter.Description
Index = $adapter.Index
TcpipNetbiosOptions = $adapter.TcpipNetbiosOptions
}
}
if ($netbiosSnapshot.Count -gt 0) {
$netbiosJson = $netbiosSnapshot | ConvertTo-Json -Depth 5
$netbiosBackup = Register-Backup -Type "AdvancedSecurity" -Data $netbiosJson -Name "NetBIOS_Adapters"
if ($netbiosBackup) { $backupCount++ }
}
}
# 10. Windows Update Settings (3 simple GUI settings)
Write-Log -Level DEBUG -Message "Backing up Windows Update settings..." -Module "AdvancedSecurity"
# Setting 1: Get latest updates immediately
$wuUXBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" -BackupName "WindowsUpdate_UX_Settings"
if ($wuUXBackup) { $backupCount++ }
# Setting 1 Policy: Windows Update optional content/config updates
$wuPoliciesBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" -BackupName "WindowsUpdate_Policies"
if ($wuPoliciesBackup) { $backupCount++ }
# Setting 2: Microsoft Update for other products (moved to UX\Settings - same as Setting 1)
# No separate backup needed - already backed up in WindowsUpdate_UX_Settings
# Setting 3: Delivery Optimization
$wuDOBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization" -BackupName "WindowsUpdate_DeliveryOptimization"
if ($wuDOBackup) { $backupCount++ }
# 11. SRP (Software Restriction Policies) Settings
Write-Log -Level DEBUG -Message "Backing up SRP settings..." -Module "AdvancedSecurity"
$srpBackup = Backup-RegistryKey -KeyPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers" -BackupName "SRP_Settings"
if ($srpBackup) { $backupCount++ }
# 12. CRITICAL: Create comprehensive JSON Pre-State Snapshot (counter Registry tattooing)
# This captures EXACT state of ALL AdvancedSecurity registry keys before hardening
Write-Log -Level INFO -Message "Creating AdvancedSecurity registry pre-state snapshot (JSON)..." -Module "AdvancedSecurity"
$preStateSnapshot = @()
# All registry keys that AdvancedSecurity modifies
$allAdvSecKeys = @(
"HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest",
"HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server",
"HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client",
"HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp", # Official MS DisableWpad key
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad", # Legacy WpadOverride
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", # AutoDetect
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate",
"HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\Safer\CodeIdentifiers",
"HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect", # Wireless Display / Miracast
"HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile", # Firewall Shields Up
"HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" # IPv6 disable (mitm6 mitigation)
)
foreach ($keyPath in $allAdvSecKeys) {
if (Test-Path $keyPath) {
try {
# Get all properties for this key
$properties = Get-ItemProperty -Path $keyPath -ErrorAction Stop
$propertyNames = $properties.PSObject.Properties.Name | Where-Object {
$_ -notin @('PSPath', 'PSParentPath', 'PSChildName', 'PSProvider', 'PSDrive')
}
foreach ($propName in $propertyNames) {
$propValue = $properties.$propName
# Get value type
try {
$propType = (Get-Item $keyPath).GetValueKind($propName)
}
catch {
$propType = "String" # Default fallback
}
$preStateSnapshot += [PSCustomObject]@{
Path = $keyPath
Name = $propName
Value = $propValue
Type = $propType.ToString()
Exists = $true
}
}
}
catch {
Write-Log -Level DEBUG -Message "Could not read properties from $keyPath : $_" -Module "AdvancedSecurity"
}
}
# If key doesn't exist, we don't add it to snapshot (only existing values are tracked)
}
# Save JSON snapshot
try {
$snapshotJson = $preStateSnapshot | ConvertTo-Json -Depth 5
$result = Register-Backup -Type "AdvancedSecurity" -Data $snapshotJson -Name "AdvancedSecurity_PreState"
if ($result) {
$backupCount++
Write-Log -Level SUCCESS -Message "AdvancedSecurity pre-state snapshot created ($($preStateSnapshot.Count) registry values)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level WARNING -Message "Failed to create AdvancedSecurity pre-state snapshot: $_" -Module "AdvancedSecurity"
}
Write-Log -Level SUCCESS -Message "Advanced Security backup completed: $backupCount items backed up" -Module "AdvancedSecurity"
return $backupCount
}
catch {
Write-Log -Level ERROR -Message "Failed to backup Advanced Security settings: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,107 @@
function Block-FingerProtocol {
<#
.SYNOPSIS
Blocks outbound connections to TCP Port 79 (Finger protocol) via Windows Firewall
.DESCRIPTION
Creates a Windows Firewall rule to block all outbound connections to TCP port 79,
preventing abuse of the finger.exe command in ClickFix malware campaigns.
THREAT: ClickFix attacks use finger.exe to retrieve commands from remote servers
on port 79, which are then piped to cmd.exe for execution.
MITIGATION: Block outbound port 79 to prevent finger.exe from reaching C2 servers.
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Block-FingerProtocol
Blocks outbound finger protocol connections
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
REFERENCES:
- https://www.bleepingcomputer.com/news/security/decades-old-finger-protocol-abused-in-clickfix-malware-attacks/
- https://redteamnews.com/threat-intelligence/clickfix-malware-campaigns-resurrect-decades-old-finger-protocol-for-command-retrieval/
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$ruleName = "NoID Privacy - Block Finger Protocol (Port 79)"
Write-Log -Level INFO -Message "Checking for existing Finger protocol block rule..." -Module "AdvancedSecurity"
# Check if rule already exists
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Log -Level INFO -Message "Finger protocol block rule already exists" -Module "AdvancedSecurity"
# Show user that protection is already active
Write-Host ""
Write-Host "Finger Protocol Block: Already Protected" -ForegroundColor Green
Write-Host " Rule: $ruleName" -ForegroundColor Gray
Write-Host " Status: Active (Outbound TCP port 79 blocked)" -ForegroundColor Gray
Write-Host " Protection: ClickFix malware using finger.exe" -ForegroundColor Gray
Write-Host ""
return $true
}
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would create firewall rule to block outbound TCP port 79" -Module "AdvancedSecurity"
return $true
}
Write-Log -Level INFO -Message "Creating Windows Firewall rule to block outbound finger protocol (TCP 79)..." -Module "AdvancedSecurity"
# Create outbound firewall rule
$ruleParams = @{
DisplayName = $ruleName
Description = "Blocks outbound connections to TCP port 79 (Finger protocol) to prevent ClickFix malware attacks. The finger.exe command is abused to retrieve malicious commands from remote servers."
Direction = "Outbound"
Action = "Block"
Protocol = "TCP"
RemotePort = 79
Profile = "Any"
Enabled = "True"
ErrorAction = "Stop"
}
New-NetFirewallRule @ruleParams | Out-Null
# Verify rule was created
$verifyRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($verifyRule) {
Write-Log -Level SUCCESS -Message "Finger protocol (TCP port 79) outbound connections blocked" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "ClickFix malware campaigns using finger.exe are now mitigated" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Firewall Rule Created:" -ForegroundColor Green
Write-Host "Name: $ruleName" -ForegroundColor Gray
Write-Host "Blocks: Outbound TCP port 79 (Finger protocol)" -ForegroundColor Gray
Write-Host "Protection: ClickFix malware using finger.exe" -ForegroundColor Gray
Write-Host ""
return $true
}
else {
Write-Log -Level ERROR -Message "Firewall rule creation failed - verification unsuccessful" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to create finger protocol block rule: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,219 @@
function Disable-AdminShares {
<#
.SYNOPSIS
Disable administrative shares (C$, ADMIN$, etc.) to prevent lateral movement
.DESCRIPTION
Disables the automatic creation of administrative shares and removes existing shares.
Administrative shares (C$, D$, ADMIN$) are used by attackers for:
- Lateral movement (WannaCry, NotPetya propagation)
- Remote file access with stolen credentials
- Pass-the-Hash attacks
- Automated malware propagation
CRITICAL: Includes domain-safety check. On domain-joined systems, admin shares
are often required for Group Policy, SCCM, and remote management tools.
REQUIRES REBOOT to prevent share recreation.
.PARAMETER Force
Force disable even on domain-joined systems (NOT RECOMMENDED for enterprise!)
.EXAMPLE
Disable-AdminShares
Disables admin shares with domain-safety check
.EXAMPLE
Disable-AdminShares -Force
Forces disable even on domain-joined systems (DANGEROUS!)
.NOTES
Impact:
- Home/Workgroup: Highly recommended
- Enterprise Domain: May break management tools - TEST FIRST!
- IPC$ cannot be removed (required by Windows)
Shares will NOT be recreated after reboot (if registry values set to 0).
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$Force
)
try {
Write-Log -Level INFO -Message "Configuring administrative shares disable..." -Module "AdvancedSecurity"
# CRITICAL: Check if system is domain-joined
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
if ($computerSystem.PartOfDomain -and -not $Force) {
Write-Log -Level WARNING -Message "Domain-joined system detected. Admin shares disable SKIPPED." -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Admin shares are often required for:" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Group Policy management" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - SCCM/Management tools" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Remote administration" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Use -Force to override (NOT RECOMMENDED!)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Yellow
Write-Host " DOMAIN-JOINED SYSTEM DETECTED" -ForegroundColor Yellow
Write-Host "================================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Administrative shares are often required for:" -ForegroundColor White
Write-Host " - Group Policy remote management" -ForegroundColor Gray
Write-Host " - SCCM and other management tools" -ForegroundColor Gray
Write-Host " - Remote administration via WMI/PowerShell" -ForegroundColor Gray
Write-Host ""
Write-Host "Skipping admin shares disable to prevent breakage." -ForegroundColor Green
Write-Host "Use -DisableAdminShares -Force to override (NOT RECOMMENDED)." -ForegroundColor Red
Write-Host ""
return $true # Not an error, just skipped
}
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
# Check if LanmanServer service is running (required for Get-SmbShare)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
$serviceRunning = $serverService -and $serverService.Status -eq 'Running'
# Backup current shares and registry settings
Write-Log -Level INFO -Message "Backing up current administrative shares..." -Module "AdvancedSecurity"
if (-not $serviceRunning) {
# Server service not running - admin shares are already effectively disabled
Write-Log -Level INFO -Message "LanmanServer service is not running - admin shares already disabled" -Module "AdvancedSecurity"
$currentShares = @()
}
else {
try {
$currentShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' } |
Select-Object Name, Path, Description
}
catch {
Write-Log -Level INFO -Message "Could not query SMB shares: $($_.Exception.Message)" -Module "AdvancedSecurity"
$currentShares = @()
}
}
$backupData = @{
Shares = $currentShares
AutoShareWks = (Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue).AutoShareWks
AutoShareServer = (Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue).AutoShareServer
DomainJoined = $computerSystem.PartOfDomain
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "AdminShares_Settings" -Data $backupJson -Name "AdminShares_Disable"
Write-Log -Level INFO -Message "Backed up $($currentShares.Count) administrative shares" -Module "AdvancedSecurity"
# Disable automatic creation
Write-Log -Level INFO -Message "Disabling automatic administrative share creation..." -Module "AdvancedSecurity"
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Disable for Workstation (Home/Pro)
$existing = Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "AutoShareWks" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "AutoShareWks" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled AutoShareWks (Workstation shares)" -Module "AdvancedSecurity"
# Disable for Server editions
$existing = Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "AutoShareServer" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "AutoShareServer" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled AutoShareServer (Server edition shares)" -Module "AdvancedSecurity"
# Remove existing shares
Write-Log -Level INFO -Message "Removing existing administrative shares..." -Module "AdvancedSecurity"
$removedCount = 0
$skippedShares = @()
foreach ($share in $currentShares) {
try {
Remove-SmbShare -Name $share.Name -Force -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Removed share: $($share.Name) ($($share.Path))" -Module "AdvancedSecurity"
$removedCount++
}
catch {
# ADMIN$ and C$ cannot be removed while system is running (expected behavior)
# They will NOT be recreated after reboot due to registry settings
Write-Log -Level INFO -Message "Share $($share.Name) protected by system (will not be recreated after reboot)" -Module "AdvancedSecurity"
$skippedShares += $share.Name
}
}
if ($skippedShares.Count -gt 0) {
Write-Log -Level INFO -Message "System-protected shares: $($skippedShares -join ', ') - Will NOT be recreated after reboot" -Module "AdvancedSecurity"
}
Write-Log -Level SUCCESS -Message "Removed $removedCount administrative shares, $($skippedShares.Count) protected by system" -Module "AdvancedSecurity"
# Add firewall protection for Public networks
Write-Log -Level INFO -Message "Adding firewall protection for SMB on Public networks..." -Module "AdvancedSecurity"
$firewallRuleName = "Block Admin Shares - NoID Privacy"
# Check if rule already exists
$existingRule = Get-NetFirewallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
if ($existingRule) {
Write-Log -Level INFO -Message "Firewall rule already exists, updating..." -Module "AdvancedSecurity"
Remove-NetFirewallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
}
# Create new firewall rule
New-NetFirewallRule -DisplayName $firewallRuleName `
-Direction Inbound `
-Protocol TCP `
-LocalPort 445 `
-Profile Public `
-Action Block `
-ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Firewall rule created: Block SMB (port 445) on Public networks" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " ADMINISTRATIVE SHARES DISABLED" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Registry settings:" -ForegroundColor White
Write-Host " AutoShareWks: 0 (Disabled)" -ForegroundColor Gray
Write-Host " AutoShareServer: 0 (Disabled)" -ForegroundColor Gray
Write-Host ""
Write-Host "Removed shares: $removedCount" -ForegroundColor White
if ($skippedShares.Count -gt 0) {
Write-Host "Protected shares: $($skippedShares -join ', ') (cannot be removed while running)" -ForegroundColor Gray
}
Write-Host "Firewall: SMB blocked on Public networks" -ForegroundColor White
Write-Host ""
Write-Host "IMPORTANT: REBOOT REQUIRED" -ForegroundColor Yellow
$exampleShares = if ($skippedShares.Count -gt 0) { $skippedShares -join ', ' } else { 'C$, ADMIN$' }
Write-Host "All admin shares (including $exampleShares) will NOT be recreated after reboot." -ForegroundColor Green
Write-Host ""
Write-Host "Note: IPC$ cannot be removed (required by Windows)" -ForegroundColor Gray
Write-Host "Note: Explicit file shares will still work" -ForegroundColor Gray
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable administrative shares: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,73 @@
function Disable-LegacyTLS {
<#
.SYNOPSIS
Disable legacy TLS 1.0 and TLS 1.1
.DESCRIPTION
Disables TLS 1.0 and TLS 1.1 for both Client and Server to prevent
BEAST, CRIME, and other attacks.
Attack Prevention: BEAST, CRIME, weak cipher suites
Impact: May break old internal web applications that haven't been updated
.EXAMPLE
Disable-LegacyTLS
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Disabling legacy TLS 1.0 and TLS 1.1..." -Module "AdvancedSecurity"
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
$setCount = 0
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
# Create path if needed
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Disable TLS version
$existing = Get-ItemProperty -Path $regPath -Name "Enabled" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "Enabled" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "Enabled" -Value 0 -PropertyType DWord -Force | Out-Null
}
$existing = Get-ItemProperty -Path $regPath -Name "DisabledByDefault" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $regPath -Name "DisabledByDefault" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "Disabled $version $component" -Module "AdvancedSecurity"
$setCount += 2
}
}
Write-Log -Level SUCCESS -Message "Legacy TLS disabled ($setCount registry keys set)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Legacy TLS Disabled:" -ForegroundColor Green
Write-Host " TLS 1.0: Client + Server" -ForegroundColor Gray
Write-Host " TLS 1.1: Client + Server" -ForegroundColor Gray
Write-Host ""
Write-Host "WARNING: Old web applications may not work!" -ForegroundColor Yellow
Write-Host "Only TLS 1.2 and TLS 1.3 are now allowed." -ForegroundColor Gray
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable legacy TLS: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,245 @@
function Disable-RiskyPorts {
<#
.SYNOPSIS
Disable risky firewall ports (LLMNR, NetBIOS, UPnP/SSDP)
.DESCRIPTION
Closes firewall ports that are commonly exploited for MITM attacks,
network enumeration, and credential theft:
- LLMNR (Port 5355) - HIGH RISK: Responder poisoning, credential theft
- NetBIOS (Port 137-139) - MEDIUM RISK: Network enumeration
- UPnP/SSDP (Port 1900, 2869) - MEDIUM RISK: Port forwarding vulnerabilities
Uses language-independent port-based filtering to avoid DisplayName issues.
.PARAMETER SkipUPnP
Skip disabling UPnP/SSDP ports (for users who need DLNA/media streaming)
.EXAMPLE
Disable-RiskyPorts
Disables all risky firewall ports including UPnP
.EXAMPLE
Disable-RiskyPorts -SkipUPnP
Disables LLMNR and NetBIOS but keeps UPnP enabled
.NOTES
Defense in Depth: Security Baseline disables protocols via registry,
but firewall rules may still be active. This function closes the ports
at the firewall level for additional protection.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$SkipUPnP
)
try {
Write-Log -Level INFO -Message "Disabling risky firewall ports..." -Module "AdvancedSecurity"
$disabledRules = 0
$errors = @()
# PERFORMANCE: Get all firewall rules ONCE and cache port filters
Write-Log -Level INFO -Message "Loading firewall rules for analysis..." -Module "AdvancedSecurity"
$allRules = Get-NetFirewallRule | Where-Object { $_.Direction -eq 'Inbound' -and $_.Enabled -eq $true }
# Pre-fetch port filters to avoid repeated Get-NetFirewallPortFilter calls
# NOTE: We cache both the rule and its ports so we can later filter ONLY
# ALLOW rules for disabling. NoID block rules must remain enabled.
$rulesWithPorts = @()
foreach ($rule in $allRules) {
$portFilter = $rule | Get-NetFirewallPortFilter -ErrorAction SilentlyContinue
if ($portFilter) {
$rulesWithPorts += [PSCustomObject]@{
Rule = $rule
LocalPort = $portFilter.LocalPort
RemotePort = $portFilter.RemotePort
}
}
}
Write-Log -Level INFO -Message "Analyzed $($rulesWithPorts.Count) firewall rules with port filters" -Module "AdvancedSecurity"
# Backup firewall rules
Write-Log -Level INFO -Message "Backing up firewall rules..." -Module "AdvancedSecurity"
$backupData = @{
FirewallRules = $allRules | Select-Object Name, DisplayName, Enabled, Direction, Action
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Firewall_Rules" -Data $backupJson -Name "RiskyPorts_Firewall"
# 1. LLMNR (Port 5355 UDP) - HIGH RISK
Write-Log -Level INFO -Message "Disabling LLMNR firewall rules (Port 5355)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$llmnrRules = $rulesWithPorts | Where-Object {
($_.LocalPort -eq 5355 -or $_.RemotePort -eq 5355) -and $_.Rule.Action -eq 'Allow'
} | Select-Object -ExpandProperty Rule
foreach ($rule in $llmnrRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled LLMNR rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($llmnrRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active LLMNR rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($llmnrRules.Count) LLMNR firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "LLMNR: $_"
Write-Log -Level WARNING -Message "Failed to disable some LLMNR rules: $_" -Module "AdvancedSecurity"
}
# 2. NetBIOS (Port 137-139) - MEDIUM RISK
Write-Log -Level INFO -Message "Disabling NetBIOS firewall rules (Port 137-139)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$netbiosRules = $rulesWithPorts | Where-Object {
($_.LocalPort -in @(137, 138, 139)) -or ($_.RemotePort -in @(137, 138, 139))
} | Where-Object { $_.Rule.Action -eq 'Allow' } | Select-Object -ExpandProperty Rule
foreach ($rule in $netbiosRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled NetBIOS rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($netbiosRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active NetBIOS rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($netbiosRules.Count) NetBIOS firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "NetBIOS: $_"
Write-Log -Level WARNING -Message "Failed to disable some NetBIOS rules: $_" -Module "AdvancedSecurity"
}
# Also disable NetBIOS over TCP/IP on all network adapters
Write-Log -Level INFO -Message "Disabling NetBIOS over TCP/IP on all adapters..." -Module "AdvancedSecurity"
try {
$adapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE"
$adaptedCount = 0
foreach ($adapter in $adapters) {
try {
$result = Invoke-CimMethod -InputObject $adapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions = 2 }
if ($result.ReturnValue -eq 0) {
Write-Log -Level DEBUG -Message "Disabled NetBIOS on adapter: $($adapter.Description)" -Module "AdvancedSecurity"
$adaptedCount++
}
}
catch {
Write-Log -Level WARNING -Message "Could not disable NetBIOS on adapter $($adapter.Description): $_" -Module "AdvancedSecurity"
}
}
Write-Log -Level SUCCESS -Message "Disabled NetBIOS over TCP/IP on $adaptedCount adapters" -Module "AdvancedSecurity"
}
catch {
$errors += "NetBIOS TCP/IP: $_"
Write-Log -Level WARNING -Message "Failed to disable NetBIOS over TCP/IP: $_" -Module "AdvancedSecurity"
}
# 3. UPnP/SSDP (Port 1900, 2869) - MEDIUM RISK (conditional)
if (-not $SkipUPnP) {
Write-Log -Level INFO -Message "Disabling UPnP/SSDP firewall rules (Port 1900, 2869)..." -Module "AdvancedSecurity"
try {
# Filter from pre-loaded cache (ONLY ALLOW rules - keep NoID block rules enabled)
$upnpRules = $rulesWithPorts | Where-Object {
($_.LocalPort -in @(1900, 2869)) -or ($_.RemotePort -in @(1900, 2869))
} | Where-Object { $_.Rule.Action -eq 'Allow' } | Select-Object -ExpandProperty Rule
foreach ($rule in $upnpRules) {
Disable-NetFirewallRule -Name $rule.Name -ErrorAction Stop
Write-Log -Level DEBUG -Message "Disabled UPnP/SSDP rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$disabledRules++
}
if ($upnpRules.Count -eq 0) {
Write-Log -Level INFO -Message "No active UPnP/SSDP rules found (already disabled or not present)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Disabled $($upnpRules.Count) UPnP/SSDP firewall rules" -Module "AdvancedSecurity"
}
}
catch {
$errors += "UPnP/SSDP: $_"
Write-Log -Level WARNING -Message "Failed to disable some UPnP/SSDP rules: $_" -Module "AdvancedSecurity"
}
# Ensure a dedicated inbound block rule exists for SSDP (UDP 1900)
try {
$ssdpRuleName = "NoID Privacy - Block SSDP (UDP 1900)"
$existingSsdpRule = Get-NetFirewallRule -DisplayName $ssdpRuleName -ErrorAction SilentlyContinue
if (-not $existingSsdpRule) {
New-NetFirewallRule -DisplayName $ssdpRuleName `
-Direction Inbound `
-Action Block `
-Enabled True `
-Protocol UDP `
-LocalPort 1900 `
-Profile Any `
-ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Created SSDP block rule: $ssdpRuleName" -Module "AdvancedSecurity"
}
else {
Write-Log -Level INFO -Message "SSDP block rule already exists: $ssdpRuleName" -Module "AdvancedSecurity"
}
}
catch {
$errors += "SSDP BlockRule: $_"
Write-Log -Level WARNING -Message "Failed to ensure SSDP block rule: $_" -Module "AdvancedSecurity"
}
}
else {
Write-Log -Level INFO -Message "UPnP/SSDP blocking skipped (user choice for DLNA compatibility)" -Module "AdvancedSecurity"
}
# Summary
if ($errors.Count -eq 0) {
if ($disabledRules -gt 0) {
Write-Log -Level SUCCESS -Message "Disabled $disabledRules risky firewall rules" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "No risky firewall rules required changes (system already protected at firewall level)" -Module "AdvancedSecurity"
}
Write-Host ""
Write-Host "Risky Firewall Ports Disabled: $disabledRules rules" -ForegroundColor Green
if ($disabledRules -eq 0) {
Write-Host " System already protected - no risky ALLOW rules were found for these ports:" -ForegroundColor Gray
}
Write-Host " - LLMNR (5355)" -ForegroundColor Gray
Write-Host " - NetBIOS (137-139)" -ForegroundColor Gray
if (-not $SkipUPnP) {
Write-Host " - UPnP/SSDP (1900, 2869)" -ForegroundColor Gray
}
else {
Write-Host " - UPnP/SSDP (1900, 2869) - SKIPPED" -ForegroundColor Yellow
}
Write-Host ""
return $true
}
else {
Write-Log -Level WARNING -Message "Completed with $($errors.Count) errors. Disabled $disabledRules rules." -Module "AdvancedSecurity"
return $true # Partial success is still success
}
}
catch {
Write-Log -Level ERROR -Message "Failed to disable risky ports: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,147 @@
function Disable-WPAD {
<#
.SYNOPSIS
Disable WPAD (Web Proxy Auto-Discovery) to prevent proxy hijacking
.DESCRIPTION
Disables WPAD auto-discovery to prevent MITM attacks and proxy hijacking.
Uses the official Microsoft-recommended registry key (DisableWpad) plus
browser-level AutoDetect settings for third-party app compatibility.
Reference: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/disable-http-proxy-auth-features
Attack Prevention: MITM attacks, proxy hijacking, credential theft
.EXAMPLE
Disable-WPAD
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Disabling WPAD (Web Proxy Auto-Discovery)..." -Module "AdvancedSecurity"
# HKLM keys (machine-wide)
# Key 1: Official Microsoft-recommended key (Windows 10 1809+ / Server 2019+)
# Key 2: Legacy WpadOverride (for older compatibility)
# Key 3: AutoDetect for HKLM (browser-level setting)
$hklmKeys = @(
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"
Name = "DisableWpad"
Value = 1
Description = "Official MS key - disables WPAD for all WinHTTP API calls"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"
Name = "WpadOverride"
Value = 1
Description = "Legacy override key"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
Name = "AutoDetect"
Value = 0
Description = "Browser-level auto-detect (HKLM)"
}
)
# Backup HKLM keys
$backupData = @{}
foreach ($key in $hklmKeys) {
if (Test-Path $key.Path) {
$currentValue = (Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue).($key.Name)
$backupData["$($key.Path)\$($key.Name)"] = $currentValue
}
}
# Apply HKLM keys
$setCount = 0
foreach ($key in $hklmKeys) {
if (-not (Test-Path $key.Path)) {
New-Item -Path $key.Path -Force | Out-Null
}
$existing = Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue
if ($null -ne $existing -and $null -ne $existing.($key.Name)) {
Set-ItemProperty -Path $key.Path -Name $key.Name -Value $key.Value -Force | Out-Null
} else {
New-ItemProperty -Path $key.Path -Name $key.Name -Value $key.Value -PropertyType DWord -Force | Out-Null
}
$setCount++
}
# HKCU key - must be set for ALL user profiles, not just current elevated admin
# When running as admin, HKCU points to admin's profile, not the logged-in user
# Solution: Iterate through all user profiles via HKU (HKEY_USERS)
$hkuPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
$hkuName = "AutoDetect"
$hkuValue = 0
# Mount HKU if not already available
if (-not (Test-Path "HKU:")) {
New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null
}
# Get all user SIDs (excluding system accounts)
$userSIDs = Get-ChildItem -Path "HKU:\" -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match '^S-1-5-21-' -and $_.PSChildName -notmatch '_Classes$' } |
Select-Object -ExpandProperty PSChildName
foreach ($sid in $userSIDs) {
$userKeyPath = "HKU:\$sid\$hkuPath"
try {
# Backup
if (Test-Path $userKeyPath) {
$currentValue = (Get-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue).($hkuName)
$backupData["HKU\$sid\$hkuPath\$hkuName"] = $currentValue
}
# Create path if not exists
if (-not (Test-Path $userKeyPath)) {
New-Item -Path $userKeyPath -Force | Out-Null
}
# Always use Set-ItemProperty with -Type to ensure correct value type
# Remove existing value first to avoid type conflicts
Remove-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue
New-ItemProperty -Path $userKeyPath -Name $hkuName -Value $hkuValue -PropertyType DWord -Force | Out-Null
# Verify the value was set correctly
$verifyVal = (Get-ItemProperty -Path $userKeyPath -Name $hkuName -ErrorAction SilentlyContinue).($hkuName)
if ($verifyVal -eq $hkuValue) {
$setCount++
Write-Log -Level DEBUG -Message "WPAD AutoDetect set for SID $sid (verified: $verifyVal)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level WARNING -Message "WPAD AutoDetect verification failed for SID $sid (expected $hkuValue, got $verifyVal)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level DEBUG -Message "Could not set WPAD for SID $sid : $_" -Module "AdvancedSecurity"
}
}
# Also set for .DEFAULT (applies to new users)
$defaultPath = "HKU:\.DEFAULT\$hkuPath"
try {
if (-not (Test-Path $defaultPath)) {
New-Item -Path $defaultPath -Force -ErrorAction SilentlyContinue | Out-Null
}
New-ItemProperty -Path $defaultPath -Name $hkuName -Value $hkuValue -PropertyType DWord -Force -ErrorAction SilentlyContinue | Out-Null
$setCount++
}
catch {
Write-Log -Level DEBUG -Message "Could not set WPAD for .DEFAULT: $_" -Module "AdvancedSecurity"
}
Register-Backup -Type "WPAD_Settings" -Data ($backupData | ConvertTo-Json) -Name "WPAD"
Write-Log -Level SUCCESS -Message "WPAD disabled ($setCount registry keys set across all user profiles)" -Module "AdvancedSecurity"
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to disable WPAD: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,172 @@
function Enable-RdpNLA {
<#
.SYNOPSIS
Enforce Network Level Authentication (NLA) for Remote Desktop Protocol
.DESCRIPTION
HYBRID ENFORCEMENT APPROACH (Best of Security + Usability):
LEVEL 1 - ENFORCED VIA POLICIES (admin cannot disable):
- NLA (Network Level Authentication) = REQUIRED
- SSL/TLS encryption = REQUIRED
Path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services
LEVEL 2 - USER CONTROL VIA SYSTEM (admin can change in Settings):
- RDP Enable/Disable = User choice
Path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server
Attack Prevention:
- Prevents brute-force attacks before login screen appears
- Forces SSL/TLS encryption for RDP traffic (cannot be disabled)
- Requires authentication at network level (cannot be disabled)
.PARAMETER DisableRDP
Optionally completely disable RDP (for air-gapped systems)
.PARAMETER Force
Force RDP disable even on domain-joined systems (NOT RECOMMENDED)
.EXAMPLE
Enable-RdpNLA
Enforces NLA and SSL/TLS for RDP
.EXAMPLE
Enable-RdpNLA -DisableRDP -Force
Completely disables RDP (air-gapped mode)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DisableRDP,
[Parameter(Mandatory = $false)]
[switch]$Force
)
try {
Write-Log -Level INFO -Message "Configuring RDP hardening (Hybrid Enforcement)..." -Module "AdvancedSecurity"
# POLICIES PATH (enforced - admin cannot change via GUI)
$policyPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services"
# SYSTEM PATH (user control - admin can change via Settings)
$systemPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
# Backup current settings from BOTH paths
$backupData = @{
Policy_UserAuthentication = $null
Policy_SecurityLayer = $null
System_fDenyTSConnections = $null
}
# Backup Policies path (if exists)
if (Test-Path $policyPath) {
$backupData.Policy_UserAuthentication = (Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue).UserAuthentication
$backupData.Policy_SecurityLayer = (Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue).SecurityLayer
}
# Backup System path (if exists)
if (Test-Path $systemPath) {
$backupData.System_fDenyTSConnections = (Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue).fDenyTSConnections
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "RDP_Settings" -Data $backupJson -Name "RDP_Hardening"
# ========================================
# LEVEL 1: ENFORCE NLA + SSL/TLS (Policies)
# ========================================
Write-Log -Level INFO -Message "LEVEL 1: Enforcing NLA + SSL/TLS via Policies (admin cannot disable)..." -Module "AdvancedSecurity"
# Create Policies path if not exists
if (-not (Test-Path $policyPath)) {
New-Item -Path $policyPath -Force | Out-Null
Write-Log -Level DEBUG -Message "Created Policies registry path" -Module "AdvancedSecurity"
}
# ENFORCE NLA (cannot be disabled by admin via GUI)
$existing = Get-ItemProperty -Path $policyPath -Name "UserAuthentication" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $policyPath -Name "UserAuthentication" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $policyPath -Name "UserAuthentication" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "NLA ENFORCED via Policies (UserAuthentication = 1)" -Module "AdvancedSecurity"
# ENFORCE SSL/TLS (cannot be disabled by admin via GUI)
$existing = Get-ItemProperty -Path $policyPath -Name "SecurityLayer" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $policyPath -Name "SecurityLayer" -Value 2 -Force | Out-Null
} else {
New-ItemProperty -Path $policyPath -Name "SecurityLayer" -Value 2 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "SSL/TLS ENFORCED via Policies (SecurityLayer = 2)" -Module "AdvancedSecurity"
# ========================================
# LEVEL 2: RDP ENABLE/DISABLE (System - User Control)
# ========================================
Write-Log -Level INFO -Message "LEVEL 2: Setting RDP enable/disable (user CAN change in Settings)..." -Module "AdvancedSecurity"
# Create System path if not exists
if (-not (Test-Path $systemPath)) {
New-Item -Path $systemPath -Force | Out-Null
}
if ($DisableRDP) {
# Check if domain-joined
$computerSystem = Get-CimInstance -ClassName Win32_ComputerSystem
if ($computerSystem.PartOfDomain -and -not $Force) {
Write-Log -Level WARNING -Message "Domain-joined system detected. RDP disable skipped." -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Use -Force to override (NOT RECOMMENDED for enterprise!)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "WARNING: Domain-joined system detected!" -ForegroundColor Yellow
Write-Host "Skipping RDP complete disable (may be required for management)." -ForegroundColor Yellow
Write-Host "Use -DisableRDP -Force to override (NOT RECOMMENDED)." -ForegroundColor Yellow
Write-Host ""
}
else {
# Set RDP DISABLED as default (user CAN re-enable)
$existing = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 1 -Force | Out-Null
} else {
New-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 1 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "RDP DISABLED by default (user CAN re-enable via Settings)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "Windows will automatically adjust RDP firewall rules" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "RDP Status: DISABLED by default" -ForegroundColor Yellow
Write-Host " You CAN re-enable RDP anytime via:" -ForegroundColor White
Write-Host " -> Settings > System > Remote Desktop > Enable Remote Desktop" -ForegroundColor Gray
Write-Host " [OK] NLA + SSL/TLS will remain ENFORCED (secure!)" -ForegroundColor Green
Write-Host ""
}
}
else {
# Set RDP ENABLED (with NLA+SSL enforced)
$existing = Get-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $systemPath -Name "fDenyTSConnections" -Value 0 -PropertyType DWord -Force | Out-Null
}
Write-Log -Level SUCCESS -Message "RDP ENABLED with enforced NLA+SSL (user CAN disable via Settings)" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "RDP Status: ENABLED with enforced security" -ForegroundColor Green
Write-Host " [ENFORCED] NLA (Network Level Authentication)" -ForegroundColor Green
Write-Host " [ENFORCED] SSL/TLS encryption" -ForegroundColor Green
Write-Host " You CAN disable RDP anytime via Settings if not needed" -ForegroundColor White
Write-Host ""
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure RDP hardening: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,98 @@
function Remove-PowerShellV2 {
<#
.SYNOPSIS
Remove PowerShell v2 to prevent downgrade attacks
.DESCRIPTION
Removes the PowerShell v2 Windows Feature to prevent downgrade attacks.
PowerShell v2 bypasses logging, AMSI, and Constrained Language Mode.
Attack Prevention: Downgrade attacks, script logging bypass, AMSI bypass
Impact: Legacy scripts using -Version 2 will not work
.EXAMPLE
Remove-PowerShellV2
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Checking PowerShell v2 optional feature state..." -Module "AdvancedSecurity"
# Canonical detection: use Windows Optional Feature state
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature) {
# Feature is not available on this OS (e.g. removed in newer Windows 11 builds)
Write-Log -Level INFO -Message "PowerShell v2 optional feature not available on this OS (nothing to remove)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Success = $true
Changed = $false
}
}
if ($psv2Feature.State -ne 'Enabled') {
# Feature exists but is not enabled/installed
Write-Log -Level SUCCESS -Message "PowerShell v2 feature state: $($psv2Feature.State) - no removal required" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Success = $true
Changed = $false
}
}
# PSv2 feature is enabled - proceed with backup and removal
Write-Log -Level DEBUG -Message "PowerShell v2 feature is ENABLED - preparing backup and removal via DISM..." -Module "AdvancedSecurity"
# Backup current state
$backupData = @{
FeatureName = $psv2Feature.FeatureName
State = $psv2Feature.State
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
Register-Backup -Type "WindowsFeature" -Data ($backupData | ConvertTo-Json) -Name "PowerShellV2"
# Remove PowerShell v2
Write-Log -Level WARNING -Message "Removing PowerShell v2 (this may take a moment)..." -Module "AdvancedSecurity"
Write-Host ""
Write-Host "Removing PowerShell v2..." -ForegroundColor Yellow
Write-Host "This may take up to 60 seconds..." -ForegroundColor Gray
Write-Host ""
$result = Disable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop
if ($result.RestartNeeded) {
Write-Log -Level WARNING -Message "PowerShell v2 removed - REBOOT REQUIRED to complete" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "PowerShell v2 Removed!" -ForegroundColor Green
Write-Host ""
Write-Host "IMPORTANT: REBOOT REQUIRED" -ForegroundColor Yellow
Write-Host "Changes will take effect after reboot." -ForegroundColor Gray
Write-Host ""
}
else {
Write-Log -Level SUCCESS -Message "PowerShell v2 removed successfully" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "PowerShell v2 Removed!" -ForegroundColor Green
Write-Host ""
}
return [PSCustomObject]@{
Success = $true
Changed = $true
}
}
catch {
Write-Log -Level ERROR -Message "Failed to remove PowerShell v2: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Success = $false
Changed = $false
}
}
}

View file

@ -0,0 +1,150 @@
function Set-DiscoveryProtocolsSecurity {
<#
.SYNOPSIS
Completely hardens discovery protocols (WS-Discovery + mDNS) for air-gapped systems.
.DESCRIPTION
This function is designed for the AdvancedSecurity **Maximum** profile.
It applies the following changes:
- Disables OS-level mDNS client resolution
- Stops and disables WS-Discovery related services
- Adds explicit Windows Firewall BLOCK rules for WS-Discovery and mDNS ports
Protocols/ports affected:
- WS-Discovery: UDP 3702, TCP 5357/5358
- mDNS: UDP 5353
NOTE: Backup for services, registry and firewall rules is handled centrally by
Backup-AdvancedSecuritySettings and the Core rollback system.
.PARAMETER DisableCompletely
When present, applies full discovery protocol hardening. Currently this
function is only called with -DisableCompletely in Maximum profile.
.EXAMPLE
Set-DiscoveryProtocolsSecurity -DisableCompletely
# Completely disables WS-Discovery and mDNS on this host.
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
Write-Log -Level INFO -Message "Applying discovery protocol security (WS-Discovery + mDNS)... DisableCompletely: $DisableCompletely" -Module "AdvancedSecurity"
if (-not $DisableCompletely) {
Write-Log -Level INFO -Message "Set-DiscoveryProtocolsSecurity called without -DisableCompletely. No changes applied." -Module "AdvancedSecurity"
return $true
}
$changesApplied = 0
# =============================
# 1) Disable mDNS via DNS Client parameters
# =============================
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
if (-not (Test-Path $dnsParamsPath)) {
New-Item -Path $dnsParamsPath -Force | Out-Null
Write-Log -Level INFO -Message "Created registry key: $dnsParamsPath" -Module "AdvancedSecurity"
}
$mdnsProps = Get-ItemProperty -Path $dnsParamsPath -ErrorAction SilentlyContinue
$currentEnableMdns = if ($mdnsProps) { $mdnsProps.EnableMDNS } else { $null }
if ($currentEnableMdns -ne 0) {
New-ItemProperty -Path $dnsParamsPath -Name "EnableMDNS" -Value 0 -PropertyType DWord -Force | Out-Null
Write-Log -Level INFO -Message "Set EnableMDNS = 0 (Disable OS mDNS resolver)" -Module "AdvancedSecurity"
$changesApplied++
}
# =============================
# 2) Stop and disable WS-Discovery related services
# =============================
$wsdServices = @(
@{ Name = "FDResPub"; DisplayName = "Function Discovery Resource Publication" },
@{ Name = "fdPHost"; DisplayName = "Function Discovery Provider Host" }
)
foreach ($svc in $wsdServices) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if (-not $service) {
Write-Log -Level INFO -Message "Service $($svc.Name) not found (may not be installed)" -Module "AdvancedSecurity"
continue
}
if ($service.Status -eq 'Running') {
try {
Stop-Service -Name $svc.Name -Force -ErrorAction Stop
Write-Log -Level INFO -Message "Stopped service: $($svc.Name) ($($svc.DisplayName))" -Module "AdvancedSecurity"
$changesApplied++
}
catch {
Write-Log -Level WARNING -Message "Failed to stop service $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
if ($service.StartType -ne 'Disabled') {
try {
Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop
Write-Log -Level INFO -Message "Set service $($svc.Name) StartupType = Disabled" -Module "AdvancedSecurity"
$changesApplied++
}
catch {
Write-Log -Level WARNING -Message "Failed to set StartupType=Disabled for $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
}
# =============================
# 3) Add firewall BLOCK rules for WS-Discovery and mDNS
# =============================
$firewallRules = @(
@{ Name = "NoID-Block-WSD-UDP-3702"; DisplayName = "NoID Privacy - Block WS-Discovery UDP 3702"; Protocol = "UDP"; LocalPort = 3702 },
@{ Name = "NoID-Block-WSD-TCP-5357"; DisplayName = "NoID Privacy - Block WS-Discovery HTTP TCP 5357"; Protocol = "TCP"; LocalPort = 5357 },
@{ Name = "NoID-Block-WSD-TCP-5358"; DisplayName = "NoID Privacy - Block WS-Discovery HTTPS TCP 5358"; Protocol = "TCP"; LocalPort = 5358 },
@{ Name = "NoID-Block-mDNS-UDP-5353"; DisplayName = "NoID Privacy - Block mDNS UDP 5353"; Protocol = "UDP"; LocalPort = 5353 }
)
foreach ($rule in $firewallRules) {
try {
$existing = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if (-not $existing) {
New-NetFirewallRule -Name $rule.Name `
-DisplayName $rule.DisplayName `
-Direction Inbound `
-Protocol $rule.Protocol `
-LocalPort $rule.LocalPort `
-Action Block `
-Profile Any `
-Enabled True | Out-Null
Write-Log -Level INFO -Message "Created firewall rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$changesApplied++
}
else {
# Ensure rule is enabled and blocking
Set-NetFirewallRule -Name $rule.Name -Enabled True -Action Block -ErrorAction SilentlyContinue
Write-Log -Level DEBUG -Message "Firewall rule already exists and was enforced: $($rule.DisplayName)" -Module "AdvancedSecurity"
}
}
catch {
Write-Log -Level WARNING -Message "Failed to ensure firewall rule $($rule.DisplayName): $_" -Module "AdvancedSecurity"
}
}
if ($changesApplied -eq 0) {
Write-Log -Level SUCCESS -Message "Discovery protocol security already configured (no changes needed)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Discovery protocol security applied ($changesApplied changes)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to apply discovery protocol security (WS-Discovery/mDNS): $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,66 @@
function Set-FirewallShieldsUp {
<#
.SYNOPSIS
Enable "Shields Up" mode - Block ALL incoming connections on Public network
.DESCRIPTION
Sets DoNotAllowExceptions=1 for PublicProfile firewall.
This blocks ALL incoming connections, even from allowed apps.
Goes BEYOND Microsoft Security Baseline.
.PARAMETER Enable
Enable Shields Up mode (block all incoming on Public)
.PARAMETER Disable
Disable Shields Up mode (allow configured exceptions)
#>
[CmdletBinding()]
param(
[switch]$Enable,
[switch]$Disable
)
$moduleName = "AdvancedSecurity"
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile"
$valueName = "DoNotAllowExceptions"
try {
if ($Enable) {
Write-Log -Level INFO -Message "Enabling Firewall Shields Up mode (Public profile)..." -Module $moduleName
# Ensure path exists
if (!(Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Set DoNotAllowExceptions = 1
Set-ItemProperty -Path $regPath -Name $valueName -Value 1 -Type DWord -Force
Write-Log -Level SUCCESS -Message "Firewall Shields Up ENABLED - All incoming connections blocked on Public network" -Module $moduleName
Write-Host ""
Write-Host " SHIELDS UP: Public network now blocks ALL incoming connections" -ForegroundColor Green
Write-Host " This includes allowed apps (Teams, Discord, etc. cannot receive calls)" -ForegroundColor Yellow
Write-Host ""
return $true
}
elseif ($Disable) {
Write-Log -Level INFO -Message "Disabling Firewall Shields Up mode..." -Module $moduleName
if (Test-Path $regPath) {
Set-ItemProperty -Path $regPath -Name $valueName -Value 0 -Type DWord -Force
}
Write-Log -Level SUCCESS -Message "Firewall Shields Up disabled - Normal firewall exceptions apply" -Module $moduleName
return $true
}
else {
Write-Log -Level WARNING -Message "No action specified for Set-FirewallShieldsUp" -Module $moduleName
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to set Firewall Shields Up: $_" -Module $moduleName
return $false
}
}

View file

@ -0,0 +1,102 @@
function Set-IPv6Security {
<#
.SYNOPSIS
Disable IPv6 to prevent DHCPv6/Router Solicitation attacks (mitm6)
.DESCRIPTION
Disables IPv6 via registry to prevent:
- mitm6 attacks (DHCPv6 spoofing DNS takeover NTLM relay)
- IPv6 Router Advertisement spoofing
- DHCPv6 poisoning attacks
This is the recommended mitigation per Fox-IT security research.
WARNING: May break Exchange Server and some Active Directory features.
Only recommended for air-gapped or high-security systems.
.PARAMETER DisableCompletely
If true, completely disables IPv6 (DisabledComponents = 0xFF)
.EXAMPLE
Set-IPv6Security -DisableCompletely
.NOTES
Registry: HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\DisabledComponents
Value 0xFF = Disable all IPv6 components
REBOOT REQUIRED for changes to take effect.
References:
- https://blog.fox-it.com/2018/01/11/mitm6-compromising-ipv4-networks-via-ipv6/
- https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/configure-ipv6-in-windows
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
if (-not $DisableCompletely) {
Write-Log -Level INFO -Message "IPv6 disable not requested - keeping default configuration" -Module "AdvancedSecurity"
return $true
}
Write-Log -Level INFO -Message "Disabling IPv6 (mitm6 attack mitigation)..." -Module "AdvancedSecurity"
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
# Backup current value
$currentValue = Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue
$backupData = @{
Path = $regPath
Name = "DisabledComponents"
PreviousValue = if ($currentValue) { $currentValue.DisabledComponents } else { "_NOT_SET" }
NewValue = 255
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Registry" -Data $backupJson -Name "IPv6_DisabledComponents"
# Ensure registry path exists
if (-not (Test-Path $regPath)) {
New-Item -Path $regPath -Force | Out-Null
}
# Set DisabledComponents to 0xFF (255) = Disable all IPv6 components
Set-ItemProperty -Path $regPath -Name "DisabledComponents" -Value 255 -Type DWord -Force
Write-Log -Level SUCCESS -Message "IPv6 disabled (DisabledComponents = 0xFF)" -Module "AdvancedSecurity"
# Verify
$verifyValue = (Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue).DisabledComponents
if ($verifyValue -eq 255) {
Write-Log -Level SUCCESS -Message "IPv6 disable verified - REBOOT REQUIRED" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Yellow
Write-Host " IPv6 DISABLED (mitm6 Attack Mitigation)" -ForegroundColor Yellow
Write-Host "================================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Registry: DisabledComponents = 0xFF (255)" -ForegroundColor White
Write-Host ""
Write-Host "Protection against:" -ForegroundColor Cyan
Write-Host " - DHCPv6 spoofing (mitm6 tool)" -ForegroundColor Gray
Write-Host " - IPv6 Router Advertisement attacks" -ForegroundColor Gray
Write-Host " - DNS takeover via fake DHCPv6 server" -ForegroundColor Gray
Write-Host " - NTLM credential relay attacks" -ForegroundColor Gray
Write-Host ""
Write-Host "REBOOT REQUIRED for changes to take effect!" -ForegroundColor Red
Write-Host ""
return $true
}
else {
Write-Log -Level ERROR -Message "IPv6 disable verification failed" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to disable IPv6: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,192 @@
function Set-SRPRules {
<#
.SYNOPSIS
Configures Software Restriction Policies (SRP) to block .lnk execution from Temp/Downloads
.DESCRIPTION
Implements SRP rules to mitigate CVE-2025-9491 (Windows LNK Remote Code Execution).
CRITICAL ZERO-DAY MITIGATION:
- CVE-2025-9491: Actively exploited since 2017
- No patch available (Microsoft: "does not meet servicing threshold")
- ASR and SmartScreen do NOT protect against this
SRP Rules Created:
1. Block *.lnk from %LOCALAPPDATA%\Temp\* (Outlook attachments)
2. Block *.lnk from %USERPROFILE%\Downloads\* (Browser downloads)
Windows 11 Bug Fix:
- Removes buggy registry keys that disable SRP on Win11
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Set-SRPRules
Applies SRP rules to block malicious .lnk execution
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
REFERENCES:
- CVE-2025-9491: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-9491
- CISA KEV: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- SRP Documentation: https://docs.microsoft.com/windows/security/threat-protection/windows-defender-application-control/applocker/software-restriction-policies
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$configPath = Join-Path $PSScriptRoot "..\Config\SRP-Rules.json"
if (-not (Test-Path $configPath)) {
Write-Log -Level ERROR -Message "SRP-Rules.json not found: $configPath" -Module "AdvancedSecurity"
return $false
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
Write-Log -Level INFO -Message "Configuring Software Restriction Policies (SRP) for CVE-2025-9491..." -Module "AdvancedSecurity"
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would configure SRP with following rules:" -Module "AdvancedSecurity"
foreach ($rule in $config.PathRules) {
Write-Log -Level INFO -Message "[DRYRUN] - $($rule.Name): $($rule.Path)" -Module "AdvancedSecurity"
}
return $true
}
# Step 1: Create SRP Policy Root
$policyRoot = $config.RegistryPaths.PolicyRoot
if (-not (Test-Path $policyRoot)) {
Write-Log -Level INFO -Message "Creating SRP policy root: $policyRoot" -Module "AdvancedSecurity"
New-Item -Path $policyRoot -Force | Out-Null
}
# Step 2: Set Default Level (Unrestricted)
Write-Log -Level INFO -Message "Setting SRP default level to Unrestricted (262144)" -Module "AdvancedSecurity"
$existingDefaultLevel = Get-ItemProperty -Path $policyRoot -Name "DefaultLevel" -ErrorAction SilentlyContinue
if ($null -ne $existingDefaultLevel) {
Set-ItemProperty -Path $policyRoot -Name "DefaultLevel" -Value $config.SRPConfiguration.DefaultLevel -Force | Out-Null
}
else {
New-ItemProperty -Path $policyRoot -Name "DefaultLevel" -Value $config.SRPConfiguration.DefaultLevel -PropertyType DWord -Force | Out-Null
}
# Step 3: Enable Transparent Enforcement
$existingTransparent = Get-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -ErrorAction SilentlyContinue
if ($null -ne $existingTransparent) {
Set-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -Value $config.SRPConfiguration.TransparentEnabled -Force | Out-Null
}
else {
New-ItemProperty -Path $policyRoot -Name "TransparentEnabled" -Value $config.SRPConfiguration.TransparentEnabled -PropertyType DWord -Force | Out-Null
}
# Step 4: Create Path Rules
$pathRulesRoot = $config.RegistryPaths.PathRules
if (-not (Test-Path $pathRulesRoot)) {
Write-Log -Level INFO -Message "Creating SRP path rules root: $pathRulesRoot" -Module "AdvancedSecurity"
New-Item -Path $pathRulesRoot -Force | Out-Null
}
$rulesCreated = 0
foreach ($rule in $config.PathRules) {
if (-not $rule.Enabled) {
Write-Log -Level INFO -Message "Skipping disabled rule: $($rule.Name)" -Module "AdvancedSecurity"
continue
}
# Generate GUID for rule
$ruleGuid = "{$([guid]::NewGuid().ToString())}"
$rulePath = Join-Path $pathRulesRoot $ruleGuid
Write-Log -Level INFO -Message "Creating SRP rule: $($rule.Name)" -Module "AdvancedSecurity"
# Create rule key
if (-not (Test-Path $rulePath)) {
New-Item -Path $rulePath -Force | Out-Null
}
# Set ItemData (path pattern)
$existingItemData = Get-ItemProperty -Path $rulePath -Name "ItemData" -ErrorAction SilentlyContinue
if ($null -ne $existingItemData) {
Set-ItemProperty -Path $rulePath -Name "ItemData" -Value $rule.Path -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "ItemData" -Value $rule.Path -PropertyType ExpandString -Force | Out-Null
}
# Set Description
$existingDescription = Get-ItemProperty -Path $rulePath -Name "Description" -ErrorAction SilentlyContinue
if ($null -ne $existingDescription) {
Set-ItemProperty -Path $rulePath -Name "Description" -Value $rule.Description -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "Description" -Value $rule.Description -PropertyType String -Force | Out-Null
}
# Set SaferFlags
$existingSaferFlags = Get-ItemProperty -Path $rulePath -Name "SaferFlags" -ErrorAction SilentlyContinue
if ($null -ne $existingSaferFlags) {
Set-ItemProperty -Path $rulePath -Name "SaferFlags" -Value $rule.SaferFlags -Force | Out-Null
}
else {
New-ItemProperty -Path $rulePath -Name "SaferFlags" -Value $rule.SaferFlags -PropertyType DWord -Force | Out-Null
}
$rulesCreated++
Write-Log -Level SUCCESS -Message "SRP rule created: $($rule.Name) -> $($rule.Path)" -Module "AdvancedSecurity"
}
# Step 5: Windows 11 Bug Fix
$bugFixPath = $config.RegistryPaths.Win11BugFix
if (Test-Path $bugFixPath) {
Write-Log -Level INFO -Message "Applying Windows 11 SRP bug fix..." -Module "AdvancedSecurity"
foreach ($keyName in $config.Windows11BugFix.KeysToRemove) {
$keyExists = Get-ItemProperty -Path $bugFixPath -Name $keyName -ErrorAction SilentlyContinue
if ($null -ne $keyExists) {
Remove-ItemProperty -Path $bugFixPath -Name $keyName -Force -ErrorAction SilentlyContinue
Write-Log -Level SUCCESS -Message "Removed buggy key: $keyName (Windows 11 SRP fix)" -Module "AdvancedSecurity"
}
}
}
Write-Log -Level SUCCESS -Message "SRP configuration completed: $rulesCreated rules created" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "CVE-2025-9491 mitigation active - .lnk files from Temp/Downloads now blocked" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " SRP RULES CONFIGURED (CVE-2025-9491)" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Zero-Day Protection: Windows LNK RCE (ACTIVELY EXPLOITED)" -ForegroundColor Yellow
Write-Host ""
Write-Host "Rules Created: $rulesCreated" -ForegroundColor Cyan
Write-Host "Protected Paths:" -ForegroundColor White
Write-Host " - Outlook Temp (%LOCALAPPDATA%\Temp\*.lnk)" -ForegroundColor Gray
Write-Host " - Downloads (%USERPROFILE%\Downloads\*.lnk)" -ForegroundColor Gray
Write-Host ""
Write-Host "Status: ACTIVE (malicious .lnk files blocked)" -ForegroundColor Green
Write-Host "CVE-2025-9491: MITIGATED" -ForegroundColor Green
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure SRP rules: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,113 @@
function Set-WDigestProtection {
<#
.SYNOPSIS
Disable WDigest credential caching to prevent plaintext password storage in LSASS
.DESCRIPTION
Configures WDigest to NOT store plaintext credentials in LSASS memory.
Prevents Mimikatz, Windows Credential Editor (WCE), and other memory-dumping
tools from extracting plaintext passwords.
Status: This setting is DEPRECATED in Windows 11 24H2+ (default is already secure),
but we set it explicitly for:
- Backwards compatibility with older Windows versions
- Defense-in-depth (explicit is better than implicit)
- Mixed environments with Win7/8/Server 2008/2012
No negative impact on modern systems (setting is ignored on Win11 24H2+).
.EXAMPLE
Set-WDigestProtection
Sets UseLogonCredential = 0 to prevent plaintext credential storage
.NOTES
Microsoft Security Advisory: KB2871997 (May 2014)
Deprecated in Windows 11 24H2 Security Baseline (September 2024)
Default behavior:
- Windows 7/8/Server 2008/2012: UseLogonCredential = 1 (INSECURE!)
- Windows 8.1+: UseLogonCredential = 0 (Secure)
- Windows 11 24H2+: Setting ignored (hardcoded secure)
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Configuring WDigest credential protection..." -Module "AdvancedSecurity"
$wdigestRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest"
# Check Windows version for informational logging
$osVersion = [System.Environment]::OSVersion.Version
$isWin11 = $osVersion.Major -ge 10 -and $osVersion.Build -ge 22000
if ($isWin11 -and $osVersion.Build -ge 26100) {
# Windows 11 24H2+ (Build 26100+)
Write-Log -Level INFO -Message "Windows 11 24H2+ detected - WDigest setting is deprecated but will be set for backwards compatibility" -Module "AdvancedSecurity"
}
# Backup current setting
$currentValue = $null
if (Test-Path $wdigestRegPath) {
$currentValue = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue).UseLogonCredential
}
$backupData = @{
OriginalValue = $currentValue
RegistryPath = $wdigestRegPath
SettingName = "UseLogonCredential"
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
# Register backup
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "WDigest_Settings" -Data $backupJson -Name "WDigest_Protection"
# Create registry path if it doesn't exist
if (-not (Test-Path $wdigestRegPath)) {
Write-Log -Level INFO -Message "Creating WDigest registry path..." -Module "AdvancedSecurity"
New-Item -Path $wdigestRegPath -Force | Out-Null
}
# Set UseLogonCredential = 0 (Secure - no plaintext in memory)
$existing = Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue
if ($null -ne $existing) {
Set-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -Value 0 -Force | Out-Null
} else {
New-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -Value 0 -PropertyType DWord -Force | Out-Null
}
# Verify
$newValue = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential").UseLogonCredential
if ($newValue -eq 0) {
if ($currentValue -eq 1) {
Write-Log -Level SUCCESS -Message "WDigest credential protection enabled (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message "Previous value was 1 (INSECURE) - system was vulnerable to plaintext credential dumps!" -Module "AdvancedSecurity"
Write-Host ""
Write-Host "SECURITY IMPROVEMENT: WDigest was storing plaintext credentials!" -ForegroundColor Yellow
Write-Host "This has now been FIXED. Plaintext credential storage is disabled." -ForegroundColor Green
Write-Host ""
}
elseif ($null -eq $currentValue) {
Write-Log -Level SUCCESS -Message "WDigest credential protection configured (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "WDigest setting was not previously configured (default varies by OS version)" -Module "AdvancedSecurity"
}
else {
# currentValue was already 0
Write-Log -Level SUCCESS -Message "WDigest credential protection verified (UseLogonCredential = 0)" -Module "AdvancedSecurity"
Write-Log -Level INFO -Message "Setting was already correct (no change needed)" -Module "AdvancedSecurity"
}
return $true
}
else {
Write-Log -Level ERROR -Message "Failed to verify WDigest setting (expected 0, got $newValue)" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to configure WDigest protection: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,110 @@
function Set-WindowsUpdate {
<#
.SYNOPSIS
Configures Windows Update using simple GUI-equivalent settings
.DESCRIPTION
Applies 3 simple Windows Update settings that align with the Windows Settings GUI:
1. Get the latest updates as soon as they're available (ON, enforced via policy)
2. Receive updates for other Microsoft products (ON, user-toggleable)
3. Delivery Optimization - Downloads from other devices (OFF, enforced via policy)
NO forced schedules and NO auto-reboot policies are configured.
Installation timing remains user-controlled via the Windows Update GUI; where
policies are used, Windows clearly indicates that "Some settings are managed
by your organization".
.PARAMETER DryRun
Preview changes without applying them
.EXAMPLE
Set-WindowsUpdate
.NOTES
Author: NexusOne23
Version: 2.2.0
Requires: Administrator privileges
Based on: Windows Settings > Windows Update > Advanced options
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[switch]$DryRun
)
try {
$configPath = Join-Path $PSScriptRoot "..\Config\WindowsUpdate.json"
if (-not (Test-Path $configPath)) {
Write-Log -Level ERROR -Message "WindowsUpdate.json not found: $configPath" -Module "AdvancedSecurity"
return $false
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
Write-Log -Level INFO -Message "Configuring Windows Update (3 simple GUI settings)..." -Module "AdvancedSecurity"
if ($DryRun) {
Write-Log -Level INFO -Message "[DRYRUN] Would configure 3 Windows Update settings" -Module "AdvancedSecurity"
return $true
}
$settingsApplied = 0
# Loop through all 3 settings from config
foreach ($settingKey in $config.Settings.PSObject.Properties.Name) {
$setting = $config.Settings.$settingKey
$regPath = $setting.RegistryPath
# Ensure registry path exists
if (-not (Test-Path $regPath)) {
Write-Log -Level DEBUG -Message "Creating registry path: $regPath" -Module "AdvancedSecurity"
New-Item -Path $regPath -Force | Out-Null
}
# Apply each value in this setting
foreach ($valueName in $setting.Values.PSObject.Properties.Name) {
$valueData = $setting.Values.$valueName
# Always use New-ItemProperty with -Force to ensure correct type and value
# -Force will overwrite existing keys
New-ItemProperty -Path $regPath -Name $valueName -Value $valueData.Value -PropertyType DWord -Force | Out-Null
Write-Log -Level SUCCESS -Message "$($setting.Name): $valueName = $($valueData.Value)" -Module "AdvancedSecurity"
$settingsApplied++
}
}
Write-Log -Level SUCCESS -Message "Windows Update configured: $settingsApplied registry keys set" -Module "AdvancedSecurity"
# Restart Windows Update service to apply changes immediately
Write-Log -Level INFO -Message "Restarting Windows Update service to apply changes..." -Module "AdvancedSecurity"
try {
Restart-Service -Name wuauserv -Force -ErrorAction Stop | Out-Null
Write-Log -Level SUCCESS -Message "Windows Update service restarted successfully" -Module "AdvancedSecurity"
}
catch {
Write-Log -Level WARNING -Message "Could not restart Windows Update service: $($_.Exception.Message)" -Module "AdvancedSecurity"
}
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " Windows Update Configured (3 Settings)" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "[1] Get latest updates immediately: ON (Policy)" -ForegroundColor Gray
Write-Host "[2] Microsoft Update (Office, etc.): ON (User can toggle)" -ForegroundColor Gray
Write-Host "[3] P2P Delivery Optimization: OFF (Policy)" -ForegroundColor Gray
Write-Host ""
Write-Host "Installation timing remains user-controlled (no forced schedules, no auto-reboot policies)." -ForegroundColor White
Write-Host "Windows will indicate where settings are managed by policy in the GUI." -ForegroundColor White
Write-Host ""
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to configure Windows Update: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,200 @@
function Set-WirelessDisplaySecurity {
<#
.SYNOPSIS
Hardens Wireless Display (Miracast) settings to prevent screen interception attacks.
.DESCRIPTION
Configures Windows Wireless Display policies to prevent attackers from:
- Setting up rogue Miracast receivers to capture your screen
- Using your PC as an unauthorized display receiver
- Intercepting screen content via mDNS spoofing
Default (always applied): Blocks receiving projections, requires PIN for pairing
Full disable: Also blocks sending projections and mDNS discovery
.PARAMETER DisableCompletely
If specified, completely disables all Wireless Display functionality.
Default: Only hardens (blocks receiving, requires PIN) but allows sending.
.EXAMPLE
Set-WirelessDisplaySecurity
# Applies default hardening (blocks receiving, requires PIN)
.EXAMPLE
Set-WirelessDisplaySecurity -DisableCompletely
# Completely disables all Wireless Display functionality
#>
[CmdletBinding()]
param(
[switch]$DisableCompletely
)
try {
Write-Log -Level INFO -Message "Applying Wireless Display security hardening (DisableCompletely: $DisableCompletely)..." -Module "AdvancedSecurity"
$changesApplied = 0
# Registry path for Wireless Display policies
$connectPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect"
# Create key if it doesn't exist
if (-not (Test-Path $connectPath)) {
New-Item -Path $connectPath -Force | Out-Null
Write-Log -Level INFO -Message "Created registry key: $connectPath" -Module "AdvancedSecurity"
}
# ============================================
# ALWAYS APPLIED (Default hardening for all profiles)
# ============================================
# 1. AllowProjectionToPC = 0 (Block receiving projections - prevents rogue receiver attacks)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionToPC" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionToPC -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionToPC" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionToPC = 0 (Block receiving)" -Module "AdvancedSecurity"
$changesApplied++
}
# 2. RequirePinForPairing = 2 (Always require PIN - prevents unauthorized pairing)
$currentValue = Get-ItemProperty -Path $connectPath -Name "RequirePinForPairing" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.RequirePinForPairing -ne 2) {
Set-ItemProperty -Path $connectPath -Name "RequirePinForPairing" -Value 2 -Type DWord -Force
Write-Log -Level INFO -Message "Set RequirePinForPairing = 2 (Always require PIN)" -Module "AdvancedSecurity"
$changesApplied++
}
# ============================================
# OPTIONAL: Complete disable (user choice)
# ============================================
if ($DisableCompletely) {
Write-Log -Level INFO -Message "Applying complete Wireless Display disable..." -Module "AdvancedSecurity"
# 3. AllowProjectionFromPC = 0 (Block sending projections)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionFromPC" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionFromPC -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionFromPC" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionFromPC = 0 (Block sending)" -Module "AdvancedSecurity"
$changesApplied++
}
# 4. AllowMdnsAdvertisement = 0 (Don't advertise as receiver)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowMdnsAdvertisement" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowMdnsAdvertisement -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowMdnsAdvertisement" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowMdnsAdvertisement = 0 (No mDNS ads)" -Module "AdvancedSecurity"
$changesApplied++
}
# 5. AllowMdnsDiscovery = 0 (Don't discover receivers via mDNS)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowMdnsDiscovery" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowMdnsDiscovery -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowMdnsDiscovery" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowMdnsDiscovery = 0 (No mDNS discovery)" -Module "AdvancedSecurity"
$changesApplied++
}
# 6. AllowProjectionFromPCOverInfrastructure = 0 (Block infrastructure projection)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionFromPCOverInfrastructure" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionFromPCOverInfrastructure -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionFromPCOverInfrastructure" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionFromPCOverInfrastructure = 0" -Module "AdvancedSecurity"
$changesApplied++
}
# 7. AllowProjectionToPCOverInfrastructure = 0 (Block infrastructure receiving)
$currentValue = Get-ItemProperty -Path $connectPath -Name "AllowProjectionToPCOverInfrastructure" -ErrorAction SilentlyContinue
if ($null -eq $currentValue -or $currentValue.AllowProjectionToPCOverInfrastructure -ne 0) {
Set-ItemProperty -Path $connectPath -Name "AllowProjectionToPCOverInfrastructure" -Value 0 -Type DWord -Force
Write-Log -Level INFO -Message "Set AllowProjectionToPCOverInfrastructure = 0" -Module "AdvancedSecurity"
$changesApplied++
}
# 8. Block Miracast ports via Windows Firewall (7236, 7250)
$firewallRules = @(
@{
Name = "NoID-Block-Miracast-TCP-7236"
DisplayName = "NoID Privacy - Block Miracast TCP 7236"
Direction = "Inbound"
Protocol = "TCP"
LocalPort = 7236
},
@{
Name = "NoID-Block-Miracast-TCP-7250"
DisplayName = "NoID Privacy - Block Miracast TCP 7250"
Direction = "Inbound"
Protocol = "TCP"
LocalPort = 7250
},
@{
Name = "NoID-Block-Miracast-UDP-7236"
DisplayName = "NoID Privacy - Block Miracast UDP 7236"
Direction = "Inbound"
Protocol = "UDP"
LocalPort = 7236
},
@{
Name = "NoID-Block-Miracast-UDP-7250"
DisplayName = "NoID Privacy - Block Miracast UDP 7250"
Direction = "Inbound"
Protocol = "UDP"
LocalPort = 7250
}
)
foreach ($rule in $firewallRules) {
$existingRule = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if (-not $existingRule) {
New-NetFirewallRule -Name $rule.Name `
-DisplayName $rule.DisplayName `
-Direction $rule.Direction `
-Protocol $rule.Protocol `
-LocalPort $rule.LocalPort `
-Action Block `
-Profile Any `
-Enabled True | Out-Null
Write-Log -Level INFO -Message "Created firewall rule: $($rule.DisplayName)" -Module "AdvancedSecurity"
$changesApplied++
}
}
# 9. Disable WiFi Direct Service (WFDSConMgrSvc) - CRITICAL for complete Miracast block
# Registry policies alone don't block WiFi Direct P2P discovery!
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
if ($wfdService) {
if ($wfdService.Status -eq 'Running') {
Stop-Service -Name "WFDSConMgrSvc" -Force -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Stopped WiFi Direct Service (WFDSConMgrSvc)" -Module "AdvancedSecurity"
}
if ($wfdService.StartType -ne 'Disabled') {
Set-Service -Name "WFDSConMgrSvc" -StartupType Disabled -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Disabled WiFi Direct Service (WFDSConMgrSvc) - survives reboot" -Module "AdvancedSecurity"
$changesApplied++
}
}
# 10. Disable WiFi Direct Virtual Adapters (immediate effect)
$wfdAdapters = Get-NetAdapter -InterfaceDescription "Microsoft Wi-Fi Direct Virtual*" -IncludeHidden -ErrorAction SilentlyContinue
if ($wfdAdapters) {
$wfdAdapters | Where-Object { $_.Status -ne 'Disabled' } | ForEach-Object {
Disable-NetAdapter -Name $_.Name -Confirm:$false -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Disabled WiFi Direct adapter: $($_.Name)" -Module "AdvancedSecurity"
$changesApplied++
}
}
}
if ($changesApplied -eq 0) {
Write-Log -Level SUCCESS -Message "Wireless Display security already configured (no changes needed)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level SUCCESS -Message "Wireless Display security applied ($changesApplied changes)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to apply Wireless Display security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,216 @@
function Stop-RiskyServices {
<#
.SYNOPSIS
Stop and disable risky network services
.DESCRIPTION
Stops and disables network services that pose security risks:
- SSDPSRV (SSDP Discovery) - Port 1900 UDP
- upnphost (UPnP Device Host) - Port 2869 TCP
- lmhosts (TCP/IP NetBIOS Helper) - Port 139 TCP
Defense in Depth: Firewall blocks external access, but services
still run and listen locally. Stopping services completely closes ports.
Service Dependencies:
upnphost depends on SSDPSRV, so upnphost must be stopped FIRST.
.EXAMPLE
Stop-RiskyServices
Stops all risky network services
.NOTES
Impact:
- Smart home device auto-discovery may not work
- DLNA/casting features may require manual configuration
- NetBIOS name resolution disabled (already disabled via registry)
#>
[CmdletBinding()]
param()
try {
Write-Log -Level INFO -Message "Stopping risky network services..." -Module "AdvancedSecurity"
$services = @(
@{
Name = "upnphost"
DisplayName = "UPnP Device Host"
Port = 2869
Protocol = "TCP"
Risk = "MEDIUM"
Impact = "DLNA/casting features may require manual configuration"
},
@{
Name = "SSDPSRV"
DisplayName = "SSDP Discovery"
Port = 1900
Protocol = "UDP"
Risk = "MEDIUM"
Impact = "Smart home device auto-discovery may not work"
},
@{
Name = "lmhosts"
DisplayName = "TCP/IP NetBIOS Helper"
Port = 139
Protocol = "TCP"
Risk = "MEDIUM"
Impact = "NetBIOS name resolution disabled"
}
# Note: Computer Browser (Browser) service is DEPRECATED in Win10/11
# It's tied to SMB1 which is not installed by default
# Removing from list to avoid errors on modern systems
)
# Backup service states
Write-Log -Level INFO -Message "Backing up service states..." -Module "AdvancedSecurity"
$serviceBackup = @{}
foreach ($svc in $services) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if ($service) {
$serviceBackup[$svc.Name] = @{
Status = $service.Status.ToString()
StartType = $service.StartType.ToString()
DisplayName = $service.DisplayName
}
}
}
$backupData = @{
Services = $serviceBackup
BackupDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
}
$backupJson = $backupData | ConvertTo-Json -Depth 10
Register-Backup -Type "Services_State" -Data $backupJson -Name "RiskyServices"
Write-Log -Level SUCCESS -Message "Backed up state of $($serviceBackup.Count) services" -Module "AdvancedSecurity"
# Stop and disable services
$stoppedCount = 0
$errors = @()
# CRITICAL: Stop upnphost FIRST (it depends on SSDPSRV)
foreach ($svc in $services) {
Write-Log -Level INFO -Message "Processing service: $($svc.DisplayName) ($($svc.Name))..." -Module "AdvancedSecurity"
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if (-not $service) {
Write-Log -Level INFO -Message "Service $($svc.Name) not found (may not be installed)" -Module "AdvancedSecurity"
continue
}
try {
# Stop service if running
if ($service.Status -eq 'Running') {
Write-Log -Level INFO -Message "Stopping $($svc.Name)..." -Module "AdvancedSecurity"
Stop-Service -Name $svc.Name -Force -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Stopped $($svc.Name)" -Module "AdvancedSecurity"
}
else {
Write-Log -Level INFO -Message "$($svc.Name) already stopped" -Module "AdvancedSecurity"
}
# Disable service
Write-Log -Level INFO -Message "Disabling $($svc.Name)..." -Module "AdvancedSecurity"
Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop
Write-Log -Level SUCCESS -Message "Disabled $($svc.Name) (StartupType = Disabled)" -Module "AdvancedSecurity"
$stoppedCount++
}
catch {
$errors += "$($svc.Name): $_"
Write-Log -Level WARNING -Message "Failed to stop/disable $($svc.Name): $_" -Module "AdvancedSecurity"
}
}
# Verify ports are closed
Write-Log -Level INFO -Message "Verifying ports are closed..." -Module "AdvancedSecurity"
Start-Sleep -Seconds 2 # Give services time to fully stop
$portsClosed = @()
$portsStillOpen = @()
# Check TCP ports
foreach ($port in @(139, 2869)) {
$listener = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
if (-not $listener) {
$portsClosed += "TCP $port"
Write-Log -Level SUCCESS -Message "Port TCP $port is CLOSED" -Module "AdvancedSecurity"
}
else {
$portsStillOpen += "TCP $port"
Write-Log -Level WARNING -Message "Port TCP $port is still LISTENING!" -Module "AdvancedSecurity"
}
}
# Check UDP port 1900
$udpListener = Get-NetUDPEndpoint -LocalPort 1900 -ErrorAction SilentlyContinue
if (-not $udpListener) {
$portsClosed += "UDP 1900"
Write-Log -Level SUCCESS -Message "Port UDP 1900 is CLOSED" -Module "AdvancedSecurity"
}
else {
$portsStillOpen += "UDP 1900"
Write-Log -Level WARNING -Message "Port UDP 1900 is still LISTENING locally. SSDP service is disabled and blocked by firewall; this listener is a known Windows behavior and not reachable from external networks." -Module "AdvancedSecurity"
}
# Summary
Write-Host ""
Write-Host "================================================" -ForegroundColor Green
Write-Host " RISKY SERVICES STOPPED" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Green
Write-Host ""
Write-Host "Services stopped: $stoppedCount" -ForegroundColor White
foreach ($svc in $services) {
$service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue
if ($service) {
$status = if ($service.Status -eq 'Stopped') { "STOPPED" } else { $service.Status }
$startType = $service.StartType
Write-Host " $($svc.DisplayName): $status (StartType: $startType)" -ForegroundColor Gray
}
}
Write-Host ""
Write-Host "Ports closed: $($portsClosed.Count)" -ForegroundColor White
foreach ($port in $portsClosed) {
Write-Host " $port" -ForegroundColor Green
}
if ($portsStillOpen.Count -gt 0) {
Write-Host ""
Write-Host "Ports still open: $($portsStillOpen.Count)" -ForegroundColor Yellow
foreach ($port in $portsStillOpen) {
Write-Host " $port" -ForegroundColor Yellow
}
if ($portsStillOpen -contains "UDP 1900") {
Write-Host ""
Write-Host "Note: UDP 1900 may still show a local listener, but SSDP is disabled and blocked by firewall. This is a known Windows behavior and not remotely reachable." -ForegroundColor Gray
}
}
if ($errors.Count -gt 0) {
Write-Host ""
Write-Host "Errors: $($errors.Count)" -ForegroundColor Red
foreach ($errorMsg in $errors) {
Write-Host " $errorMsg" -ForegroundColor Red
}
}
Write-Host ""
if ($errors.Count -eq 0) {
Write-Log -Level SUCCESS -Message "All risky services stopped and disabled successfully" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "Completed with $($errors.Count) errors" -Module "AdvancedSecurity"
return $true # Partial success
}
}
catch {
Write-Log -Level ERROR -Message "Failed to stop risky services: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}

View file

@ -0,0 +1,100 @@
function Test-AdminShares {
<#
.SYNOPSIS
Test administrative shares compliance
.DESCRIPTION
Checks if administrative shares (C$, ADMIN$, etc.) are disabled
.EXAMPLE
Test-AdminShares
#>
[CmdletBinding()]
param()
try {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters"
$result = [PSCustomObject]@{
Feature = "Admin Shares"
Status = "Unknown"
Details = @()
AutoShareWks = $null
AutoShareServer = $null
ActiveShares = @()
Compliant = $false
}
# Check registry settings
if (Test-Path $regPath) {
$result.AutoShareWks = (Get-ItemProperty -Path $regPath -Name "AutoShareWks" -ErrorAction SilentlyContinue).AutoShareWks
$result.AutoShareServer = (Get-ItemProperty -Path $regPath -Name "AutoShareServer" -ErrorAction SilentlyContinue).AutoShareServer
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
$result.Details += "Registry: AutoShareWks = 0, AutoShareServer = 0 (Disabled)"
}
else {
$result.Details += "Registry: AutoShareWks = $($result.AutoShareWks), AutoShareServer = $($result.AutoShareServer)"
}
}
# Check for active admin shares (requires LanmanServer service)
$serverService = Get-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
if (-not $serverService -or $serverService.Status -ne 'Running') {
# Server service is stopped/disabled - admin shares are effectively disabled
$result.Details += "LanmanServer service is not running (admin shares cannot exist)"
$adminShares = @()
}
else {
try {
$adminShares = Get-SmbShare | Where-Object { $_.Name -match '^[A-Z]\$$|^ADMIN\$$' }
}
catch {
# Get-SmbShare failed - treat as no shares
$result.Details += "Could not query SMB shares: $($_.Exception.Message)"
$adminShares = @()
}
}
$result.ActiveShares = $adminShares | Select-Object -ExpandProperty Name
if ($adminShares.Count -eq 0) {
$result.Details += "No administrative shares found (C$, ADMIN$ removed)"
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
$result.Status = "Secure"
$result.Compliant = $true
}
else {
$result.Status = "Partially Secure"
$result.Compliant = $false
$result.Details += "WARNING: Shares removed but AutoShare registry not set (will recreate on reboot!)"
}
}
else {
# Shares are present, check if Registry is configured to disable them
if ($result.AutoShareWks -eq 0 -and $result.AutoShareServer -eq 0) {
# Config is correct, just needs a reboot
$result.Status = "Pending Reboot"
$result.Compliant = $true
$result.Details += "Active admin shares: $($adminShares.Name -join ', ') (Will be removed after reboot)"
}
else {
# Config is NOT correct
$result.Status = "Insecure"
$result.Compliant = $false
$result.Details += "Active admin shares: $($adminShares.Name -join ', ')"
}
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test admin shares: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Admin Shares"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,121 @@
function Test-DiscoveryProtocolsSecurity {
<#
.SYNOPSIS
Tests WS-Discovery and mDNS hardening state.
.DESCRIPTION
Verifies that the following conditions are met:
- OS-level mDNS resolver disabled (EnableMDNS = 0)
- FDResPub and fdPHost services disabled and not running
- NoID firewall BLOCK rules for WS-Discovery and mDNS exist and are enabled
Returns a PSCustomObject with detailed fields and an overall Compliant flag.
.EXAMPLE
Test-DiscoveryProtocolsSecurity
#>
[CmdletBinding()]
param()
$result = [PSCustomObject]@{
EnableMDNS = $null
FDResPubDisabled = $false
FdPHostDisabled = $false
FirewallRulesPresent = $false
FirewallRulesEnabled = $false
Udp3702ListenersClosed = $null
Udp5353ListenersClosed = $null
Tcp5357ListenersClosed = $null
Tcp5358ListenersClosed = $null
Compliant = $false
}
try {
# 1) Check mDNS registry flag
$dnsParamsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters"
if (Test-Path $dnsParamsPath) {
$props = Get-ItemProperty -Path $dnsParamsPath -ErrorAction SilentlyContinue
if ($props.PSObject.Properties.Name -contains 'EnableMDNS') {
$result.EnableMDNS = $props.EnableMDNS
}
}
# 2) Check services
$fdResPub = Get-Service -Name "FDResPub" -ErrorAction SilentlyContinue
if ($fdResPub) {
$result.FDResPubDisabled = ($fdResPub.StartType -eq 'Disabled' -and $fdResPub.Status -ne 'Running')
}
$fdPHost = Get-Service -Name "fdPHost" -ErrorAction SilentlyContinue
if ($fdPHost) {
$result.FdPHostDisabled = ($fdPHost.StartType -eq 'Disabled' -and $fdPHost.Status -ne 'Running')
}
# 3) Check firewall rules
$ruleNames = @(
"NoID-Block-WSD-UDP-3702",
"NoID-Block-WSD-TCP-5357",
"NoID-Block-WSD-TCP-5358",
"NoID-Block-mDNS-UDP-5353"
)
$rules = @()
foreach ($name in $ruleNames) {
$r = Get-NetFirewallRule -Name $name -ErrorAction SilentlyContinue
if ($r) {
$rules += $r
}
}
if ($rules.Count -gt 0) {
$result.FirewallRulesPresent = ($rules.Count -eq $ruleNames.Count)
$result.FirewallRulesEnabled = ($rules | Where-Object { $_.Enabled -eq 'True' -and $_.Action -eq 'Block' }).Count -eq $ruleNames.Count
}
# 4) Optional: check that ports are not listening
try {
$udp3702 = Get-NetUDPEndpoint -LocalPort 3702 -ErrorAction SilentlyContinue
$result.Udp3702ListenersClosed = (-not $udp3702)
}
catch {
$result.Udp3702ListenersClosed = $null
}
try {
$udp5353 = Get-NetUDPEndpoint -LocalPort 5353 -ErrorAction SilentlyContinue
$result.Udp5353ListenersClosed = (-not $udp5353)
}
catch {
$result.Udp5353ListenersClosed = $null
}
try {
$tcp5357 = Get-NetTCPConnection -LocalPort 5357 -State Listen -ErrorAction SilentlyContinue
$result.Tcp5357ListenersClosed = (-not $tcp5357)
}
catch {
$result.Tcp5357ListenersClosed = $null
}
try {
$tcp5358 = Get-NetTCPConnection -LocalPort 5358 -State Listen -ErrorAction SilentlyContinue
$result.Tcp5358ListenersClosed = (-not $tcp5358)
}
catch {
$result.Tcp5358ListenersClosed = $null
}
# Overall compliance: mDNS disabled, services disabled, firewall rules present+enabled
$mdnsOk = ($result.EnableMDNS -eq 0)
$servicesOk = $result.FDResPubDisabled -and $result.FdPHostDisabled
$firewallOk = $result.FirewallRulesPresent -and $result.FirewallRulesEnabled
$result.Compliant = $mdnsOk -and $servicesOk -and $firewallOk
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test discovery protocol security (WS-Discovery/mDNS): $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $result
}
}

View file

@ -0,0 +1,57 @@
function Test-FingerProtocol {
<#
.SYNOPSIS
Test if Finger Protocol (TCP 79) is blocked
.DESCRIPTION
Verifies that the Windows Firewall rule blocking outbound TCP port 79
is present and enabled. This prevents ClickFix malware attacks that
abuse finger.exe to retrieve commands from C2 servers.
.OUTPUTS
PSCustomObject with compliance result
#>
[CmdletBinding()]
param()
try {
$ruleName = "NoID Privacy - Block Finger Protocol (Port 79)"
# Check if firewall rule exists and is enabled
$rule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
if ($rule) {
$isEnabled = $rule.Enabled -eq 'True'
$isBlocking = $rule.Action -eq 'Block'
$isOutbound = $rule.Direction -eq 'Outbound'
$compliant = $isEnabled -and $isBlocking -and $isOutbound
if ($compliant) {
$status = "Finger Protocol blocked (TCP 79 outbound)"
}
else {
$status = "Rule exists but misconfigured (Enabled: $isEnabled, Block: $isBlocking, Outbound: $isOutbound)"
}
}
else {
$compliant = $false
$status = "Firewall rule not found"
}
return [PSCustomObject]@{
Feature = "Finger Protocol Block"
Compliant = $compliant
Status = $status
Details = if ($rule) { "Rule: $ruleName" } else { "ClickFix malware protection not active" }
}
}
catch {
return [PSCustomObject]@{
Feature = "Finger Protocol Block"
Compliant = $false
Status = "Error checking: $($_.Exception.Message)"
Details = $null
}
}
}

View file

@ -0,0 +1,39 @@
function Test-FirewallShieldsUp {
<#
.SYNOPSIS
Test if Firewall Shields Up mode is enabled
.DESCRIPTION
Checks DoNotAllowExceptions value for PublicProfile firewall.
#>
[CmdletBinding()]
param()
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\PublicProfile"
$valueName = "DoNotAllowExceptions"
try {
$value = Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction SilentlyContinue
if ($null -eq $value -or $value.$valueName -ne 1) {
return @{
Pass = $false
Message = "Shields Up NOT enabled (Public network allows configured exceptions)"
CurrentValue = if ($null -eq $value) { "Not Set" } else { $value.$valueName }
}
}
return @{
Pass = $true
Message = "Shields Up ENABLED (Public network blocks ALL incoming)"
CurrentValue = 1
}
}
catch {
return @{
Pass = $false
Message = "Error checking Shields Up: $_"
CurrentValue = "Error"
}
}
}

View file

@ -0,0 +1,61 @@
function Test-IPv6Security {
<#
.SYNOPSIS
Test IPv6 disable status (mitm6 attack mitigation)
.DESCRIPTION
Checks if IPv6 is completely disabled via DisabledComponents registry value.
This is an OPTIONAL setting only available in Maximum profile.
DisabledComponents = 0xFF (255) means IPv6 is completely disabled.
.EXAMPLE
Test-IPv6Security
#>
[CmdletBinding()]
param()
try {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
$value = Get-ItemProperty -Path $regPath -Name "DisabledComponents" -ErrorAction SilentlyContinue
if ($value -and $value.DisabledComponents -eq 255) {
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true
Compliant = $true
Message = "IPv6 DISABLED (DisabledComponents = 0xFF) - mitm6 protected"
Details = "IPv6 completely disabled - DHCPv6 spoofing attacks blocked"
}
}
elseif ($value -and $value.DisabledComponents -gt 0) {
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true
Compliant = $true
Message = "IPv6 PARTIALLY disabled (DisabledComponents = $($value.DisabledComponents))"
Details = "IPv6 partially disabled - some mitm6 protection"
}
}
else {
# IPv6 is enabled - this is OPTIONAL, so still "pass" but note it's not configured
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true # Optional feature - not a failure
Compliant = $true # Optional feature
Message = "IPv6 ENABLED (Optional - not configured)"
Details = "IPv6 enabled (default) - WPAD disabled provides partial mitm6 protection"
}
}
}
catch {
Write-Log -Level ERROR -Message "Failed to test IPv6 security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Pass = $true # Don't fail on error for optional feature
Compliant = $true
Message = "Error checking IPv6 status"
Details = "Could not determine IPv6 status: $_"
}
}
}

View file

@ -0,0 +1,75 @@
function Test-LegacyTLS {
<#
.SYNOPSIS
Test Legacy TLS configuration compliance
.DESCRIPTION
Verifies that TLS 1.0 and TLS 1.1 are disabled for both Client and Server.
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Legacy TLS (1.0/1.1)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$tlsVersions = @("TLS 1.0", "TLS 1.1")
$components = @("Server", "Client")
$nonCompliantCount = 0
foreach ($version in $tlsVersions) {
foreach ($component in $components) {
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\$version\$component"
if (Test-Path $regPath) {
$enabled = (Get-ItemProperty -Path $regPath -Name "Enabled" -ErrorAction SilentlyContinue).Enabled
$disabledByDefault = (Get-ItemProperty -Path $regPath -Name "DisabledByDefault" -ErrorAction SilentlyContinue).DisabledByDefault
if ($enabled -eq 0) {
# Compliant
}
elseif ($null -eq $enabled -and $disabledByDefault -eq 1) {
# Compliant (implicitly disabled)
}
else {
$result.Details += "$version $component is NOT disabled (Enabled=$enabled)"
$nonCompliantCount++
}
}
else {
# Key missing usually means default (Enabled on old OS, Disabled on very new OS)
# For hardening, we expect explicit disable keys
$result.Details += "$version $component registry keys missing"
$nonCompliantCount++
}
}
}
if ($nonCompliantCount -eq 0) {
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure ($nonCompliantCount issues)"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test Legacy TLS: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "Legacy TLS (1.0/1.1)"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,61 @@
function Test-PowerShellV2 {
<#
.SYNOPSIS
Test PowerShell v2 status
.DESCRIPTION
Verifies that the PowerShell v2 feature is disabled or not present.
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "PowerShell v2 (Downgrade Attack)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$psv2Feature = $null
try {
$psv2Feature = Get-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -ErrorAction SilentlyContinue
}
catch {
$psv2Feature = $null
}
if (-not $psv2Feature) {
# Feature not present on OS - Secure by default
$result.Status = "Secure (Not Present)"
$result.Compliant = $true
$result.Details += "Feature 'MicrosoftWindowsPowerShellV2Root' not found on this OS"
}
elseif ($psv2Feature.State -ne 'Enabled') {
# Feature present but disabled - Secure
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
$result.Details += "Feature state: $($psv2Feature.State)"
}
else {
# Feature Enabled - Insecure
$result.Status = "Insecure (Enabled)"
$result.Compliant = $false
$result.Details += "PowerShell v2 is enabled (allows downgrade attacks)"
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test PowerShell v2: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "PowerShell v2"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,96 @@
function Test-RdpSecurity {
<#
.SYNOPSIS
Test RDP security hardening compliance
.DESCRIPTION
Verifies that RDP is properly hardened:
- NLA (Network Level Authentication) is enforced
- SSL/TLS encryption is required
- Optionally checks if RDP is completely disabled
.EXAMPLE
Test-RdpSecurity
Returns compliance status for RDP hardening
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$rdpRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
$rdpServerPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server"
$result = [PSCustomObject]@{
Feature = "RDP Security"
Status = "Unknown"
Details = @()
NLA_Enabled = $false
SSL_TLS_Enabled = $false
RDP_Disabled = $false
Compliant = $false
}
# Check NLA
if (Test-Path $rdpRegPath) {
$userAuth = (Get-ItemProperty -Path $rdpRegPath -Name "UserAuthentication" -ErrorAction SilentlyContinue).UserAuthentication
$secLayer = (Get-ItemProperty -Path $rdpRegPath -Name "SecurityLayer" -ErrorAction SilentlyContinue).SecurityLayer
if ($userAuth -eq 1) {
$result.NLA_Enabled = $true
$result.Details += "NLA enforced (UserAuthentication = 1)"
}
else {
$result.Details += "NLA NOT enforced (UserAuthentication = $userAuth)"
}
if ($secLayer -eq 2) {
$result.SSL_TLS_Enabled = $true
$result.Details += "SSL/TLS enforced (SecurityLayer = 2)"
}
else {
$result.Details += "SSL/TLS NOT enforced (SecurityLayer = $secLayer)"
}
}
else {
$result.Details += "RDP registry path not found"
}
# Check if RDP is completely disabled
if (Test-Path $rdpServerPath) {
$rdpDisabled = (Get-ItemProperty -Path $rdpServerPath -Name "fDenyTSConnections" -ErrorAction SilentlyContinue).fDenyTSConnections
if ($rdpDisabled -eq 1) {
$result.RDP_Disabled = $true
$result.Details += "RDP completely disabled (fDenyTSConnections = 1)"
}
}
# Determine compliance
if ($result.RDP_Disabled) {
$result.Status = "Secure (RDP Disabled)"
$result.Compliant = $true
}
elseif ($result.NLA_Enabled -and $result.SSL_TLS_Enabled) {
$result.Status = "Secure (NLA + SSL/TLS)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test RDP security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "RDP Security"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,134 @@
function Test-RiskyPorts {
<#
.SYNOPSIS
Test risky firewall ports compliance
.DESCRIPTION
Checks if risky firewall ports (LLMNR, NetBIOS, UPnP/SSDP) are closed
.EXAMPLE
Test-RiskyPorts
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Risky Firewall Ports"
Status = "Unknown"
Details = @()
OpenPorts = @()
DisabledRules = 0
EnabledRules = 0
Compliant = $false
}
$riskyPorts = @(5355, 137, 138, 139, 1900, 2869)
# Detect NoID SSDP firewall block rule for UDP 1900
$ssdpRuleName = "NoID Privacy - Block SSDP (UDP 1900)"
$ssdpBlockRule = Get-NetFirewallRule -DisplayName $ssdpRuleName -ErrorAction SilentlyContinue
$ssdpBlockActive = $false
if ($ssdpBlockRule -and $ssdpBlockRule.Enabled -eq 'True' -and $ssdpBlockRule.Action -eq 'Block') {
$ssdpBlockActive = $true
}
# PERFORMANCE FIX: Batch query instead of per-rule queries
# Old approach: Get-NetFirewallRule | ForEach { Get-NetFirewallPortFilter } = 300 queries × 200ms = 60s!
# New approach: Get all port filters once, then filter = 2-3s total
# Get all inbound firewall rules (pre-filter by direction)
$inboundRules = Get-NetFirewallRule -Direction Inbound -ErrorAction SilentlyContinue
# Get all port filters in one batch query
$allPortFilters = @{}
Get-NetFirewallPortFilter -ErrorAction SilentlyContinue | ForEach-Object {
$allPortFilters[$_.InstanceID] = $_
}
# Now filter rules by risky ports (fast lookup)
$riskyRules = $inboundRules | Where-Object {
$portFilter = $allPortFilters[$_.InstanceID]
if ($portFilter) {
($portFilter.LocalPort -in $riskyPorts) -or ($portFilter.RemotePort -in $riskyPorts)
}
else {
$false
}
}
foreach ($rule in $riskyRules) {
if ($rule.Enabled -eq $true) {
$portFilter = $allPortFilters[$rule.InstanceID]
if ($rule.Action -eq 'Allow') {
$result.EnabledRules++
$result.Details += "WARNING: Allow rule '$($rule.DisplayName)' is ENABLED (Port: $($portFilter.LocalPort))"
}
else {
$result.Details += "INFO: Block rule '$($rule.DisplayName)' is ENABLED (Port: $($portFilter.LocalPort))"
}
}
else {
$result.DisabledRules++
}
}
# Check actual port listeners
foreach ($port in $riskyPorts) {
if ($port -in @(137, 138, 139, 2869)) {
# TCP ports
$listener = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
if ($listener) {
$result.OpenPorts += "TCP $port"
$result.Details += "OPEN: TCP port $port is LISTENING!"
}
}
else {
# UDP ports (5355, 1900)
$listener = Get-NetUDPEndpoint -LocalPort $port -ErrorAction SilentlyContinue
if ($listener) {
$result.OpenPorts += "UDP $port"
$result.Details += "OPEN: UDP port $port is LISTENING!"
}
}
}
# Determine compliance
$udp1900Open = $result.OpenPorts -contains "UDP 1900"
$otherOpenPorts = $result.OpenPorts | Where-Object { $_ -ne "UDP 1900" }
if ($result.OpenPorts.Count -eq 0 -and $result.EnabledRules -eq 0) {
# Ideal case: no listeners and no allow rules
$result.Status = "Secure"
$result.Compliant = $true
$result.Details += "All risky ports closed and firewall rules disabled"
}
elseif ($udp1900Open -and -not $otherOpenPorts -and $result.EnabledRules -eq 0 -and $ssdpBlockActive) {
# Only open endpoint is UDP 1900, but protected by NoID block rule (inbound)
$result.Status = "Secure (blocked by firewall)"
$result.Compliant = $true
$result.Details += "UDP 1900 is listening locally but inbound traffic is blocked by '$ssdpRuleName'"
}
elseif ($result.OpenPorts.Count -eq 0 -and $result.EnabledRules -gt 0) {
$result.Status = "Partially Secure"
$result.Compliant = $false
$result.Details += "Ports closed but $($result.EnabledRules) firewall rules still enabled"
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test risky ports: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Risky Firewall Ports"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,89 @@
function Test-RiskyServices {
<#
.SYNOPSIS
Test risky network services compliance
.DESCRIPTION
Checks if risky network services (SSDPSRV, upnphost, lmhosts) are stopped and disabled
.EXAMPLE
Test-RiskyServices
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "Risky Network Services"
Status = "Unknown"
Details = @()
RunningServices = @()
StoppedServices = @()
Compliant = $false
}
# Note: Computer Browser (Browser) is deprecated in Win10/11 - not included
$services = @("SSDPSRV", "upnphost", "lmhosts")
foreach ($svcName in $services) {
$service = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if (-not $service) {
$result.Details += "$svcName - Not found (service may not be installed)"
continue
}
if ($service.Status -eq 'Running') {
$result.RunningServices += $svcName
$result.Details += "WARNING - $svcName is RUNNING (StartType: $($service.StartType))"
}
else {
$result.StoppedServices += $svcName
if ($service.StartType -eq 'Disabled') {
$result.Details += "${svcName}: Stopped and Disabled"
}
else {
$result.Details += "WARNING: ${svcName} is stopped but StartType is $($service.StartType) (should be Disabled)"
}
}
}
# Determine compliance
if ($result.RunningServices.Count -eq 0) {
$stoppedAndDisabled = $true
foreach ($svcName in $services) {
$service = Get-Service -Name $svcName -ErrorAction SilentlyContinue
if ($service -and $service.StartType -ne 'Disabled') {
$stoppedAndDisabled = $false
break
}
}
if ($stoppedAndDisabled) {
$result.Status = "Secure"
$result.Compliant = $true
}
else {
$result.Status = "Partially Secure"
$result.Compliant = $false
}
}
else {
$result.Status = "Insecure"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test risky services: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "Risky Network Services"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,126 @@
function Test-SRPCompliance {
<#
.SYNOPSIS
Verifies Software Restriction Policies (SRP) configuration for CVE-2025-9491
.DESCRIPTION
Tests whether SRP rules are correctly configured to block .lnk execution from Temp/Downloads.
Returns compliance status for CVE-2025-9491 mitigation.
.EXAMPLE
Test-SRPCompliance
.OUTPUTS
PSCustomObject with compliance results
#>
[CmdletBinding()]
param()
try {
$configPath = Join-Path $PSScriptRoot "..\Config\SRP-Rules.json"
if (-not (Test-Path $configPath)) {
return [PSCustomObject]@{
Feature = "SRP Configuration"
Status = "Not Configured"
Compliant = $false
Details = "SRP-Rules.json not found"
}
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$policyRoot = $config.RegistryPaths.PolicyRoot
# Check if SRP policy exists
if (-not (Test-Path $policyRoot)) {
Write-Log -Level WARNING -Message "SRP Check Failed: Policy root not found ($policyRoot)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Not Configured"
Compliant = $false
Details = "SRP policy root not found"
}
}
# Check Default Level
$defaultLevel = Get-ItemProperty -Path $policyRoot -Name "DefaultLevel" -ErrorAction SilentlyContinue
if ($null -eq $defaultLevel -or $defaultLevel.DefaultLevel -ne 262144) {
Write-Log -Level WARNING -Message "SRP Check Failed: DefaultLevel is not Unrestricted (262144)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Misconfigured"
Compliant = $false
Details = "Default level not set to Unrestricted (262144)"
}
}
# Check Path Rules
$pathRulesRoot = $config.RegistryPaths.PathRules
if (-not (Test-Path $pathRulesRoot)) {
Write-Log -Level WARNING -Message "SRP Check Failed: PathRules root not found ($pathRulesRoot)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Incomplete"
Compliant = $false
Details = "Path rules not configured"
}
}
# Count configured rules
$configuredRules = Get-ChildItem -Path $pathRulesRoot -ErrorAction SilentlyContinue
$ruleCount = if ($configuredRules) { $configuredRules.Count } else { 0 }
# Check for Windows 11 bug
$bugFixPath = $config.RegistryPaths.Win11BugFix
$hasBuggyKeys = $false
if (Test-Path $bugFixPath) {
foreach ($keyName in $config.Windows11BugFix.KeysToRemove) {
$keyExists = Get-ItemProperty -Path $bugFixPath -Name $keyName -ErrorAction SilentlyContinue
if ($null -ne $keyExists) {
$hasBuggyKeys = $true
break
}
}
}
if ($hasBuggyKeys) {
Write-Log -Level WARNING -Message "SRP Check Failed: Windows 11 buggy keys present (RuleCount/LastWriteTime)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Windows 11 Bug Detected"
Compliant = $false
Details = "Buggy registry keys present (RuleCount/LastWriteTime) - SRP may not work"
}
}
# All checks passed
if ($ruleCount -ge 2) {
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Protected"
Compliant = $true
Details = "$ruleCount path rules configured, Windows 11 bug fix applied"
}
}
else {
Write-Log -Level WARNING -Message "SRP Check Failed: Insufficient rules found ($ruleCount, expected 2+)" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Incomplete"
Compliant = $false
Details = "Only $ruleCount path rules found (expected 2+)"
}
}
}
catch {
return [PSCustomObject]@{
Feature = "SRP CVE-2025-9491"
Status = "Error"
Compliant = $false
Details = "Test failed: $_"
}
}
}

View file

@ -0,0 +1,130 @@
function Test-WDigest {
<#
.SYNOPSIS
Test WDigest credential protection compliance
.DESCRIPTION
Verifies that WDigest is configured to NOT store plaintext credentials in LSASS memory.
Checks the UseLogonCredential registry value.
Expected: UseLogonCredential = 0 (Secure)
Insecure: UseLogonCredential = 1 (Plaintext credentials in memory!)
.EXAMPLE
Test-WDigest
Returns compliance status for WDigest protection
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$wdigestRegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest"
$result = [PSCustomObject]@{
Feature = "WDigest Protection"
Status = "Unknown"
Details = @()
UseLogonCredential = $null
Compliant = $false
Windows_Version = ""
Deprecated = $false
}
# Get Windows version
$osVersion = [System.Environment]::OSVersion.Version
$isWin11 = $osVersion.Major -ge 10 -and $osVersion.Build -ge 22000
$isWin11_24H2Plus = $isWin11 -and $osVersion.Build -ge 26100
if ($isWin11) {
$result.Windows_Version = "Windows 11 (Build $($osVersion.Build))"
if ($isWin11_24H2Plus) {
$result.Deprecated = $true
$result.Details += "Windows 11 24H2+ detected - WDigest setting is deprecated"
}
}
elseif ($osVersion.Major -eq 10) {
$result.Windows_Version = "Windows 10 (Build $($osVersion.Build))"
}
else {
$result.Windows_Version = "Windows $($osVersion.Major).$($osVersion.Minor) (Build $($osVersion.Build))"
}
# Check registry value
if (Test-Path $wdigestRegPath) {
$useLogonCred = (Get-ItemProperty -Path $wdigestRegPath -Name "UseLogonCredential" -ErrorAction SilentlyContinue).UseLogonCredential
if ($null -ne $useLogonCred) {
$result.UseLogonCredential = $useLogonCred
if ($useLogonCred -eq 0) {
$result.Status = "Secure"
$result.Compliant = $true
$result.Details += "UseLogonCredential = 0 (Plaintext credentials NOT stored)"
if ($result.Deprecated) {
$result.Details += "Note: Setting is deprecated but explicitly configured for backwards compatibility"
}
}
elseif ($useLogonCred -eq 1) {
$result.Status = "INSECURE!"
$result.Compliant = $false
$result.Details += "WARNING: UseLogonCredential = 1 (Plaintext credentials IN MEMORY!)"
$result.Details += "VULNERABLE to Mimikatz, WCE, and other credential dumping tools!"
}
else {
$result.Status = "Unknown Value"
$result.Compliant = $false
$result.Details += "UseLogonCredential = $useLogonCred (Unknown value)"
}
}
else {
# Value not set - default depends on OS version
if ($osVersion.Major -eq 6 -and $osVersion.Minor -le 2) {
# Windows 7/8 - default is 1 (INSECURE!)
$result.Status = "Insecure (Default)"
$result.Compliant = $false
$result.Details += "UseLogonCredential not set - Windows 7/8 default is 1 (INSECURE!)"
}
else {
# Windows 8.1+ - default is 0 (Secure)
$result.Status = "Secure (Default)"
$result.Compliant = $true
$result.Details += "UseLogonCredential not set - Windows 8.1+ default is 0 (Secure)"
if ($result.Deprecated) {
$result.Details += "Windows 11 24H2+: Setting is hardcoded secure (deprecated)"
}
}
}
}
else {
# Registry path doesn't exist
if ($osVersion.Major -eq 6 -and $osVersion.Minor -le 2) {
# Windows 7/8
$result.Status = "Insecure (No Config)"
$result.Compliant = $false
$result.Details += "WDigest registry path not found - Windows 7/8 default is INSECURE!"
}
else {
# Windows 8.1+
$result.Status = "Secure (Default)"
$result.Compliant = $true
$result.Details += "WDigest registry path not found - Windows 8.1+ default is secure"
}
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test WDigest protection: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return [PSCustomObject]@{
Feature = "WDigest Protection"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,88 @@
function Test-WPAD {
<#
.SYNOPSIS
Test WPAD configuration compliance
.DESCRIPTION
Verifies that Web Proxy Auto-Discovery (WPAD) is disabled using the official
Microsoft-recommended key plus legacy keys for compatibility.
Reference: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/disable-http-proxy-auth-features
.OUTPUTS
PSCustomObject with compliance details
#>
[CmdletBinding()]
param()
try {
$result = [PSCustomObject]@{
Feature = "WPAD (Proxy Auto-Discovery)"
Status = "Unknown"
Details = @()
Compliant = $true
}
$wpadKeys = @(
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp"
Name = "DisableWpad"
Expected = 1
Description = "Official MS key (Win10 1809+)"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Wpad"
Name = "WpadOverride"
Expected = 1
Description = "Legacy override key"
},
@{
Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"
Name = "AutoDetect"
Expected = 0
Description = "Browser-level HKLM"
}
)
$nonCompliantCount = 0
foreach ($key in $wpadKeys) {
if (Test-Path $key.Path) {
$val = (Get-ItemProperty -Path $key.Path -Name $key.Name -ErrorAction SilentlyContinue).($key.Name)
if ($val -eq $key.Expected) {
# Compliant
}
else {
$result.Details += "$($key.Name) is NOT set to $($key.Expected) (Current: $val)"
$nonCompliantCount++
}
}
else {
# Key missing
$result.Details += "Registry key missing: $($key.Path)"
$nonCompliantCount++
}
}
if ($nonCompliantCount -eq 0) {
$result.Status = "Secure (Disabled)"
$result.Compliant = $true
}
else {
$result.Status = "Insecure ($nonCompliantCount issues)"
$result.Compliant = $false
}
return $result
}
catch {
Write-Log -Level ERROR -Message "Failed to test WPAD: $_" -Module "AdvancedSecurity"
return [PSCustomObject]@{
Feature = "WPAD (Proxy Auto-Discovery)"
Status = "Error"
Details = @("Failed to test: $_")
Compliant = $false
}
}
}

View file

@ -0,0 +1,91 @@
function Test-WindowsUpdate {
<#
.SYNOPSIS
Verifies Windows Update configuration (3 simple GUI settings)
.DESCRIPTION
Tests whether the 3 Windows Update GUI settings are properly configured:
1. Get latest updates immediately
2. Microsoft Update for other products
3. Delivery Optimization disabled
.EXAMPLE
Test-WindowsUpdate
.OUTPUTS
PSCustomObject with compliance results
#>
[CmdletBinding()]
param()
try {
$configPath = Join-Path $PSScriptRoot "..\Config\WindowsUpdate.json"
if (-not (Test-Path $configPath)) {
return [PSCustomObject]@{
Feature = "Windows Update"
Status = "Not Configured"
Compliant = $false
Details = "WindowsUpdate.json not found"
}
}
$config = Get-Content $configPath -Raw | ConvertFrom-Json
$settingsConfigured = 0
$settingsTotal = 0
$details = @()
# Check all 3 settings from config
foreach ($settingKey in $config.Settings.PSObject.Properties.Name) {
$setting = $config.Settings.$settingKey
$regPath = $setting.RegistryPath
foreach ($valueName in $setting.Values.PSObject.Properties.Name) {
$valueData = $setting.Values.$valueName
$settingsTotal++
if (Test-Path $regPath) {
$actual = Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction SilentlyContinue
if ($null -ne $actual -and $actual.$valueName -eq $valueData.Value) {
$settingsConfigured++
$details += "$($setting.Name): OK"
}
else {
$details += "$($setting.Name): NOT SET"
Write-Log -Level WARNING -Message "Windows Update Check Failed: $($setting.Name)" -Module "AdvancedSecurity"
if ($null -eq $actual) {
Write-Log -Level WARNING -Message " - Value '$valueName' not found in $regPath" -Module "AdvancedSecurity"
} else {
Write-Log -Level WARNING -Message " - Value '$valueName' mismatch. Expected: $($valueData.Value), Actual: $($actual.$valueName)" -Module "AdvancedSecurity"
}
}
}
else {
$details += "$($setting.Name): NOT SET (reg path missing)"
Write-Log -Level WARNING -Message "Windows Update Check Failed: $($setting.Name)" -Module "AdvancedSecurity"
Write-Log -Level WARNING -Message " - Registry Path Missing: $regPath" -Module "AdvancedSecurity"
}
}
}
$compliant = ($settingsConfigured -eq $settingsTotal)
return [PSCustomObject]@{
Feature = "Windows Update"
Status = if ($compliant) { "Configured" } else { "Incomplete" }
Compliant = $compliant
Details = "$settingsConfigured/$settingsTotal settings OK. $(if ($details) { $details -join ', ' })"
}
}
catch {
return [PSCustomObject]@{
Feature = "Windows Update"
Status = "Error"
Compliant = $false
Details = "Test failed: $_"
}
}
}

View file

@ -0,0 +1,70 @@
function Test-WirelessDisplaySecurity {
<#
.SYNOPSIS
Tests Wireless Display (Miracast) security configuration.
.DESCRIPTION
Verifies that Wireless Display policies are configured securely:
- AllowProjectionToPC = 0 (blocking receiving)
- RequirePinForPairing = 2 (always require PIN)
- Optionally: Complete disable of all Wireless Display
.EXAMPLE
Test-WirelessDisplaySecurity
#>
[CmdletBinding()]
param()
try {
$connectPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Connect"
$results = @{
AllowProjectionToPC = $null
RequirePinForPairing = $null
AllowProjectionFromPC = $null
AllowMdnsAdvertisement = $null
AllowMdnsDiscovery = $null
WiFiDirectServiceDisabled = $null
Compliant = $false
FullyDisabled = $false
}
if (Test-Path $connectPath) {
$props = Get-ItemProperty -Path $connectPath -ErrorAction SilentlyContinue
# Check basic hardening (always required)
$results.AllowProjectionToPC = $props.AllowProjectionToPC
$results.RequirePinForPairing = $props.RequirePinForPairing
# Check optional complete disable
$results.AllowProjectionFromPC = $props.AllowProjectionFromPC
$results.AllowMdnsAdvertisement = $props.AllowMdnsAdvertisement
$results.AllowMdnsDiscovery = $props.AllowMdnsDiscovery
# Check WiFi Direct Service status (CRITICAL for complete block)
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
$results.WiFiDirectServiceDisabled = ($null -eq $wfdService) -or ($wfdService.StartType -eq 'Disabled')
# Basic compliance: receiving blocked + PIN required
$results.Compliant = ($results.AllowProjectionToPC -eq 0) -and ($results.RequirePinForPairing -eq 2)
# Fully disabled: all settings at 0/2 AND WiFi Direct service disabled
$results.FullyDisabled = $results.Compliant -and
($results.AllowProjectionFromPC -eq 0) -and
($results.AllowMdnsAdvertisement -eq 0) -and
($results.AllowMdnsDiscovery -eq 0) -and
$results.WiFiDirectServiceDisabled
}
else {
# Key doesn't exist = not hardened
$results.Compliant = $false
$results.FullyDisabled = $false
}
return [PSCustomObject]$results
}
catch {
Write-Log -Level ERROR -Message "Failed to test Wireless Display security: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $null
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,312 @@
function Restore-AdvancedSecuritySettings {
<#
.SYNOPSIS
Restore Advanced Security settings from backup
.DESCRIPTION
Restores custom Advanced Security settings that are not handled by the generic
registry/service restore logic. This includes:
- Firewall Rules (Risky Ports)
- Windows Features (PowerShell v2)
- SMB Shares (Admin Shares)
.PARAMETER BackupFilePath
Path to the JSON backup file
.OUTPUTS
Boolean indicating success
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory = $true)]
[string]$BackupFilePath
)
if (-not (Test-Path $BackupFilePath)) {
Write-Log -Level ERROR -Message "Backup file not found: $BackupFilePath" -Module "AdvancedSecurity"
return $false
}
try {
$filename = Split-Path $BackupFilePath -Leaf
Write-Log -Level INFO -Message "Processing Advanced Security backup: $filename" -Module "AdvancedSecurity"
# Skip Empty Marker files - these are already processed by generic Empty Marker logic in Core/Rollback.ps1
if ($filename -match "_EMPTY\.json$") {
Write-Log -Level DEBUG -Message "Skipping Empty Marker file (already processed): $filename" -Module "AdvancedSecurity"
return $true # Success - nothing to do here
}
# Load backup data
$backupData = Get-Content -Path $BackupFilePath -Raw | ConvertFrom-Json
# Determine backup type based on filename or content
if ($filename -match "RiskyPorts_Firewall") {
return Restore-FirewallRules -BackupData $backupData
}
elseif ($filename -match "PowerShellV2") {
return Restore-PowerShellV2 -BackupData $backupData
}
elseif ($filename -match "AdminShares") {
return Restore-AdminShares -BackupData $backupData
}
elseif ($filename -match "NetBIOS_Adapters") {
return Restore-NetBIOSAdapters -BackupData $backupData
}
elseif ($filename -match "RDP_Hardening") {
# RDP settings are already restored via the Smart JSON-Fallback mechanism in Rollback.ps1
# This JSON backup serves as a fallback and doesn't require separate restore logic
Write-Log -Level DEBUG -Message "RDP_Hardening.json acknowledged (already handled by Smart JSON-Fallback)" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "Unknown Advanced Security backup type: $filename" -Module "AdvancedSecurity"
return $false
}
}
catch {
Write-Log -Level ERROR -Message "Failed to restore Advanced Security settings: $_" -Module "AdvancedSecurity" -Exception $_.Exception
return $false
}
}
function Restore-FirewallRules {
param($BackupData)
Write-Log -Level INFO -Message "Restoring firewall rules..." -Module "AdvancedSecurity"
try {
# 1. Remove rules created by hardening (identified by Group or Name pattern)
# The hardening module creates rules with specific names/groups.
# Since we don't have the exact names of created rules stored in a "CreatedRules" list here,
# we rely on the fact that we are restoring the *previous* state.
# However, for firewall rules, "restoring" usually means:
# 1. Deleting the BLOCK rules we added
# 2. Re-enabling any rules we disabled (if any)
# The backup contains a SNAPSHOT of rules matching the risky ports.
# We should restore their state (Enabled/Disabled, Action).
if ($BackupData.Rules) {
foreach ($rule in $BackupData.Rules) {
# Check if rule exists
$currentRule = Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue
if ($currentRule) {
# Restore state
Set-NetFirewallRule -Name $rule.Name `
-Enabled $rule.Enabled `
-Action $rule.Action `
-ErrorAction SilentlyContinue
Write-Log -Level DEBUG -Message "Restored rule state: $($rule.Name)" -Module "AdvancedSecurity"
}
}
}
# Also remove the specific block rules added by AdvancedSecurity
# These include:
# - Block Risky Port * (legacy patterns)
# - NoID Privacy - Block Finger Protocol (Port 79)
# - NoID Privacy - Block SSDP (UDP 1900)
# - Block Admin Shares - NoID Privacy (TCP 445 on Public profile)
$blockRules = Get-NetFirewallRule -DisplayName "Block Risky Port *" -ErrorAction SilentlyContinue
if ($blockRules) {
Remove-NetFirewallRule -InputObject $blockRules -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Removed $($blockRules.Count) hardening block rules" -Module "AdvancedSecurity"
}
# Remove Finger Protocol rule (corrected name with NoID prefix)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Finger Protocol (Port 79)" -ErrorAction SilentlyContinue
# Remove SSDP block rule (UDP 1900)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block SSDP (UDP 1900)" -ErrorAction SilentlyContinue
# Remove WS-Discovery and mDNS block rules (Maximum profile discovery hardening)
Remove-NetFirewallRule -Name "NoID-Block-WSD-UDP-3702" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-WSD-TCP-5357" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-WSD-TCP-5358" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -Name "NoID-Block-mDNS-UDP-5353" -ErrorAction SilentlyContinue
# Remove Admin Shares SMB block rule (TCP 445 on Public profile)
Remove-NetFirewallRule -DisplayName "Block Admin Shares - NoID Privacy" -ErrorAction SilentlyContinue
# Remove Miracast/Wireless Display block rules (Ports 7236, 7250)
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast TCP 7236" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast TCP 7250" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast UDP 7236" -ErrorAction SilentlyContinue
Remove-NetFirewallRule -DisplayName "NoID Privacy - Block Miracast UDP 7250" -ErrorAction SilentlyContinue
# Re-enable WiFi Direct Service (WFDSConMgrSvc) for Miracast functionality
$wfdService = Get-Service -Name "WFDSConMgrSvc" -ErrorAction SilentlyContinue
if ($wfdService -and $wfdService.StartType -eq 'Disabled') {
Set-Service -Name "WFDSConMgrSvc" -StartupType Manual -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Re-enabled WiFi Direct Service (WFDSConMgrSvc)" -Module "AdvancedSecurity"
}
# Re-enable WiFi Direct Virtual Adapters
Get-NetAdapter -InterfaceDescription "Microsoft Wi-Fi Direct Virtual*" -IncludeHidden -ErrorAction SilentlyContinue |
Where-Object { $_.Status -eq 'Disabled' } |
ForEach-Object {
Enable-NetAdapter -Name $_.Name -Confirm:$false -ErrorAction SilentlyContinue
Write-Log -Level INFO -Message "Re-enabled WiFi Direct adapter: $($_.Name)" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore firewall rules: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-PowerShellV2 {
param($BackupData)
Write-Log -Level INFO -Message "Restoring PowerShell v2 state..." -Module "AdvancedSecurity"
try {
$shouldEnable = ($BackupData.State -eq "Enabled")
# Check current state
$psv2RegPath = "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine"
$psv2EngineVersion = (Get-ItemProperty -Path $psv2RegPath -Name "PowerShellVersion" -ErrorAction SilentlyContinue).PowerShellVersion
$isEnabled = ($null -ne $psv2EngineVersion -and $psv2EngineVersion -like "2.*")
if ($shouldEnable -and -not $isEnabled) {
Write-Log -Level INFO -Message "Re-enabling PowerShell v2 (via DISM)..." -Module "AdvancedSecurity"
Enable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop | Out-Null
}
elseif (-not $shouldEnable -and $isEnabled) {
Write-Log -Level INFO -Message "Disabling PowerShell v2 (via DISM)..." -Module "AdvancedSecurity"
Disable-WindowsOptionalFeature -Online -FeatureName "MicrosoftWindowsPowerShellV2Root" -NoRestart -ErrorAction Stop | Out-Null
}
else {
Write-Log -Level INFO -Message "PowerShell v2 state already matches backup ($($BackupData.State))" -Module "AdvancedSecurity"
}
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore PowerShell v2: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-AdminShares {
# Note: No parameters needed - registry restore happens separately via Core\Rollback.ps1
Write-Log -Level INFO -Message "Restoring Admin Shares..." -Module "AdvancedSecurity"
try {
# The backup contains a list of shares that existed.
# If we disabled them, they might be gone or the AutoShareServer/Wks registry keys were changed.
# Registry keys are handled by the generic Registry restore!
# So we mainly need to verify if we need to manually recreate shares or if registry restore + reboot is enough.
# Changing AutoShareServer/AutoShareWks requires a reboot to take effect.
# So simply restoring the registry keys (which happens before this) should be sufficient for the next boot.
# However, we can try to force re-creation if possible, but usually LanmanServer needs restart.
Write-Log -Level INFO -Message "Admin Shares settings restored via Registry. A reboot is required to fully restore shares." -Module "AdvancedSecurity"
return $true
}
catch {
Write-Log -Level ERROR -Message "Failed to restore Admin Shares: $_" -Module "AdvancedSecurity"
return $false
}
}
function Restore-NetBIOSAdapters {
<#
.SYNOPSIS
Restore NetBIOS over TCP/IP settings on network adapters
.DESCRIPTION
Restores the TcpipNetbiosOptions setting on each network adapter
to its pre-hardening state.
TcpipNetbiosOptions values:
- 0 = Default (use DHCP option)
- 1 = Enable NetBIOS over TCP/IP
- 2 = Disable NetBIOS over TCP/IP (set by hardening)
.PARAMETER BackupData
JSON backup data containing adapter descriptions and their original TcpipNetbiosOptions
#>
param($BackupData)
Write-Log -Level INFO -Message "Restoring NetBIOS over TCP/IP settings on network adapters..." -Module "AdvancedSecurity"
try {
# BackupData can be an array directly or have a nested structure
$adaptersToRestore = if ($BackupData -is [Array]) { $BackupData } else { @($BackupData) }
if ($adaptersToRestore.Count -eq 0) {
Write-Log -Level INFO -Message "No NetBIOS adapter settings to restore" -Module "AdvancedSecurity"
return $true
}
$restoredCount = 0
$failedCount = 0
# Get current adapters
$currentAdapters = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled = TRUE" -ErrorAction SilentlyContinue
foreach ($backupAdapter in $adaptersToRestore) {
try {
# Find matching adapter by Index (most reliable) or Description
$targetAdapter = $currentAdapters | Where-Object {
$_.Index -eq $backupAdapter.Index -or
$_.Description -eq $backupAdapter.Description
} | Select-Object -First 1
if ($targetAdapter) {
$originalSetting = $backupAdapter.TcpipNetbiosOptions
# Only restore if different from current
if ($targetAdapter.TcpipNetbiosOptions -ne $originalSetting) {
$result = Invoke-CimMethod -InputObject $targetAdapter -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions = $originalSetting }
if ($result.ReturnValue -eq 0) {
Write-Log -Level DEBUG -Message "Restored NetBIOS setting on adapter '$($targetAdapter.Description)' to $originalSetting" -Module "AdvancedSecurity"
$restoredCount++
}
else {
Write-Log -Level WARNING -Message "SetTcpipNetbios returned $($result.ReturnValue) for adapter '$($targetAdapter.Description)'" -Module "AdvancedSecurity"
$failedCount++
}
}
else {
Write-Log -Level DEBUG -Message "NetBIOS setting on adapter '$($targetAdapter.Description)' already matches backup ($originalSetting)" -Module "AdvancedSecurity"
$restoredCount++
}
}
else {
Write-Log -Level WARNING -Message "Adapter not found for restore: Index=$($backupAdapter.Index), Description='$($backupAdapter.Description)'" -Module "AdvancedSecurity"
$failedCount++
}
}
catch {
Write-Log -Level WARNING -Message "Failed to restore NetBIOS on adapter '$($backupAdapter.Description)': $_" -Module "AdvancedSecurity"
$failedCount++
}
}
if ($failedCount -eq 0) {
Write-Log -Level SUCCESS -Message "NetBIOS settings restored on $restoredCount adapter(s)" -Module "AdvancedSecurity"
return $true
}
else {
Write-Log -Level WARNING -Message "NetBIOS restore completed with issues: $restoredCount succeeded, $failedCount failed" -Module "AdvancedSecurity"
return $true # Still return true - partial success is acceptable
}
}
catch {
Write-Log -Level ERROR -Message "Failed to restore NetBIOS adapter settings: $_" -Module "AdvancedSecurity"
return $false
}
}

View file

@ -0,0 +1,188 @@
function Test-AdvancedSecurity {
<#
.SYNOPSIS
Test Advanced Security compliance
.DESCRIPTION
Runs all compliance tests for Advanced Security hardening and returns
a comprehensive report of the current security posture.
Tests include:
- RDP Security (NLA enforcement, SSL/TLS, disable status)
- WDigest Protection (credential caching disabled)
- Administrative Shares (disabled and removed)
- Risky Firewall Ports (LLMNR, NetBIOS, UPnP/SSDP closed)
- Risky Network Services (SSDPSRV, upnphost, lmhosts stopped)
- Discovery Protocols (WS-Discovery, mDNS)
.EXAMPLE
Test-AdvancedSecurity
Runs all compliance tests and displays results
.EXAMPLE
$results = Test-AdvancedSecurity
$results | Format-Table
.OUTPUTS
Array of PSCustomObjects with compliance results
#>
[CmdletBinding()]
param()
try {
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " ADVANCED SECURITY COMPLIANCE TEST" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
$results = @()
# 1. RDP Security
Write-Host "Testing RDP Security..." -ForegroundColor Gray
$rdpTest = Test-RdpSecurity
$results += $rdpTest
# 2. WDigest Protection
Write-Host "Testing WDigest Protection..." -ForegroundColor Gray
$wdigestTest = Test-WDigest
$results += $wdigestTest
# 3. Admin Shares
Write-Host "Testing Administrative Shares..." -ForegroundColor Gray
$adminSharesTest = Test-AdminShares
$results += $adminSharesTest
# 4. Legacy TLS
Write-Host "Testing Legacy TLS (1.0/1.1)..." -ForegroundColor Gray
$tlsTest = Test-LegacyTLS
$results += $tlsTest
# 5. WPAD
Write-Host "Testing WPAD Configuration..." -ForegroundColor Gray
$wpadTest = Test-WPAD
$results += $wpadTest
# 6. PowerShell v2
Write-Host "Testing PowerShell v2 Status..." -ForegroundColor Gray
$psv2Test = Test-PowerShellV2
$results += $psv2Test
# 7. Risky Ports
Write-Host "Testing Risky Firewall Ports..." -ForegroundColor Gray
$riskyPortsTest = Test-RiskyPorts
$results += $riskyPortsTest
# 8. Risky Services
Write-Host "Testing Risky Network Services..." -ForegroundColor Gray
$riskyServicesTest = Test-RiskyServices
$results += $riskyServicesTest
# 9. SRP Configuration (CVE-2025-9491)
Write-Host "Testing SRP Configuration (CVE-2025-9491)..." -ForegroundColor Gray
$srpTest = Test-SRPCompliance
$results += $srpTest
# 10. Windows Update Configuration
Write-Host "Testing Windows Update Configuration..." -ForegroundColor Gray
$wuTest = Test-WindowsUpdate
$results += $wuTest
# 11. Finger Protocol Block
Write-Host "Testing Finger Protocol Block..." -ForegroundColor Gray
$fingerTest = Test-FingerProtocol
$results += $fingerTest
# 12. Wireless Display Security
Write-Host "Testing Wireless Display Security..." -ForegroundColor Gray
$wirelessDisplayTest = Test-WirelessDisplaySecurity
if ($wirelessDisplayTest) {
$results += [PSCustomObject]@{
Feature = "Wireless Display Security"
Compliant = $wirelessDisplayTest.Compliant
Details = if ($wirelessDisplayTest.FullyDisabled) { "Fully Disabled" }
elseif ($wirelessDisplayTest.Compliant) { "Hardened (receiving blocked, PIN required)" }
else { "NOT HARDENED - screen interception possible!" }
}
}
# 13. Discovery Protocols (WS-Discovery + mDNS) - Maximum profile only
Write-Host "Testing Discovery Protocols (WS-Discovery + mDNS)..." -ForegroundColor Gray
$discoveryTest = Test-DiscoveryProtocolsSecurity
if ($discoveryTest) {
$results += [PSCustomObject]@{
Feature = "Discovery Protocols (WS-Discovery + mDNS)"
Status = if ($discoveryTest.Compliant) { "Secure" } else { "Insecure" }
Details = "mDNS=" + $(if ($discoveryTest.EnableMDNS -eq 0) { "Disabled" } else { "Enabled/Not Set" }) +
"; Services: FDResPub=" + $discoveryTest.FDResPubDisabled + ", fdPHost=" + $discoveryTest.FdPHostDisabled +
"; FirewallRulesEnabled=" + $discoveryTest.FirewallRulesEnabled
Compliant = $discoveryTest.Compliant
}
}
# 14. Firewall Shields Up (optional - Maximum profile only)
Write-Host "Testing Firewall Shields Up (Public)..." -ForegroundColor Gray
$shieldsUpTest = Test-FirewallShieldsUp
# Always pass - this is an optional hardening only for the Maximum (air-gapped) profile
$results += [PSCustomObject]@{
Feature = "Firewall Shields Up (Public)"
Compliant = $shieldsUpTest.Pass
Details = $shieldsUpTest.Message
}
# 15. IPv6 Disable (optional - Maximum profile only, mitm6 mitigation)
Write-Host "Testing IPv6 Security (mitm6 mitigation)..." -ForegroundColor Gray
$ipv6Test = Test-IPv6Security
# Always pass - this is an optional hardening only for the Maximum profile
$results += [PSCustomObject]@{
Feature = "IPv6 Disable (mitm6 mitigation)"
Compliant = $ipv6Test.Pass
Details = $ipv6Test.Message
}
# Summary
Write-Host ""
Write-Host "============================================" -ForegroundColor Cyan
Write-Host " COMPLIANCE SUMMARY" -ForegroundColor Cyan
Write-Host "============================================" -ForegroundColor Cyan
Write-Host ""
$compliantCount = ($results | Where-Object { $_.Compliant -eq $true }).Count
$totalTests = $results.Count
$compliancePercent = [math]::Round(($compliantCount / $totalTests) * 100, 1)
Write-Host "Total Tests: $totalTests" -ForegroundColor White
Write-Host "Compliant: $compliantCount" -ForegroundColor Green
Write-Host "Non-Compliant: $($totalTests - $compliantCount)" -ForegroundColor Red
Write-Host "Compliance: $compliancePercent%" -ForegroundColor $(if ($compliancePercent -ge 80) { 'Green' } elseif ($compliancePercent -ge 50) { 'Yellow' } else { 'Red' })
Write-Host ""
# Detailed results table
Write-Host "DETAILED RESULTS:" -ForegroundColor White
Write-Host ""
$tableFormat = @{Expression = { $_.Feature }; Label = "Feature"; Width = 30 },
@{Expression = { $_.Status }; Label = "Status"; Width = 20 },
@{Expression = { if ($_.Compliant) { "[X]" }else { "[ ]" } }; Label = "Compliant"; Width = 10 }
$results | Format-Table $tableFormat -AutoSize
Write-Host ""
# Return structured object with metadata for programmatic use
return [PSCustomObject]@{
Results = $results
TotalChecks = $totalTests
CompliantCount = $compliantCount
Compliance = $compliancePercent
}
}
catch {
Write-Log -Level ERROR -Message "Failed to run compliance tests: $_" -Module "AdvancedSecurity" -Exception $_.Exception
Write-Host ""
Write-Host "ERROR: Failed to run compliance tests" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Gray
Write-Host ""
return $null
}
}