In my last post I continued the development of a set of scripts to identify and fix unquoted services paths in Windows, the vulnerability I demonstrated here. So far, we can get services with paths as objects and identify which ones are bad, so now its time to start fixing them…
I’ve posted updated copies of all 3 scripts with progress output and instructions for executing in parallel here. The explanation below is still valid, but I have adjusted the handling of some output to clean up the pipeline.
I spent some time trying to figure out the best way to do this. In the end, I wound up right back where I started: REG.exe
Fixing Unquoted Service Paths
REG allows me to utilize this against computers without PowerShell, as well as deliver remotely when PSRemoting isn’t enabled. Since all I have to do is get the fixed path in a format REG can understand, this script ended up being pretty short.
First, we handle input and a process loop.
[cmdletbinding()] Param ( #Define a Mandatory input [Parameter( ValueFromPipeline=$true, ValueFromPipelinebyPropertyName=$true, Position=0)] $obj ) #End Param Process { #Process Each object on Pipeline } #End Process |
That should look fairly familiar if you’ve seen my last post.
Its possible I will get objects in the pipeline that have been evaluated and do not need to be changed, so I need to account for this. Beyond that, its a simple matter of escaping quotes correctly (ironically the very skill that got us into this mess!) and feeding it to the REG ADD command.
if ($obj.badkey -eq "Yes"){ $regpath = $obj.Fixedkey $regpath = '"' + $regpath.replace('"', '\"') + '"' + ' /f' Write-Output "$($obj.ComputerName) : $($obj.FixedKey) : $regpath" REG ADD "\\$($obj.computername)\$($obj.key)" /v ImagePath /t REG_EXPAND_SZ /d $regpath } |
That’s pretty much it. We are only writing out those objects that require a change, so good keys are getting dropped in this script. It’s designed to be the end of the pipeline (or last before something like Out-File) and those things that don’t need edits are being ignored. REG.exe escapes with \, so its fairly straight forward. We hold the escaped version in a temporary string and feed to REG ADD with the /f (force) parameter. We get back single line statements that contain the computer name, the old path, and the new path we’re writing (delimited by colons). It also writes the output of the REG command, so you’ll see “The Command Completed Successfully” a lot. If REG throws an error, you’ll see it in this output.
And that’s all she wrote folks. I still have the outstanding task to try to catch unflagged arguments, but they are not common enough to hold up putting this out there. I will update when I’ve got a solution for handling that argument.
Final Thoughts
This whole thing stems from developers who do not escape their strings correctly. After something like 10+ years, we’re still dealing with (and in a lot of cases, just discovering) this vulnerability. The right answer for your vulnerabilities is to demand your developers and vendors write their code correctly.
That said – hopefully this is a tool that will help you keep the band aids on between releases. You will need to run this periodically. As you discover those titles that are vulnerable, you’ll need to keep up with desktop and server deployments, as well as application patches for software that you identify as vulnerable but haven’t fixed yet. That is part of the reason I cut these scripts up like I did. This gives us both a reporting tool and a correcting tool, so we can monitor our environments and quickly remediate those things we find if appropriate.
I cannot say it enough, check your results before you run the fix. Look for those unflagged arguments. Exceptions that fall outside what is coded for here are uncommon, but I have found them. At the end of the day, I cannot be liable for what you do with this code, but I do hope you use it and its helpful. If you have suggestions or comments or would like to see something changed, please leave me a comment or send me an email at Jeff@RyanandJeffShow.com
As an aside, Hey Microsoft! Can we stop parsing these paths this way and start reading them literally? Can you provide us some insight on why this hasn’t been done and what prevents it from happening? Thanks!
Here’s the whole Fix script.
#Fix-BADSVCPath.ps1 [cmdletbinding()] Param ( #Define a Mandatory input [Parameter( ValueFromPipeline=$true, ValueFromPipelinebyPropertyName=$true, Position=0)] $obj ) #End Param Process { #Process Each object on Pipeline if ($obj.badkey -eq "Yes"){ $regpath = $obj.Fixedkey $regpath = '"' + $regpath.replace('"', '\"') + '"' + ' /f' Write-Output "$($obj.ComputerName) : $($obj.FixedKey) : $regpath" REG ADD "\\$($obj.computername)\$($obj.key)" /v ImagePath /t REG_EXPAND_SZ /d $regpath } } #End Process |