#Requires -Version 5.1 <# .SYNOPSIS Win_Network_ProxyConfigAudit -- Detect rogue or unexpected proxy settings .DESCRIPTION Audits proxy configuration across all common Windows locations: - HKCU (per-user Internet Settings) for all user profiles - HKLM (system-wide WinHTTP proxy) - Environment variables (http_proxy / https_proxy) - netsh winhttp proxy Flags any proxy that is enabled or set, logs findings to a local log file, and exits with a non-zero code if rogue proxies are detected so an RMM/monitoring tool (or a scheduled task) can alert accordingly. .PARAMETER LogDir Directory to write the log file to. Defaults to a "ProxyConfigAudit" folder under the current user's TEMP path so this runs cleanly on any machine with no setup required. Override with your own path if you want logs centralized somewhere specific (e.g. an RMM's standard log folder). .NOTES Author : Fred Claus Version : 1.0 (Community Edition) Created : 2026-06-13 Requires : PowerShell 5.1 | Recommended: run as SYSTEM/Administrator Designed for unattended or RMM-driven execution | Suggested timeout: 60s Exit 0 = No proxy settings detected (clean) Exit 1 = One or more proxy settings found (review required) #> param( [string]$LogDir = (Join-Path $env:TEMP "ProxyConfigAudit") ) # --------------------------------------------------------------------------- # CONFIGURATION # --------------------------------------------------------------------------- $ScriptName = "Win_Network_ProxyConfigAudit" $LogFile = Join-Path $LogDir "$ScriptName.log" # Allowed/expected proxy values -- add your known-good entries here. # Leave empty to flag ALL proxy settings. # Example: $AllowedProxies = @("proxy.contoso.com:8080") $AllowedProxies = @() # --------------------------------------------------------------------------- # LOGGING # --------------------------------------------------------------------------- if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null } function Write-Log { param([string]$Message, [string]$Level = "INFO") $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $Entry = "[$Timestamp] [$Level] $Message" Add-Content -Path $LogFile -Value $Entry Write-Output $Entry } # --------------------------------------------------------------------------- # HELPERS # --------------------------------------------------------------------------- function Test-IsRogue { param([string]$Value) if ([string]::IsNullOrWhiteSpace($Value)) { return $false } if ($AllowedProxies.Count -eq 0) { return $true } foreach ($allowed in $AllowedProxies) { if ($Value -ieq $allowed) { return $false } } return $true } # --------------------------------------------------------------------------- # START # --------------------------------------------------------------------------- Write-Log "===== $ScriptName started =====" Write-Log "Log file: $LogFile" $findings = [System.Collections.Generic.List[string]]::new() # --------------------------------------------------------------------------- # 1. HKCU -- Per-user Internet Settings (all loaded hives) # --------------------------------------------------------------------------- Write-Log "Checking HKCU proxy settings for all user profiles..." $profileList = @() # Currently loaded hives (interactive sessions) $loadedHives = Get-ChildItem "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -ErrorAction SilentlyContinue if ($loadedHives) { $profileList += [PSCustomObject]@{ Source = "HKCU (current)"; RegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" } } # Iterate all user SIDs in HKU for dormant profiles $huKeys = Get-ChildItem "HKU:\" -ErrorAction SilentlyContinue if (-not $huKeys) { # Mount HKU if not already available if (-not (Get-PSDrive -Name HKU -ErrorAction SilentlyContinue)) { New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null } $huKeys = Get-ChildItem "HKU:\" -ErrorAction SilentlyContinue } if ($huKeys) { foreach ($sidKey in $huKeys) { $sid = $sidKey.PSChildName # Skip system SIDs and _Classes hive if ($sid -match "^S-1-5-(18|19|20)$" -or $sid -match "_Classes$") { continue } $inetPath = "HKU:\$sid\Software\Microsoft\Windows\CurrentVersion\Internet Settings" if (Test-Path $inetPath) { # Try to resolve friendly username try { $objSID = New-Object System.Security.Principal.SecurityIdentifier($sid) $objUser = $objSID.Translate([System.Security.Principal.NTAccount]) $label = $objUser.Value } catch { $label = $sid } $profileList += [PSCustomObject]@{ Source = "HKU ($label)"; RegPath = $inetPath } } } } foreach ($profile in $profileList) { $regPath = $profile.RegPath $source = $profile.Source try { $props = Get-ItemProperty -Path $regPath -ErrorAction Stop $proxyEnabled = $props.ProxyEnable $proxyServer = $props.ProxyServer $proxyOverride= $props.ProxyOverride $autoConfigURL= $props.AutoConfigURL if ($proxyEnabled -eq 1) { $msg = "PROXY ENABLED [$source] Server: '$proxyServer' | Override: '$proxyOverride'" Write-Log $msg "WARN" if (Test-IsRogue $proxyServer) { $findings.Add($msg) } } if (-not [string]::IsNullOrWhiteSpace($autoConfigURL)) { $msg = "AUTO-CONFIG URL [$source] PAC: '$autoConfigURL'" Write-Log $msg "WARN" if (Test-IsRogue $autoConfigURL) { $findings.Add($msg) } } if ($proxyEnabled -ne 1 -and [string]::IsNullOrWhiteSpace($autoConfigURL)) { Write-Log "Clean -- no proxy enabled [$source]" } } catch { Write-Log "Could not read registry at $regPath : $_" "WARN" } } # --------------------------------------------------------------------------- # 2. HKLM -- System-wide WinHTTP proxy (used by services and SYSTEM processes) # --------------------------------------------------------------------------- Write-Log "Checking HKLM WinHTTP proxy..." $winHttpPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" try { $winHttpProps = Get-ItemProperty -Path $winHttpPath -Name "WinHttpSettings" -ErrorAction Stop # WinHttpSettings is a binary blob -- presence alone is noteworthy; decode length check $blob = $winHttpProps.WinHttpSettings # Byte offset 8 indicates proxy length (non-zero = proxy set) if ($blob -and $blob.Length -gt 8 -and $blob[8] -gt 0) { # Extract proxy string from blob $proxyLen = $blob[8] $proxyStr = [System.Text.Encoding]::ASCII.GetString($blob, 12, $proxyLen) $msg = "WINHTTP SYSTEM PROXY SET: '$proxyStr'" Write-Log $msg "WARN" if (Test-IsRogue $proxyStr) { $findings.Add($msg) } } else { Write-Log "WinHTTP system proxy: not set (blob present but no proxy configured)" } } catch { Write-Log "WinHTTP registry key not found or not readable -- likely clean" } # --------------------------------------------------------------------------- # 3. netsh winhttp show proxy # --------------------------------------------------------------------------- Write-Log "Checking netsh winhttp proxy..." try { $netshOutput = & netsh winhttp show proxy 2>&1 $netshText = $netshOutput -join " " if ($netshText -match "Proxy Server\(s\)\s*:\s*(.+?)(\s+Bypass|$)") { $netshProxy = $Matches[1].Trim() if ($netshProxy -ne "Direct access (no proxy server)." -and $netshProxy -notmatch "^\s*$") { $msg = "NETSH WINHTTP PROXY: '$netshProxy'" Write-Log $msg "WARN" if (Test-IsRogue $netshProxy) { $findings.Add($msg) } } else { Write-Log "netsh winhttp: direct access -- clean" } } else { Write-Log "netsh winhttp: no proxy configured -- clean" } } catch { Write-Log "Could not run netsh: $_" "WARN" } # --------------------------------------------------------------------------- # 4. Environment Variables -- http_proxy / https_proxy / HTTP_PROXY # --------------------------------------------------------------------------- Write-Log "Checking environment variable proxies..." $envVars = @("http_proxy","https_proxy","HTTP_PROXY","HTTPS_PROXY","all_proxy","ALL_PROXY") foreach ($var in $envVars) { # Check Machine, User, and Process scopes foreach ($scope in @("Machine","User","Process")) { $val = [System.Environment]::GetEnvironmentVariable($var, $scope) if (-not [string]::IsNullOrWhiteSpace($val)) { $msg = "ENV PROXY [$scope] $var = '$val'" Write-Log $msg "WARN" if (Test-IsRogue $val) { $findings.Add($msg) } } } } if ($findings.Count -eq 0) { Write-Log "No environment variable proxies found -- clean" } # --------------------------------------------------------------------------- # 5. Scheduled Tasks / Startup -- proxy-related entries (quick heuristic) # --------------------------------------------------------------------------- Write-Log "Scanning scheduled tasks for proxy-manipulation heuristics..." try { $tasks = Get-ScheduledTask -ErrorAction Stop | Where-Object { $_.Actions | Where-Object { $_.Execute -match "netsh|bitsadmin|proxycfg" -or ($_.Arguments -match "proxy" -and $_.Execute -notmatch "MpCmdRun") } } if ($tasks) { foreach ($t in $tasks) { $msg = "SUSPICIOUS TASK (proxy-related): '$($t.TaskName)' in '$($t.TaskPath)'" Write-Log $msg "WARN" $findings.Add($msg) } } else { Write-Log "No suspicious proxy-related scheduled tasks found -- clean" } } catch { Write-Log "Could not enumerate scheduled tasks: $_" "WARN" } # --------------------------------------------------------------------------- # SUMMARY # --------------------------------------------------------------------------- Write-Log "===== SUMMARY =====" if ($findings.Count -eq 0) { Write-Log "RESULT: CLEAN -- No rogue proxy settings detected on this endpoint." Write-Log "===== $ScriptName completed =====" exit 0 } else { Write-Log "RESULT: FINDINGS ($($findings.Count) item(s) flagged):" foreach ($f in $findings) { Write-Log " >> $f" "ALERT" } Write-Log "===== $ScriptName completed with findings =====" exit 1 }