• Travis McCormack

Finding a 0 Day Race Condition

The background

Recently while performing an application penetration test for one of our clients we discovered a very serious arbitrary code execution vulnerability in their desktop agent. In a way the flaw was hiding in plain sight, and caused by a very simple error in file permissions. At this time we cannot reveal the client or software that was affected as we await approval from the client to disclose.

The environment included a desktop agent, which we were using the Windows version of, and that agent connects to a web service which provides commands on patches to install, scripts to run to gather data, etc. Overall, from the web portal an administrator could run any kind of PowerShell scripts they want. Our focus however was on compromising the execution of the agent itself.

So where do we start?

First we would want to observe what this application is doing. One aspect of that was monitoring the web traffic to understand how the agent is controlled by the web application. We quickly realized the agent utilized TLS Client Authentication and needed to find a way around that. We shared a tip a few weeks ago directly related to this issue and how we obtained the certificate and used it to intercept traffic from the agent. You can find that post here: TLS Client Authentication While Testing Applications

The next thing we wanted to monitor involved every file, registry key, or database that the agent was interacting with locally for further review. For this we like to use Process Monitor (Commonly called ProcMon) from the SysInternals suite. In ProcMon we filtered to only see events from our target process and then let it run for a while to identify what it interacts with.

Finding the flaw

While observing the application’s operations we discovered that scripts sent from the web application to be executed are stored temporarily in the C:\ProgramData\{agent directory}\ directory as shown here:

As we see the PowerShell scripts are being written to the ProgramData directory in Windows. This is a very common place for applications to store files they need for their operation because it does not require elevated rights so running the application in user space still allows interaction with log or configuration files etc. that are stored there. Because of this by default ALL authenticated users can write to subdirectories of ProgramData.

Knowing this behavior we quickly checked the subdirectory and sure enough everyone had write permission to the directory, but did not have permission to edit the files. Furthermore, the scripts were deleted after executing so we would need to put our own script in place before it is executed if we wanted the agent to run our arbitrary code in place of the intended code.

Finally the fun part...exploitation!

All we needed now was to setup our own PowerShell script that would perform an action we wanted performed, and then create our own watchdog script to observe for the agent creating scripts and replace them before they were executed and deleted. For this we found a Python library called WatchDog which offered the functionality we needed. Instead of writing our own watchdog for this proof of concept we simply utilized the prebuilt watchmedo module. For an unknown reason in my VM this worked most reliably when running the watchdog from the source directory instead of providing the full path.

watchmedo shell-command –patterns=*.ps1” –recursive –command=’copy {path to your powershell}.ps1 ${watch_src_path}C:\ProgramData\{agent directory}

The command above instructs watchmedo to monitor for files created with the .ps1 extension, and then copy a file we specify into the path that the watchdog triggered on seeing a .ps1 file created. This allowed us to monitor and replace the PowerShell scripts even though we could not predict their directory name or file name.

For our replacement PowerShell script we used a very simple proof of concept to show that the agent executed our script as system successfully. We leveraged this flaw to escalate privileges by adding a new user as a local administrator to the target.

net user /add raceCondition
net localgroup Administrators raceCondition /add
echo “race won” >> {path to your desktop} raceCondition.txt

This simple setup was very noisy when attempting to trigger and thus could be refined greatly, but it got the job done. While running we saw the result of our copy shell command running each time there was a change in the directories we were monitoring.

From here we simply watch the desktop for the creation of our text file letting us know that the race condition has executed successfully, and then we can confirm our user was also created as we see here:

Remediating the issue

We provided a few suggestions ranging in effort required, and protection offered, to remediate this vulnerability. Put simply the most complete fix would involve file integrity controls such as validating a hash of the file to execute matches a hash that the server indicates is valid for the script it sent. Additionally, storing the files in a protected directory would help to mitigate much of this problem.

Ultimately, our client chose to perform the quickest fix first which included setting strict permissions on their subdirectory in ProgramData so that only the agent process or administrators could write to the directory. This fix was deployed within a week of our report on the issue to protect their customers.

Are you looking for a security assessment for your network or applications? Send us an email at

308 views0 comments