mirror of
https://github.com/NexusOne23/noid-privacy.git
synced 2026-02-26 13:14:17 +01:00
v2.2.0 - Complete Security Hardening Framework (632 Settings)
This commit is contained in:
commit
ba364813ed
195 changed files with 43788 additions and 0 deletions
73
Modules/AdvancedSecurity/AdvancedSecurity.psd1
Normal file
73
Modules/AdvancedSecurity/AdvancedSecurity.psd1
Normal 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
|
||||
'@
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Modules/AdvancedSecurity/AdvancedSecurity.psm1
Normal file
65
Modules/AdvancedSecurity/AdvancedSecurity.psm1
Normal 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
|
||||
116
Modules/AdvancedSecurity/Config/AdminShares.json
Normal file
116
Modules/AdvancedSecurity/Config/AdminShares.json
Normal 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)"
|
||||
}
|
||||
}
|
||||
78
Modules/AdvancedSecurity/Config/Credentials.json
Normal file
78
Modules/AdvancedSecurity/Config/Credentials.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
Modules/AdvancedSecurity/Config/Firewall.json
Normal file
20
Modules/AdvancedSecurity/Config/Firewall.json
Normal 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)"
|
||||
}
|
||||
}
|
||||
64
Modules/AdvancedSecurity/Config/RDP.json
Normal file
64
Modules/AdvancedSecurity/Config/RDP.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
85
Modules/AdvancedSecurity/Config/SRP-Rules.json
Normal file
85
Modules/AdvancedSecurity/Config/SRP-Rules.json
Normal 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)"
|
||||
}
|
||||
}
|
||||
57
Modules/AdvancedSecurity/Config/WindowsUpdate.json
Normal file
57
Modules/AdvancedSecurity/Config/WindowsUpdate.json
Normal 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."
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
107
Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1
Normal file
107
Modules/AdvancedSecurity/Private/Block-FingerProtocol.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
219
Modules/AdvancedSecurity/Private/Disable-AdminShares.ps1
Normal file
219
Modules/AdvancedSecurity/Private/Disable-AdminShares.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
73
Modules/AdvancedSecurity/Private/Disable-LegacyTLS.ps1
Normal file
73
Modules/AdvancedSecurity/Private/Disable-LegacyTLS.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
245
Modules/AdvancedSecurity/Private/Disable-RiskyPorts.ps1
Normal file
245
Modules/AdvancedSecurity/Private/Disable-RiskyPorts.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
147
Modules/AdvancedSecurity/Private/Disable-WPAD.ps1
Normal file
147
Modules/AdvancedSecurity/Private/Disable-WPAD.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
172
Modules/AdvancedSecurity/Private/Enable-RdpNLA.ps1
Normal file
172
Modules/AdvancedSecurity/Private/Enable-RdpNLA.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
98
Modules/AdvancedSecurity/Private/Remove-PowerShellV2.ps1
Normal file
98
Modules/AdvancedSecurity/Private/Remove-PowerShellV2.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
66
Modules/AdvancedSecurity/Private/Set-FirewallShieldsUp.ps1
Normal file
66
Modules/AdvancedSecurity/Private/Set-FirewallShieldsUp.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
102
Modules/AdvancedSecurity/Private/Set-IPv6Security.ps1
Normal file
102
Modules/AdvancedSecurity/Private/Set-IPv6Security.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
192
Modules/AdvancedSecurity/Private/Set-SRPRules.ps1
Normal file
192
Modules/AdvancedSecurity/Private/Set-SRPRules.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
113
Modules/AdvancedSecurity/Private/Set-WDigestProtection.ps1
Normal file
113
Modules/AdvancedSecurity/Private/Set-WDigestProtection.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
110
Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1
Normal file
110
Modules/AdvancedSecurity/Private/Set-WindowsUpdate.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
200
Modules/AdvancedSecurity/Private/Set-WirelessDisplaySecurity.ps1
Normal file
200
Modules/AdvancedSecurity/Private/Set-WirelessDisplaySecurity.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
216
Modules/AdvancedSecurity/Private/Stop-RiskyServices.ps1
Normal file
216
Modules/AdvancedSecurity/Private/Stop-RiskyServices.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
100
Modules/AdvancedSecurity/Private/Test-AdminShares.ps1
Normal file
100
Modules/AdvancedSecurity/Private/Test-AdminShares.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
57
Modules/AdvancedSecurity/Private/Test-FingerProtocol.ps1
Normal file
57
Modules/AdvancedSecurity/Private/Test-FingerProtocol.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Modules/AdvancedSecurity/Private/Test-FirewallShieldsUp.ps1
Normal file
39
Modules/AdvancedSecurity/Private/Test-FirewallShieldsUp.ps1
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Modules/AdvancedSecurity/Private/Test-IPv6Security.ps1
Normal file
61
Modules/AdvancedSecurity/Private/Test-IPv6Security.ps1
Normal 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: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Modules/AdvancedSecurity/Private/Test-LegacyTLS.ps1
Normal file
75
Modules/AdvancedSecurity/Private/Test-LegacyTLS.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Modules/AdvancedSecurity/Private/Test-PowerShellV2.ps1
Normal file
61
Modules/AdvancedSecurity/Private/Test-PowerShellV2.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
96
Modules/AdvancedSecurity/Private/Test-RdpSecurity.ps1
Normal file
96
Modules/AdvancedSecurity/Private/Test-RdpSecurity.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Modules/AdvancedSecurity/Private/Test-RiskyPorts.ps1
Normal file
134
Modules/AdvancedSecurity/Private/Test-RiskyPorts.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Modules/AdvancedSecurity/Private/Test-RiskyServices.ps1
Normal file
89
Modules/AdvancedSecurity/Private/Test-RiskyServices.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Modules/AdvancedSecurity/Private/Test-SRPCompliance.ps1
Normal file
126
Modules/AdvancedSecurity/Private/Test-SRPCompliance.ps1
Normal 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: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
130
Modules/AdvancedSecurity/Private/Test-WDigest.ps1
Normal file
130
Modules/AdvancedSecurity/Private/Test-WDigest.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Modules/AdvancedSecurity/Private/Test-WPAD.ps1
Normal file
88
Modules/AdvancedSecurity/Private/Test-WPAD.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Modules/AdvancedSecurity/Private/Test-WindowsUpdate.ps1
Normal file
91
Modules/AdvancedSecurity/Private/Test-WindowsUpdate.ps1
Normal 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: $_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
1141
Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1
Normal file
1141
Modules/AdvancedSecurity/Public/Invoke-AdvancedSecurity.ps1
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
}
|
||||
}
|
||||
188
Modules/AdvancedSecurity/Public/Test-AdvancedSecurity.ps1
Normal file
188
Modules/AdvancedSecurity/Public/Test-AdvancedSecurity.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue