At work as part of some security certification we are running Nessus scans on all our systems and it came up with the following vulnerability – link. Read that link, it’s good info.
Basically if one of your Windows Service entries point to (say) “C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\SMSvcHost.exe
” without the double quotes then one could potentially create a malicious file called Windows.exe
at “C:\Windows\Microsoft.NET\Framework64\v3.0\Windows
” and Windows will execute that file instead of parsing the full path and treating it as part of a folder name. That’s because Windows uses space as a delimiter between a command and its switches & arguments and so it could treat the entry as “C:\Windows\Microsoft.NET\Framework64\v3.0\Windows.exe
” with arguments “Communication Foundation\SMSvcHost.exe
“.
The solution for this is to find all such entries that contain a space, and if the path is not in double quotes then make it so. You have to do this in the registry, so you could either do it manually or make a script and do it en masse. I went the latter route so here’s something I created.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
param ( [switch]$FixIt ) # Quick and dirty script by Rakhesh to fix unquoted path vulnerabilities in service paths # This code is licensed under the MIT license - https://opensource.org/licenses/MIT # https://isc.sans.edu/diary/Help+eliminate+unquoted+path+vulnerabilities/14464 for more info on the vulnerability itself - good read! Get-Content .\ServerNames.txt | %{ # Preliminary stuff for registry access $Type_SZ = [Microsoft.Win32.RegistryValueKind]::String $Hive = [Microsoft.Win32.RegistryHive]::LocalMachine $KeyPath = "SYSTEM\CurrentControlSet\services" $ComputerName = $_; # hat tip to http://stackoverflow.com/a/19015125 for this newer way of creating a custom object $ResultObj = [pscustomobject]@{ ComputerName = $ComputerName Status = "Online" SubKey = $null Original = $null Replacement = $null } if (Test-Connection -ComputerName $ComputerName -Quiet -Count 2) { # Clear the variable that will hold the registry connection $Reg = $null; # Open remote registry and if it fails then set the status accordingly try { $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $ComputerName) } catch { $ResultObj.Status = "Error"; $ResultObj } if ($Reg) { # Open the services key $Key = $Reg.OpenSubKey($KeyPath, $true) # Enumerate subkeys; and for each subkey do ... foreach ($SubkeyName in $Key.GetSubKeyNames()) { # Open the subkey read-only (else we get errors on some keys which we don't have write access to) $Subkey = $Key.OpenSubKey($SubkeyName, $false) # Get value of ImagePath $Value = $Subkey.GetValue("ImagePath") # Match ImagePath to see if it has an exe; if yes, extract the exe path. Note: extract only exe path, not arguments. # If this extracted path doesn't start in quotes and when we split it for spaces we get more than one result, then enclose path in double quotes. if ($Value -match ".*\.exe" ) { if (($Matches[0] -notlike '"*') -and (($Matches[0] -split '\s').Count -gt 1)) { $Replacement = '"' + $Matches[0] + '"' $NewValue = $Value -replace ".*\.exe",$Replacement $ResultObj.SubKey = Split-Path -Leaf $SubKey; $ResultObj.Original = $Value; $ResultObj.Replacement = $NewValue; $ResultObj if ($FixIt) { # re-open the key with read-write permissions $Subkey = $Key.OpenSubKey($SubkeyName, $true) $Subkey.SetValue("ImagePath","$Replacement"); if ($?) { Write-Host -ForegroundColor Green "Success!" } else { Write-Host -ForegroundColor Red "Something went wrong!" } } } Clear-Variable Matches } } } } else { $ResultObj.Status = "Online"; $ResultObj } } |
I am being lazy and not really offering input etc as I just expect a list of servers to be scanned in a file called “ServerNames.txt”. I have the above saved to a .ps1
file and I simply run it as .\Registry.ps1
. Feel free to adapt to your needs.
What it does is that it connects to the server specified (provided they are online), tries to open the “services” key under HKLM (assuming it has access), and then enumerates all the subkeys that contain the service names and checks if the path has a space. It only matches against paths containing a .exe so it could miss out some stuff. Once it finds a match it extracts the bit up to the .exe
, splits it along any spaces, and if there are more than one results (which means the path did have spaces) it encloses it in double quotes and replaces the original entry.
The code is smart enough to know it must correctly double quote something like "D:\Program Files (x86)\BigHand\BigHand Workflow Server 4.6\BHServer.exe /V:4.6"
as "D:\Program Files (x86)\BigHand\BigHand Workflow Server 4.6\BHServer.exe" /V:4.6
and not "D:\Program Files (x86)\BigHand\BigHand Workflow Server 4.6\BHServer.exe /V:4.6"
.
By default it only displays results. An optional parameter -FixIt
will also make the changes.
Example output:
1 2 3 4 5 |
ComputerName : solarwinds02 Status : Online SubKey : SWJMXBridgeSvc Original : D:\Program Files (x86)\SolarWinds\Orion\APM\jmxbridge\jsl\jsl.exe Replacement : "D:\Program Files (x86)\SolarWinds\Orion\APM\jmxbridge\jsl\jsl.exe" |
Hope it helps!