CVE-2020–1088 — Yet another arbitrary delete EoP

Søren Fritzbøger
CSIS TechBlog
Published in
6 min readMay 18, 2020

In January of 2020 I found and reported an Escalation of Privilege (EoP) vulnerability that allowed arbitrary deletion of files using the Windows Error Reporting service. It was fixed in the May 2020 Windows update.

CVE-2020–1088 is yet another vulnerability based on symbolic link redirections within privileged services in Windows. If you are new to these types of vulnerabilities, I recommend reading An introduction to privileged file operation abuse on Windows by @clavoillotte which explains how these vulnerabilities work. Of course, none of this could have been done without the research of James Forshaw and his continued effort to expose the attack surface of privileged services in Windows, so a big thanks to him for his work.

Windows Error Reporting (WER) has had a lot of vulnerabilities the last year or so. In 2020 alone, 10 vulnerabilities have been fixed in WER, and before that Gal De Leon gave the talk “Exploiting Errors in Windows Error Reporting” (video / slides) at BlueHatIL showing even more vulnerabilities.

WER is split into a few different applications in Windows. For example when an app crashes, WerFault.exe will handle the error reporting for the application. This is done with the same privileges as the user running the crashed application. At the same time, the service Windows Error Reporting Service (WerSvc) is started under the LocalSystem user. WerFault.exe will communicate with WerSvc over raw ALPC calls. The implementation of this service can be found in %SystemRoot%\System32\WerSvc.dll.

CVE-2020–1088 is a classic arbitrary file delete vulnerability, where we can trick WerSvc to delete a file of our choosing. The vulnerability happens when ETW sessions are flushed in CWerService::SvcCollectETWLogs. In here, ETW sessions are added from the registry keys HKLM\Software\Microsoft\Windows\Windows Error Reporting\EtwSessions and HKLM\Software\Microsoft\Windows\Windows Error Reporting\DynamicEtwSessions as it can be seen in the following snippet

WerSvc!CWerService::SvcCollectETWLogs — Sessions from registry

I am not quite sure when these registry keys get populated but my best guess is by one of the many Windows diagnostic settings. In my case, I had a key with the value WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0, most likely related to Windows Performance Recorder (Hence the prefix WPR)

DynamicEtwSessions registry keys

When a ETW session trace is initiated, a folder with a random GUID and a file name matching the registry key from above will be created under %UserProfile%\AppData\local\Temp\, in my case the full path is: %UserProfile%\AppData\local\Temp\{GUID}\WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0.buf.

Later on, CEtwSession::FlushBuffered will be called to flush the trace file in the temporary GUID folder. The interesting pseudo-code looks something like this:

LPCWSTR fileName = sprintf("%s\\%s.buf.etl", path, lpFileName);
LPCWSTR newFileName = sprintf("%s\\%s.etl", path, lpNewFileName);
hFile = CreateFileW(fileName, ...)...if(!MoveFileW(fileName, newFileName)) {
DeleteFileW(fileName);
}

In other words, the service will try to rename the file from filename.buf.etl to filename.etl, and if this fails then delete the file.

So now we have a service running with SYSTEM integrity deleting a file in a folder fully controllable by the currently logged in user. That doesn’t sound dangerous at all, right?

How do we exploit this?

By now we have the following information of the vulnerability:

  1. The vulnerable code path can be triggered by crashing an application
  2. A random folder is created under %UserProfile%\AppData\Local\Temp\{GUID}
  3. The file WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0.buf.etl is created under this folder
  4. The file is then renamed to the extension .etl, and if this fails the .buf.etl file will be deleted
  5. WerSvc does not impersonate the calling user, so the file actions are done with SYSTEM integrity

