Scan and Fix Unquoted Service Path Vulnerability with PowerShell

As many security experts and system administrators are aware, Microsoft has really dropped the ball at addressing a decade old flaw in the way the Windows API handles service paths… What amazes me is how prevalent this issue still is and how easy it is for any common script kiddie to elevate privileges and gain a foothold in your system. I will discuss the vulnerability and how I scanned for and remediated vulnerable systems using Windows PowerShell.

 

The Microsoft Windows Unquoted Service Path 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.

 

Explanation:

Let me explain with pictures and colors 🙂 – Sorry Western Digital…

WDServicePath

 

Path to executable: (BAD!)

C:Program Files (x86)Western DigitalWD SmartWareWDBackupEngine.exe

 

For this particular service, the path contains spaces and was not properly enclosed with quotes, so every time it is started. It searches in the following order.

  1. Service attempts to start…
  2. C:Program.exe Files (x86)Western DigitalWD SmartWareWDBackupEngine.exe (Does this executable exist? If not move on…)
  3. C:Program Files.exe (x86)Western DigitalWD SmartWareWDBackupEngine.exe (Does this executable exist? If not move on…)
  4. C:Program Files (x86)Western.exe DigitalWD SmartWareWDBackupEngine.exe (Does this executable exist? If not move on…)
  5. C:Program Files (x86)Western DigitalWD.exe SmartWareWDBackupEngine.exe (Does this executable exist? If not move on…)
  6. C:Program Files (x86)Western DigitalWD SmartWareWDBackupEngine.exe
  7. Service started…

You see that the attack scope for this exploit can be achieved in 4 different locations. All an attacker has to do is name their executable as shown above and place it in any of those directories and the next time the service starts, it will call on the malicious executable before reaching its intended service executable.

 

Now if least privilege is handled properly in your organization, users may not be able to write to the root of C:, however they might still be able to write to any of the sub-directories in the service path.

 

Now for some PowerShell…

I spent a few hours the other day writing up this script which I see as pretty decent reusable tool to scan for this vulnerability. Essentially it will gather a list of all services on a system and search the servicepath value to see if it contains any spaces that are not enclosed in quotations. I have built in logic to include executables that use switches and parameters since most always there is spaces in the parameters after the executable.

For example: C:WindowsSystem32svchost.exe -k netsvcs does contains spaces yet it is not a vulnerable since the executable path does not contain spaces, only the parameters have the spaces.

 

Here is what the scan output looks like when run in the default “Audit” mode with Verbose output.

Get-ServicePathVulnerabilities -Verbose

ServicePath-Results

 

And this time I specify the “Fix” parameter with Verbose output.

Get-ServicePathVulnerabilities -Fix – Verbose

ServicePath-Results

 

You can see how the service is now enclosed with proper quotations and  a quick check can verify that it is no longer vulnerable to attack. Who needs vendors to give us patches for our systems!!

ServicePath-Results

 

 

Source

Since I do not like the way PowerShell code looks in blog posts I collapsed the sourcecode, you can either download the file directly with the attachment below or I just started using GitHub. Just don’t hold me liable if something goes wrong!

[code language=”PowerShell”]

Function Get-ServicePathVulnerabilities {

[Cmdletbinding()]

Param (

[switch]$Fix

) # End Param

Begin {

$VulnerableServices=@()

if ($Fix){Write-Verbose “Scan Mode: Fix”} else {Write-Verbose “Scan Mode: Audit”}

} # End Begin

Process {

# Gather Services information from WMI

$Services = Get-WmiObject -Class win32_service -Property name,pathname

# Filter out services that have been enclosed with quotations

$UnquotedPath = $Services | Where-Object {$_.PathName -notmatch ‘”‘} | Select Name,PathName

# Loop through services without quotations

foreach ($Path in $UnquotedPath) {

$Drive = $Path.PathName | Split-Path -Qualifier

$Executable = $Path.PathName | Split-Path -Leaf

# Conditional Logic to determine vulnerability

# Note: Some service paths may be unquoted and include spaces, but not vulnerable. They could just be a path to executable (no spaces) with a command line switch parameter that may contain a space.

# To avoid false positives, the logic below will exclude spaces used in any parameters

if( ($Path.PathName -match ‘ ‘) -and ($Executable -notmatch ‘ ‘) -and ($Path.PathName -notmatch ‘./’) ) {

# Vulnerability Found

Write-Warning (“Unquoted Service Path Discovered for ” + $Path.Name + ” PATH: ” + $Path.PathName)

$VulnerableServices += New-Object PSObject -Property @{

ServiceName = $Path.Name

ServicePath = $Path.PathName

HostName = $env:COMPUTERNAME

} # End Object

} # End conditional operators

} # End Foreach Path in UnquotedPath

# Attempt to encapsulate path in quotes if specified

if ($Fix) {

$VulnerableServices | ForEach-Object {

Write-Verbose (“Attempting to fix ” + $_.Servicename)

$OriginalPath = $_.ServicePath

$QuotedServicePath = (‘”‘ + $_.ServicePath + ‘”‘)

$RegistryLocation = (‘HKLM:SYSTEMCurrentControlSetServices’ + $_.ServiceName)

Try {

Set-ItemProperty -Path $RegistryLocation -Name ImagePath -Value $QuotedServicePath -Verbose

$_.ServicePath = $QuotedServicePath

} Catch {

Write-Error (“Unable to fix ” + $_.Servicename)

} # End Try/Catch

} # End Foreach object in VulnerableServices

} # End if Fix was Specified

} # End Process

End {

if ($VulnerableServices) {Return $VulnerableServices} else {Write-Verbose “No Unquoted Service path Vulnerabilites have been found”}

} # End End

} # Get-ServicePathVulnerabilites

[/code]

 

Download: Get-ServicePathVulnerabilities

GitHub: https://github.com/SecureKomodo

 

 

As always I am open to comments!

Tags// ,