Created
December 10, 2025 21:51
-
-
Save ergosteur/cf1365d3a3c321d603d30de909c7ad71 to your computer and use it in GitHub Desktop.
PowerShell script that analyzes Windows Wi-Fi profiles to extract SSID, Security, and Certificate Trust details.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Analyzes Windows Wi-Fi profiles to extract SSID, Security, and Certificate Trust details. | |
| .DESCRIPTION | |
| This tool provides a detailed analysis of Wi-Fi connection profiles, specifically focusing on | |
| Enterprise (802.1X) security settings. It extracts the Trusted Root CA Thumbprint, Server | |
| Name constraints, and validates if the required certificates are present in the local machine store. | |
| It operates in three modes: | |
| 1. Interactive Mode: select from a menu of saved profiles. | |
| 2. Direct Mode: Analyze a specific profile by name. | |
| 3. File Mode: Analyze an existing XML profile file. | |
| .EXAMPLE | |
| .\Get-Wifi-Profile-Analyzer.ps1 -Interactive | |
| Launches a menu to select a Wi-Fi profile. | |
| .EXAMPLE | |
| .\Get-Wifi-Profile-Analyzer.ps1 -ProfileName "ContosoWLAN" | |
| Analyzes the "ContosoWLAN" profile directly. | |
| #> | |
| [CmdletBinding(DefaultParameterSetName="ShowHelp")] | |
| param( | |
| # Mode 1: Interactive Menu | |
| [Parameter(ParameterSetName="Interactive", Mandatory=$true)] | |
| [switch]$Interactive, | |
| # Mode 2: Direct System Profile | |
| [Parameter(ParameterSetName="ByName", Mandatory=$true, Position=0)] | |
| [string]$ProfileName, | |
| # Mode 3: Analyze an existing XML file | |
| [Parameter(ParameterSetName="ByFile", Mandatory=$true, Position=0)] | |
| [Alias("ProfilePath")] | |
| [string]$Path | |
| ) | |
| # --- HELPER 1: CORE XML ANALYSIS --- | |
| function Analyze-WifiProfileXml { | |
| param ([xml]$XmlProfile, [string]$SourceName) | |
| Write-Host "==================================================" -ForegroundColor Cyan | |
| Write-Host " WI-FI PROFILE ANALYZER: $($XmlProfile.WLANProfile.name)" -ForegroundColor Cyan | |
| Write-Host "==================================================" -ForegroundColor Cyan | |
| Write-Host "Source: $SourceName`n" | |
| # --- PART 1: GENERAL SETTINGS --- | |
| Write-Host "--- General Wi-Fi Settings ---" -ForegroundColor Yellow | |
| # Extract standard fields (safe navigation) | |
| $SSIDName = $XmlProfile.WLANProfile.SSIDConfig.SSID.name | |
| $ConnType = $XmlProfile.WLANProfile.connectionType | |
| $ConnMode = $XmlProfile.WLANProfile.connectionMode | |
| $AutoSwitch = $XmlProfile.WLANProfile.autoSwitch | |
| Write-Host "SSID Name: $SSIDName" | |
| Write-Host "Network Type: $ConnType" | |
| Write-Host "Connection Mode: $ConnMode" | |
| Write-Host "Auto-Connect: $AutoSwitch" | |
| # --- PART 2: SECURITY BASICS --- | |
| Write-Host "`n--- Security Basics ---" -ForegroundColor Yellow | |
| $AuthAlgo = $XmlProfile.WLANProfile.MSM.security.authEncryption.authentication | |
| $Cipher = $XmlProfile.WLANProfile.MSM.security.authEncryption.encryption | |
| $UseOneX = $XmlProfile.WLANProfile.MSM.security.authEncryption.useOneX | |
| $AuthMode = $XmlProfile.WLANProfile.MSM.security.OneX.authMode | |
| Write-Host "Authentication: $AuthAlgo" | |
| Write-Host "Encryption: $Cipher" | |
| Write-Host "802.1X Enabled: $UseOneX" | |
| if ($AuthMode) { Write-Host "Auth Mode: $AuthMode" } | |
| # --- PART 3: ADVANCED TRUST SETTINGS --- | |
| if ($UseOneX -eq "true") { | |
| Write-Host "`n--- Certificate Trust & EAP Settings ---" -ForegroundColor Yellow | |
| # 1. Trusted Root CA Hash | |
| $RootCANodes = $XmlProfile.GetElementsByTagName("TrustedRootCA") | |
| if ($RootCANodes.Count -gt 0) { | |
| $RawHash = $RootCANodes[0].InnerText | |
| $CleanHash = $RawHash -replace '\s','' | |
| Write-Host "Trusted Root CA (Thumbprint):" | |
| Write-Host " $CleanHash" -ForegroundColor Green | |
| # --- LOOKUP CERTIFICATE --- | |
| Write-Host " ...Checking local store..." -NoNewline | |
| $CertPaths = @("Cert:\LocalMachine\Root", "Cert:\LocalMachine\CA", "Cert:\LocalMachine\My", "Cert:\CurrentUser\Root") | |
| $CertFound = $null | |
| foreach ($CertPath in $CertPaths) { | |
| if (Test-Path $CertPath) { | |
| $CertFound = Get-ChildItem -Path $CertPath -ErrorAction SilentlyContinue | Where-Object { $_.Thumbprint -eq $CleanHash } | Select-Object -First 1 | |
| if ($CertFound) { break } | |
| } | |
| } | |
| if ($CertFound) { | |
| Write-Host " FOUND in $($CertFound.PSParentPath.Split('\')[-1])" -ForegroundColor Green | |
| Write-Host " Subject: $($CertFound.Subject)" | |
| Write-Host " Issuer: $($CertFound.Issuer)" | |
| Write-Host " Expires: $($CertFound.NotAfter)" | |
| if ($CertFound.NotAfter -lt (Get-Date)) { | |
| Write-Host " STATUS: ❌ EXPIRED" -ForegroundColor Red | |
| } else { | |
| Write-Host " STATUS: ✅ Valid" -ForegroundColor Green | |
| } | |
| } else { | |
| Write-Host " NOT FOUND on this machine." -ForegroundColor Red | |
| Write-Host " (Profile trusts this hash, but cert is missing)" | |
| } | |
| } else { | |
| Write-Host "Trusted Root CA: Not explicitly defined (May use system defaults)" -ForegroundColor DarkGray | |
| } | |
| # 2. Server Name Constraints | |
| $ServerNameNodes = $XmlProfile.GetElementsByTagName("ServerNames") | |
| if ($ServerNameNodes.Count -gt 0) { | |
| Write-Host "`nServer Name Constraint:" | |
| Write-Host " $($ServerNameNodes[0].InnerText)" -ForegroundColor Green | |
| } else { | |
| Write-Host "`nServer Name Constraint: None" -ForegroundColor DarkGray | |
| } | |
| # 3. Validation Flags | |
| $ValidationNodes = $XmlProfile.GetElementsByTagName("PerformServerValidation") | |
| if ($ValidationNodes.Count -gt 0) { | |
| Write-Host "`nPerform Server Validation:" | |
| Write-Host " $($ValidationNodes[0].InnerText)" | |
| } | |
| } else { | |
| Write-Host "`n(No EAP/Certificate settings to analyze for non-Enterprise networks)" | |
| } | |
| Write-Host "`n==================================================" -ForegroundColor Cyan | |
| } | |
| # --- HELPER 2: EXPORT AND PROCESS SYSTEM PROFILE --- | |
| function Process-SystemProfile { | |
| param ([string]$Name) | |
| # Create temp folder | |
| $TempDir = Join-Path $env:TEMP "WifiProfileExport_$(Get-Random)" | |
| New-Item -ItemType Directory -Path $TempDir -Force | Out-Null | |
| try { | |
| # Export profile | |
| $ExportResult = netsh wlan export profile name="$Name" folder="$TempDir" | Out-String | |
| $ExportedFile = Get-ChildItem -Path $TempDir -Filter "*.xml" | Select-Object -First 1 | |
| if ($ExportedFile) { | |
| [xml]$XmlData = Get-Content -Path $ExportedFile.FullName -Raw | |
| Analyze-WifiProfileXml -XmlProfile $XmlData -SourceName "System Profile (Live Export)" | |
| } else { | |
| Write-Error "Failed to export profile '$Name'. Verify the name and ensure you have Administrator rights (required for GPO profiles)." | |
| } | |
| } | |
| finally { | |
| # Cleanup | |
| Remove-Item -Path $TempDir -Recurse -Force -ErrorAction SilentlyContinue | |
| } | |
| } | |
| # --- MAIN LOGIC --- | |
| if ($PSCmdlet.ParameterSetName -eq "ByFile") { | |
| # MODE A: Parsing an existing file | |
| if (-not (Test-Path $Path)) { | |
| Write-Error "File not found: $Path" | |
| return | |
| } | |
| [xml]$XmlData = Get-Content -Path $Path -Raw | |
| Analyze-WifiProfileXml -XmlProfile $XmlData -SourceName "XML File ($Path)" | |
| } elseif ($PSCmdlet.ParameterSetName -eq "ByName") { | |
| # MODE B: Exporting Specific System Profile | |
| Process-SystemProfile -Name $ProfileName | |
| } elseif ($PSCmdlet.ParameterSetName -eq "Interactive") { | |
| # MODE C: Interactive Menu | |
| Write-Host "`nScanning system for Wi-Fi profiles..." -ForegroundColor Cyan | |
| $NetshOutput = netsh wlan show profiles | |
| $ProfileList = @() | |
| # 1. Capture Standard Profiles (Format: " All User Profile : Name") | |
| $StandardProfiles = $NetshOutput | Select-String -Pattern "All User Profile\s*:\s*(.+)$" | |
| if ($StandardProfiles) { | |
| foreach ($item in $StandardProfiles) { | |
| $ProfileList += $item.Matches.Groups[1].Value.Trim() | |
| } | |
| } | |
| # 2. Capture GPO/Other Profiles (Format: " Name" - just indented, no key:value) | |
| $GpoCandidates = $NetshOutput | Where-Object { | |
| $_ -match "^\s{2,}" -and # Must be indented | |
| $_ -notmatch ":" -and # No colon (avoids 'Key : Value' lines) | |
| $_ -notmatch "-{3,}" -and # No separator lines (-----) | |
| $_ -notmatch "<None>" -and # No empty indicators | |
| $_ -notmatch "Profiles on" -and | |
| $_ -notmatch "Group policy" -and | |
| $_ -notmatch "User profiles" | |
| } | |
| if ($GpoCandidates) { | |
| foreach ($line in $GpoCandidates) { | |
| $cleanName = $line.Trim() | |
| if (-not [string]::IsNullOrWhiteSpace($cleanName)) { | |
| $ProfileList += $cleanName | |
| } | |
| } | |
| } | |
| # Remove duplicates and sort | |
| # WRAPPER: @(...) ensures this remains an array even if 1 result exists | |
| $ProfileList = @($ProfileList | Select-Object -Unique | Sort-Object) | |
| if ($ProfileList.Count -eq 0) { | |
| Write-Warning "No Wi-Fi profiles found on this system." | |
| return | |
| } | |
| # Display Menu | |
| Write-Host "`nSelect a profile to analyze:" -ForegroundColor Yellow | |
| $i = 1 | |
| foreach ($P in $ProfileList) { | |
| Write-Host " [$i] $P" | |
| $i++ | |
| } | |
| # Get User Input | |
| $Selection = Read-Host "`nEnter number (1-$($ProfileList.Count))" | |
| # Validate and Run | |
| if ($Selection -match "^\d+$" -and $Selection -ge 1 -and $Selection -le $ProfileList.Count) { | |
| $SelectedProfile = $ProfileList[$Selection - 1] | |
| Write-Host "`nAnalyzing '$SelectedProfile'..." -ForegroundColor Cyan | |
| Process-SystemProfile -Name $SelectedProfile | |
| } else { | |
| Write-Error "Invalid selection." | |
| } | |
| } else { | |
| # MODE D: Show Help (Default) | |
| Write-Host "==================================================" -ForegroundColor Cyan | |
| Write-Host " WI-FI PROFILE ANALYZER HELP" -ForegroundColor Cyan | |
| Write-Host "==================================================" -ForegroundColor Cyan | |
| Write-Host "`nThis tool analyzes Wi-Fi profiles to show Security and Certificate Trust details." | |
| Write-Host "`n[OPTION 1] Interactive Menu (Recommended)" -ForegroundColor Yellow | |
| Write-Host " Usage: .\Get-Wifi-Profile-Analyzer.ps1 -Interactive" | |
| Write-Host "`n[OPTION 2] Analyze by Name" -ForegroundColor Yellow | |
| Write-Host " Usage: .\Get-Wifi-Profile-Analyzer.ps1 -ProfileName `"ContosoWLAN`"" | |
| Write-Host "`n[OPTION 3] Analyze Exported File" -ForegroundColor Yellow | |
| Write-Host " Usage: .\Get-Wifi-Profile-Analyzer.ps1 -Path `"C:\Temp\Profile.xml`"" | |
| Write-Host "`n--------------------------------------------------" -ForegroundColor DarkGray | |
| Write-Host "HANDY MANUAL COMMANDS (NETSH)" -ForegroundColor DarkGray | |
| Write-Host "--------------------------------------------------" -ForegroundColor DarkGray | |
| Write-Host " > To list all profiles on your PC:" | |
| Write-Host " netsh wlan show profiles" -ForegroundColor White | |
| Write-Host "`n > To manually export a profile to XML:" | |
| Write-Host " netsh wlan export profile name=`"ContosoWLAN`" folder=`".`"" -ForegroundColor White | |
| Write-Host "`n==================================================" -ForegroundColor Cyan | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment