noid-privacy/Modules/AdvancedSecurity/Private/Disable-RiskyPorts.ps1

251 lines
12 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 FIX: Batch query instead of per-rule queries
# Old approach: foreach { Get-NetFirewallPortFilter } = 300+ queries × 200ms = 60s+
# New approach: Get all port filters once via hashtable = 2-5s total
Write-Log -Level INFO -Message "Loading firewall rules for analysis..." -Module "AdvancedSecurity"
$allRules = Get-NetFirewallRule -ErrorAction SilentlyContinue | Where-Object { $_.Direction -eq 'Inbound' -and $_.Enabled -eq $true }
# Get all port filters in one batch query and build hashtable by InstanceID
$allPortFilters = @{}
Get-NetFirewallPortFilter -ErrorAction SilentlyContinue | ForEach-Object {
$allPortFilters[$_.InstanceID] = $_
}
# Build cache with fast hashtable lookup
$rulesWithPorts = @()
foreach ($rule in $allRules) {
$portFilter = $allPortFilters[$rule.InstanceID]
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
}
}