serviceexecution1

If you’re using a vulnerability scanner on your PC or network, you’ve probably come across the Microsoft Windows Unquoted Service Path Enumeration vulnerability. The truth is, this vulnerability has been around for many years and Microsoft hasn’t done much to address it. Instead, they’ve left it up to the individual developers to fix their programs and include quotes around their service paths when creating the registry keys.

The Microsoft Windows Unquoted Service Path Enumeration Vulnerability

All Windows services have a Path to its executable. If that path is unquoted and contains whitespace or other separators, then the service will attempt to access a resource in the parent path first. This affects all versions of Windows and any Operating System that supports spaces in file names.

Essentially, if you have an unquoted service path with a space in it, that service is vulnerable to attack. If an attacker has access to a folder in the directory path, it is possible for privilege escalation to take place by inserting a malicious program in the parent path before the whitespace.

Disclaimer: Remember to check your results! I cannot be liable for any damage caused by running these scripts. It’s your environment and you ran them.

I’ve seen a few scripts floating around to resolve this vulnerability. Some can be run on individual systems and others are Powershell scripts that can be run across a network. I would recommend starting small and thoroughly test your results.

Remediation for

The first step you can do on a PC is run this command from an elevated command prompt. This will scan your registry and display all of the service paths that need remediation.

wmic service get name,displayname,pathname,startmode |findstr /i "auto" |findstr /i /v "c:windows\" |findstr /i /v """

Alternate Powershell version:

cmd /c 'wmic service get name,displayname,pathname,startmode |findstr /i "auto" |findstr /i /v "c:windows\" |findstr /i /v """'

Here’s a Powershell script you can use thanks to Microsoft’s Technet:

Before running the Powershell script below, you might need to set your ExecutionPolicy to Unrestricted if you haven’t already. To do that, run this command in Powershell and select Y:

Set-ExecutionPolicy Unrestricted

Save the file below as a .ps1 file and run it from Powershell like .filename.ps1. It will save an output to:
C:Tempservicesfix.log.

cls
Function Fix-ServicePath (

[System.IO.DirectoryInfo]$LogPath = "C:temp") {
if (-not (Test-Path $LogPath)){New-Item $LogPath -ItemType directory}
"**************************************************" | Out-File "$LogPathservicesfix.log" -Append
"Computername: $($Env:COMPUTERNAME)" | Out-File "$LogPathservicesfix.log" -Append
"Date: $(Get-date -Format "dd.MM.yyyy HH:mm")" | Out-File "$LogPathservicesfix.log" -Append

# Get all services
Get-ChildItem "HKLM:SYSTEMCurrentControlSetServices" | foreach {
$OriginalPath = (Get-ItemProperty "$($($_).name.replace('HKEY_LOCAL_MACHINE', 'HKLM:'))")
$ImagePath = $OriginalPath.ImagePath

# Get all services with vulnerability
If (($ImagePath -like "* *") -and ($ImagePath -notlike '"*"*') -and ($ImagePath -like '*.exe*')){
$NewPath = ($ImagePath -split ".exe ")[0] $key = ($ImagePath -split ".exe ")[1] $triger = ($ImagePath -split ".exe ")[2]

# Get all services with vulnerability with key in ImagePath
If (-not ($triger | Measure-Object).count -ge 1){
If (($NewPath -like "* *") -and ($NewPath -notlike "*.exe")){

" ***** Old Value $ImagePath" | Out-File "$LogPathservicesfix.log" -Append
"$($OriginalPath.PSChildName) `"$NewPath.exe`" $key" | Out-File "$LogPathservicesfix.log" -Append
Set-ItemProperty -Path $OriginalPath.PSPath -Name "ImagePath" -Value "`"$NewPath.exe`" $key"
}
}

# Get all services with vulnerability with out key in ImagePath
If (-not ($triger | Measure-Object).count -ge 1){
If (($NewPath -like "* *") -and ($NewPath -like "*.exe")){

" ***** Old Value $ImagePath" | Out-File "$LogPathservicesfix.log" -Append
"$($OriginalPath.PSChildName) `"$NewPath`"" | Out-File "$LogPathservicesfix.log" -Append
Set-ItemProperty -Path $OriginalPath.PSPath -Name "ImagePath" -Value "`"$NewPath`""
}
}
}
If (($triger | Measure-Object).count -ge 1) { "----- Error Cant parse $($OriginalPath.ImagePath) in registry $($OriginalPath.PSPath -replace 'Microsoft.PowerShell.Core\Registry::') " | Out-File $LogPathservicesfix.log -Append}
}
}
Fix-ServicePath -LogPath C:Temp

In addition to the above, you might also find paths in the UninstallStrings of installed applications. The script below will check for services and the uninstall paths.

$BaseKeys = "HKLM:SystemCurrentControlSetServices", #Services
"HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall", #32bit Uninstalls
"HKLM:SoftwareWow6432NodeMicrosoftWindowsCurrentVersionUninstall" #64bit Uninstalls
#Blacklist for keys to ignore
$BlackList = $Null
#Create an ArrayList to store results in
$Values = New-Object System.Collections.ArrayList
#Discovers all registry keys under the base keys
$DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Name | %{($_.ToString().Split('') | Select-Object -Skip 1) -join ''}
#Open the local registry
$Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
ForEach ($RegKey in $DiscKeys)
{
#Open each key with write permissions
Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
Catch { Write-Debug "Unable to open $RegKey" }
#Test if registry key has values
If ($ParentKey.ValueCount -gt 0)
{
$MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq "ImagePath" -or $_ -eq "UninstallString" }
ForEach ($Match in $MatchedValues)
{
#RegEx that matches values containing .exe with a space in the exe path and no double quote encapsulation
$ValueRegEx = '(^(?!u0022).*s.*.[Ee][Xx][Ee](?< !u0022))(.*$)' $Value = $ParentKey.GetValue($Match) #Test if value matches RegEx If ($Value -match $ValueRegEx) { $RegType = $ParentKey.GetValueKind($Match) #Uses the matches from the RegEx to build a new entry encapsulating the exe path with double quotes $Correction = "$([char]34)$($Matches[1])$([char]34)$($Matches[2])" #Attempt to correct the entry Try { $ParentKey.SetValue("$Match", "$Correction", [Microsoft.Win32.RegistryValueKind]::$RegType) } Catch { Write-Debug "Unable to write to $ParentKey" } #Add a hashtable containing details of corrected key to ArrayList $Values.Add((New-Object PSObject -Property @{ "Name" = $Match "Type" = $RegType "Value" = $Value "Correction" = $Correction "ParentKey" = "HKEY_LOCAL_MACHINE$RegKey" })) | Out-Null } } } $ParentKey.Close() } $Registry.Close() $Values | Select-Object ParentKey,Value,Correction,Name,Type

Conclusion

Depending on how you manage your servers, you should have a few options to deploy this Powershell script to multiple systems. Not every server will have this vulnerability unless the same affected software is installed on every server.