At this point, anyone with knowledge of how symbolic link redirection vulnerabilities work should be able to think of an exploit path pretty easily. For those who does not have that knowledge, the following constraints are important to know:

  1. In order to redirect a file operation, two things can be done (more things can be done, but let's keep it simple). Either the parent folder can be turned into a mount point/junction, or the file can be turned into a hardlink. Mitigations for hardlinks were implemented in build 20H1 so they no longer work. Also, this is an arbitrary delete vulnerability where hardlinks would not work.
  2. Because we need to turn the parent folder into a mountpoint, we need to know the name of the folder before the WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0.buf.etl file is moved
  3. We can force the MoveFileW call to fail by ensuring that the directory is a mountpoint before MoveFileW is called

With this information we can move on to the actual exploitation. The following flow is how the PoC works:

  1. Create a file watcher that looks for folder creations under %UserProfile%\AppData\Local\Temp
  2. Crash a process (Powershell.exe -Command “[Environment]::FailFast(‘Error’)”)
  3. Continuously try to delete the file %UserProfile%\AppData\Local\Temp\{GUID}\WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0.buf.etl. Once successful go to the next step
  4. Create a mountpoint from %UserProfile%\AppData\Local\Temp\{GUID} to \RPC Control
  5. Create a symbolic link from \RPC Control\WPR_initiated_DiagTrackMiniLogger_OneTrace User Logger 20200113 1 Event Collector_0.buf.etl to \??\C:\Windows\System32\license.rtf

Step 3 could most likely be solved better with an OpLock, but for this vulnerability it didn’t really matter as the PoC worked 100% of the time. A successful exploit can be seen on the following Process Monitor screenshot where C:\Windows\System32\license.rtf is deleted.

A full PoC can be found here.

Successful exploitation of the vulnerability. SetDispositionInformationEx is the operation that deletes the file.

How was it fixed?

As I mentioned in the beginning of this post, WER has seen a lot of similar vulnerabilities over the past year(s). Because of this, a method was added to the WER codebase to check if the file handles points to actually match the expected path. The method, UtilVerifyFilePath, is then used in the wrapper method for deleting a file, UtilDeleteFilePath.

If we look back at the snippet from CEtwSession::FlushBuffered, the call to DeleteFileW is now replaced with a call to UtilDeleteFilePath. Therefore, the relevant code now looks like this:

LPCWSTR fileName = sprintf("%s\\%s.buf.etl", path, lpFileName);
LPCWSTR newFileName = sprintf("%s\\%s.etl", path, lpNewFileName);
hFile = CreateFileW(fileName, ...)...if(!MoveFileW(fileName, newFileName)) {
UtilDeleteFilePath(fileName);
}

So, we can conclude that a mitigation has been implemented. Before we dive into that, I want to go into a little small detail that I did not disclose so far.

A little bonus for the (not so) attentive reader

In the original codesnippet for CEtwSession::FlushBuffered I skipped a little detail that the trained eye probably already caught. The method calls MoveFileW before it calls DeleteFileW. Obviously, this can be exploited as well to move a file of your choosing to an arbitrary folder.

Given we have a mountpoint from the GUID folder to \RPC Control. From here we can create two symbolic links. One from filename.etf.buf to a payload of our choice, for example %UserProfile%\payload.dll and another from filename.etf to C:\Windows\System32\windowscoredeviceinfo.dll. This allows us to use UsoDllLoader from Clément Labro to get a system shell.

Looking at the new code, this call is still there meaning we can just abuse the MoveFileW call to plant our payload, right? Well, not really. In the May 2020 edition of Windows 10 codenamed 2004, a new method was added to the WER libraries, UtilVerifyAndLockDirectory(LPCWSTR path, HANDLE handle). The following snippet is a simplified version of the method with some of the error handling removed.

Essentially the method tries to create a random file in the given path and then returns a handle to this file. This should (in theory) lock the folder and prevent an user from tampering with the folder. The caller is then responsible for closing this handle after all “dangerous” file operations.

As you might have heard, Microsoft changed the scope of the Windows Preview bug bounty program, so vulnerabilities involving file path redirection through junctions or mountpointsare no longer valid. Apparently they are working on broader mitigations to mitigate this entire bug class.

I hope you enjoyed the article. If you have any questions don’t hesitate to contact me on twitter @fritzboger.

Congratulations for reading this far. Your gift is a little bonus vulnerability as this vulnerability was not the only one fixed in the May patch. Look for the usage of UtilVerifyAndLockDirectory in Wersvc.dll and you might find another one.

Disclosure timeline

  • 2020–01–21 — Vulnerability reported to MSRC
  • 2020–01–24 — MSRC followed up asking for more information on how to reproduce
  • 2020–02–03 — Vulnerability confirmed
  • 2020–04–02 — Bounty awarded
  • 2020–05–12 — Vulnerability fixed in the May Patch Tuesday and given CVE-2020–1088

--

--