Public/Get-UnquotedServicePath.ps1
|
function Get-UnquotedServicePath { <# .SYNOPSIS Finds Windows services with unquoted executable paths that contain spaces. .DESCRIPTION Enumerates all Win32 services and identifies those whose ImagePath contains spaces but is not enclosed in quotation marks. An unquoted path with spaces is a privilege escalation vulnerability: Windows will attempt to execute each space-delimited path segment as a candidate executable before reaching the real one. Returns one object per vulnerable service. Returns nothing if no vulnerable services are found. .INPUTS None. Parameters must be supplied directly. .OUTPUTS System.Management.Automation.PSCustomObject .PARAMETER ComputerName The target computer. Defaults to the local machine. .EXAMPLE Get-UnquotedServicePath Returns all services on the local machine with unquoted paths containing spaces. .EXAMPLE Get-UnquotedServicePath -ComputerName 'Server01' Returns vulnerable services on Server01. .EXAMPLE Get-UnquotedServicePath | Enable-ServicePathQuoting Pipes each vulnerable service directly into Enable-ServicePathQuoting to remediate all findings. .NOTES Read-only. Does not modify any system state. Remote operations require WinRM to be configured on the target machine. #> [CmdletBinding()] [OutputType([System.Management.Automation.PSCustomObject])] param ( [Parameter(Mandatory = $false)] [string]$ComputerName = $env:COMPUTERNAME ) $isLocal = ($ComputerName -ieq $env:COMPUTERNAME) -or ($ComputerName -ieq 'localhost') -or ($ComputerName -eq '127.0.0.1') $scan = { Get-CimInstance -ClassName Win32_Service | ForEach-Object { $pathName = $_.PathName if ([string]::IsNullOrWhiteSpace($pathName)) { return } if ($pathName.TrimStart().StartsWith('"')) { return } if ($pathName -imatch '^(.*?\.exe)') { $exePath = $Matches[1].Trim() if ($exePath -notmatch ' ') { return } } else { return } [PSCustomObject]@{ ServiceName = $_.Name DisplayName = $_.DisplayName ImagePath = $pathName } } } if ($isLocal) { & $scan | ForEach-Object { [PSCustomObject]@{ ComputerName = $ComputerName ServiceName = $_.ServiceName DisplayName = $_.DisplayName ImagePath = $_.ImagePath } } } else { Invoke-Command -ComputerName $ComputerName -ScriptBlock $scan | ForEach-Object { [PSCustomObject]@{ ComputerName = $ComputerName ServiceName = $_.ServiceName DisplayName = $_.DisplayName ImagePath = $_.ImagePath } } } } |