Arbitrary file write in VaultSvc

TL;DR

A denial of service vulnerability (CVE-2020-1076) exists when the Credential Manager (VaultSvc) improperly handles symbolic links resulting in a low privileged user being able to write arbitrary files.

Description

The VaultSvc service binary is lsass.exe and the service itself is implemented in the vaultsvc.dll file. The service runs in the context of the local SYSTEM account and it is auto-started during system boot to initialize the credential vault. The initialization creates the %LOCALAPPDATA%\Microsoft\Vault\UserProfileRoaming directory and the Latest.dat file using impersonation. The below is the relevant excerpt of the stack related to the CreateFile operation during initialization that happens right after the user logged in.

1ServiceMain+7c8d    CVaultRoamingNotification::RaiseNotification2
2ServiceMain+7bb3    CUserVaultMgr::Initialize
3ServiceMain+60ff    CVaultMgr::GetUserVaultManager
4ServiceMain+8ffa    VltOpenVault

I have found that after the initialization process the VaultSvc service will call CVaultRoamingNotification::RaiseNotification2 again, this time without impersonating the user. The below is the relevant excerpt of the stack related to the CreateFile operation after the initialization process.

1ServiceMain+7c8d    CVaultRoamingNotification::RaiseNotification2
2ServiceMain+d67c    CVaultRoamingNotification::RaisePendingNotifications
3ServiceMain+cdc7    CVaultMgr::RaisePendingNotificationsForAllUsers

The fact that CVaultRoamingNotification::RaiseNotification2 is called without impersonation means that there is a TOCTOU vulnerability here: by setting up a pseudo-symlink and placing an oplock on the target of the symlink, we can change the symlink when the target file is opened and make it point to another target file. That is, replacing the Latest.dat file with a symbolic link after the initialization completed, but before the notifications are raised, allows a low privileged user to create or overwrite an arbitrary file. The BaitAndSwitch tool in James Forshaw’s Symbolic Link Testing Tools implements this technique.

Note that the CVaultRoamingNotification::RaiseNotification2 function will check the time of the last modification and will only write the file if sufficient time has passed. The below is the relevant excerpt of the pseudocode.

1if (GetFileTime(hFile, 0, 0, &LastWriteTime))
2{
3    GetSystemTimeAsFileTime(&SystemTimeAsFileTime);
4    if (CompareFileTime(&SystemTimeAsFileTime, &LastWriteTime) <= 0  ||
5        ((0x0D6BF94D5E57A42BD * (*&SystemTimeAsFileTime - *&LastWriteTime) >> 64) >> 23) <= v7)
6        {
7            [...]
8        }
9}

Steps to reproduce

  1. Delete the %LOCALAPPDATA%\Microsoft\Vault\UserProfileRoaming folder and the Latest.dat file.
  2. Create %USERPROFILE%\Foo with a recent dummy Latest.dat file as bait.
  3. After restart and login you will have ~1 minute to do the next step.
  4. Use BaitAndSwitch.exe with FILE_SHARE_WRITE to create a pseudo-symlink to C:\Windows\System32\foobar.dll.
  5. Wait a few seconds for the VaultSvc service to complete the initialization process.
  6. The foobar.dll file has been created in the protected C:\Windows\System32 folder.

Again, the last modification time of the dummy Latest.dat file matters. If the dummy file is too old the service will overwrite it during the first stage and will not (or just much later) trigger the second stage.

PoC

Using the BaitAndSwitch.exe tool created by James Forshaw, I have executed the below command to create the pseudo-symlink.

BaitAndSwitch.exe %LOCALAPPDATA%\Microsoft\Vault\UserProfileRoaming\Latest.dat %USERPROFILE%\Foo\Latest.dat C:\Windows\System32\foobar.dll w

The below screenshot shows the events related to lsass.exe captured by Process Monitor. We can see the lsass.exe process catching the bait and checking the dummy Latest.dat while impersonating as WINDEV1912EVAL\User. Note that the time of testing was 2020. 02. 03. 00:11 and the last modification time of my dummy file was 2020. 02. 02. 02:17. After ~1 minute the first stage completed the VaultSvc service is back again, this time operating on the target file without impersonation in the context of the local SYSTEM account.

Exploitation flow in Process Monitor

Exploitation flow in Process Monitor

The below screenshot shows the console output of the PoC exploit tested on a virtual machine running Windows 10, version 1909 (10.0.18363.418).

Console output of the PoC exploit

Console output of the PoC exploit

Fix

As usual, this vulnerability was also fixed by impersonating the logged on user in the RaisePendingNotificationsForAllUsers() function. The below screenshot shows the patch diff of the affected function.

Patch diff of RaisePendingNotificationsForAllUsers()

Patch diff of RaisePendingNotificationsForAllUsers()

Timeline

⬅️ 2020-02-03: Reported issue to MSRC.
➡️ 2020-02-03: MSRC opened case 56348.
➡️ 2020-05-12: Coordinated public release of advisory.

References

Last updated:
Categories: CVEs
Tags: DoS