Windows Unquoted Search Path Remediation

  • 14 September 2020
  • 2 replies
  • 772 views

Userlevel 5
Badge +1

This worklet will search for Windows Unquoted Search Path under uninstall and services in registry and add quotes. This addresses a vulnerability detected by some tools like Rapid7 or Nessus. All changes made by the script are logged under C:\windows\temp\unquoted-search-path-changes.csv.


#Evaluation Code


$i = 0
$BaseKeys = "HKLM:\System\CurrentControlSet\Services", #Services
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", #32bit Uninstalls
"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" #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
$i++
}
}
}
$ParentKey.Close()
}
$Registry.Close()

IF($i -eq 0){ Exit 0 }else{ Exit 1 }

#Remediation Code


$BaseKeys = "HKLM:\System\CurrentControlSet\Services",                                  #Services
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", #32bit Uninstalls
"HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" #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 @{l='Timestamp';e={"$((Get-Date).ToShortDateString()) $((Get-Date).ToShortTimeString())"}},ParentKey,Value,Correction,Name,Type | export-csv C:\windows\temp\unquoted-search-path-changes.csv -NoTypeInformation -Append

2 replies

Userlevel 4

That is a fantastic Worklet! @jack.smith, thank’s for sharing, this is great.

Badge

Hi Jack,

If the file comes out empty, does it mean there are no unquoted search path? How do we solve this vulnerability?

If the paths are identified?

Eg: 

Service Name Image Path
IntelAudioService %SystemRoot%\system32\cAVS\Intel(R)_Audio_Service\IntelAudioService.exe

Reply