Thursday, January 28, 2021

Windows Installer Local Privilege Escalation 0day Gets a Micropatch


by Mitja Kolsek, the 0patch Team


[Update 2/9/2021: February 2021 Windows Updates included an official fix for this vulnerability and assigned it CVE-2021-1727. According to our guidelines, this micropatch is no longer FREE, but part of a PRO subscription.]

On December 26, security researcher Abdelhamid Naceri published a blog post with a number of 0days in various security products and a local privilege escalation 0day in Windows Installer. We were mostly interested in the latter.

Abdelhamid provided a proof-of-concept (the GitHub repository is disabled at the time of this writing) which allowed us to quickly reproduce the issue on Windows 10 v2004, but we were having difficulties reproducing it on other Windows 10 versions and older Windows systems. It took us a while to troubleshoot the underlying problem with reproduction and come January 2021 Patch Tuesday, it turned out this vulnerability wasn't patched by Microsoft. Having successfully reproduced the issue by then on all Windows versions back to Windows 7, we decided to create a micropatch to protect Windows users waiting for the official patch. (The micropatch would also be the only available patch for Windows 7 without Extended Security Updates (ESU), or Windows 7 with only the first year of ESU.)

The Vulnerability

This vulnerability is a bypass of Microsoft's fix for CVE-2020-16902 (described by Abdelhamid in detail here), which was itself a bypass of Microsoft's fixes for CVE-2020-0814 and CVE-2020-1302 (also found by Abdelhamid), both of which were a bypass of Microsoft's fix for CVE-2019-1415 (found by SandboxEscaper and described here).

Confusing? Well, some things aren't easy to fix, and Windows Installer is a pretty complex beast that can break a leg if you fix its arm, and then break its tail when you fix the leg. So you want to be careful when fixing.

The core of this vulnerability, and all others listed above, is in tricking Windows Installer into using attacker's own rollback script (a *.rbs file) instead of the rollback script created by msiexec.exe during the installation. See, when installing an MSI package, Windows Installer gradually builds up a rollback script in case the installation should fail at some point, and all changes made up to that point would have to be reverted. But if a local non-admin attacker manages to replace that rollback script with one that "reverts" some system registry value such that it will point to attacker's executable..., well, we get a local privilege escalation.

The proof-of-concept is using a rollback script that changes the value of  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Fax\ImagePath to c:\Windows\temp\asmae.exe, which results in the Fax Service using attacker's asmae.exe when the service is launched. This service was used because any user is allowed to launch it, and it's running as Local System.

As far as this particular 0day goes, Microsoft's fix - which it bypasses - was attempting to block the planting of a malicious rollback script by first determining if it was safe to use the default c:\Config.Msi folder for storing the rollback script, and if not safe, using a different folder, c:\Windows\Installer\Config.Msi, instead. Abdelhamid noticed a logical flaw in this fix, forced Windows Installer to keep using c:\Config.Msi, and then performed the same steps as in his CVE-2020-16902 proof-of-concept to elevate himself to Local System.

Our Micropatch

We confess we do not understand why Microsoft decided to add more complexity with their fix for CVE-2020-16902 when they could have just unconditionally use the c:\Windows\Installer\Config.Msi folder for the callback script and completely avoid numerous attack vectors that c:\Config.Msi is exposed to. Maybe they didn't want to clutter the Windows folder.

Be it as it may, we decided that if Microsoft deemed c:\Windows\Installer\Config.Msi folder to be acceptable for hosting the rollback script under some attacker-controllable conditions, it shouldn't break anything if we forced Windows Installer to always use it for rollback scripts. It is running as Local System so permissions shouldn't be a problem, and a local attacker can't touch this folder in any relevant way.

And here it is, the single-instruction micropatch that fixes this 0day by changing the logic of Microsoft's fix for CVE-2020-16902 such that it now always decides to use c:\Windows\Installer\Config.Msi folder:

MODULE_PATH "..\Affected_Modules\msi.dll_5.0.19041.746_64bit\msi.dll"
VULN_ID 6912

        mov ebx,1    ; use C:\Windows\installer\Config.Msi instead of C:\Config.Msi

Here's a video of the micropatch in action. You can see that without our micropatch, the POC, launched by a local non-admin user, successfully modifies a registry value pointing to the Fax Service executable, leading to execution of attacker's code by Local System. With our micropatch applied, the POC is blocked because Windows Installer cannot be tricked into using c:\Config.Msi anymore.

We created this micropatch for the following Windows versions:

  1. Windows 10 v20H2, 32bit and 64bit, updated with January 2021 updates
  2. Windows 10 v2004, 32bit and 64bit, updated with January 2021 updates
  3. Windows 10 v1909, 32bit and 64bit, updated with January 2021 updates
  4. Windows 7, 32bit and 64bit, with ESU, updated with January 2021 updates
  5. Windows 7, 32bit and 64bit, without ESU, updated with January 2020 updates

What about Windows Servers? Fortunately, Windows Servers have a default security policy preventing non-admin users from launching any installations, which successfully prevents exploitation of this vulnerability. Nevertheless, our Windows 7 micropatches will also work on Windows Server 2008 R2, updated to January 2020 (without ESU), or to January 2021 (with ESU) should their system configuration allow non-admin installations.

According to our guidelines, this micropatch is free for everyone until Microsoft issues an official fix for it. By the time you're reading this the micropatch has already been distributed to all online 0patch Agents and also automatically applied except where Enterprise policies prevented that. If you're not a 0patch user and would like to use this micropatch on your computer(s), create an account in 0patch Central, install 0patch Agent and register it to your account. Note that no computer restart is needed for installing the agent or applying/un-applying any 0patch micropatch.
We'd like to thank Abdelhamid Naceri for their analysis of the vulnerability and an elegant proof-of-concept, which allowed us to create a micropatch.

While you're here: If your organization has Windows 7 or Server 2008 R2 machines with Extended Security Updates and wouldn't mind saving lots of money on less expensive security patches in 2021 that don't even need your machines to be restarted, proceed to our New Year's Resolution. The same applies if you're still using Office 2010 and want to keep patching critical vulnerabilities now that support has ended.

To learn more about 0patch, please visit our Help Center.  

Thursday, January 7, 2021

Local Privilege Escalation 0day in PsExec Gets a Micropatch



by Mitja Kolsek, the 0patch Team

[Update 3/25/2021: Seventy-seven days after we had issued a free micropatch for a local privilege escalation in Microsoft PsExec, a new PsExec version fixes the issue. 0patch users staying on version 2.32 remain protected by our micropatch. This issue was assigned CVE-2021-1733, although it was not properly fixed in version 2.32 as stated in Microsoft's advisory.]
[Update 2/17/2021: Corrected a sentence implying that PsExec may be part of various enterprise tools, while it's just commonly used in conjunction with such tools. (Thanks @wdormann)]

[Update 1/28/2021: Since our publication of micropatch for PsExec version 2.2, PsExec has been updated to versions 2.30, 2.31 and finally 2.32. where it still resides today. David was able to update his POC for each version so the current version 2.32. is still vulnerable to the same attack. Since this version seems to be here to stay for a while, we decided to port our micropatch to it to keep 0patch users with the latest PsExec version protected.]

Last month, security researcher David Wells of Tenable published an analysis of a local privilege escalation vulnerability in PsExec, a powerful management tool from SysInternals (acquired by Microsoft) that allows launching executables on remote computers.
It would be hard to find a Windows admin who hasn't used PsExec at some point in time, and just a tiny bit less hard to find one who isn't using it on a regular basis. Granted, some may not even know they're using PsExec because it's commonly used in conjunction with various enterprise tools - tools like JetBrains TeamCity, BMC Server Automation, Chocolatey and SolarWinds Orion.

The Vulnerability

The vulnerability is a pretty classic named pipe hijacking (a.k.a. named pipe squatting). When PsExec tries to launch an executable on the remote computer, it creates a temporary Windows service there using PSEXESVC.EXE which it extracts from its own body, launches that service under Local System user, and connects to its named pipe to provide it launch instructions. PSEXESVC.EXE creates the named pipe with permissions that don't allow a non-admin or non-system user to connect to it, which is good because otherwise any user could instruct the service to run arbitrary executable as Local System.
Now, the attack comprises a malicious local unprivileged process creating a named pipe with the same name as PSEXESVC.EXE uses, only before the service creates it. PSEXESVC.EXE, running as Local System, subsequently tries to create the same named pipe, but merely re-opens the existing one, leaving its permissions intact. At that point, attacker can connect to the named pipe and make the service run anything.
David has provided an elegant proof-of-concept for this vulnerability.
So which systems are at risk by this issue? Basically every Windows machine that admins remotely launch executables on using PsExec (or management tools utilizing PsExec) if the machine already has a non-admin attacker there trying to elevate their privileges.

Official Patch? It's... Complicated

At the time of this writing, there is no official patch available from Microsoft. PsExec.exe, and PsExec64.exe, which encapsulate the vulnerable PSEXESVC.EXE, are part of the PsTools suite, and were last updated in June 2016. According to Tenable's write-up, PsExec versions from 1.72 (built in 2006) to the latest version 2.2 (built in 2016) are all affected, meaning that the vulnerability has been there for about 14 years. [Update 1/28/2021: current version 2.32 is still affected.]

Note that PsExec is not part of Windows, and is also unlikely to be patchable with Windows Updates as it doesn't even have a designated installation location (one can just copy it anywhere and use it as a standalone executable). PsExec also doesn't have its own integrated update mechanism, meaning that while Microsoft can issue a new, patched version of it and put it on their website, all the vulnerable PsExec's out there will remain vulnerable until admins manually replace them with this new version.

Our Micropatch

Let's see how the relevant part of PSEXESVC.EXE looks where named pipe is created and connection requests accepted.

Function CreateNamedPipe is being called in a loop, each time waiting for an incoming request, spawning a new thread to process that request, and repeating the loop.
When fixing named pipe hijacking/squatting vulnerabilities, the obvious approach that comes to mind is using the FILE_FLAG_FIRST_PIPE_INSTANCE flag in the CreateNamedPipe call, which only allows the pipe to be created if it is the first instance of the pipe. We actually tried this approach but while it stopped the attack (as attacker's pipe was the first instance), it also broke PsExec because in the above loop, when the first request is accepted and sent for processing, a new instance of the pipe gets created - which is no longer the first instance.
So we went for the second best option - checking for existence of the named pipe immediately before the loop. We used a call to CreateNamedPipe with FILE_FLAG_FIRST_PIPE_INSTANCE to determine if a named pipe with this name already exists - and if so, we immediately terminate PSEXESVC.EXE, logging an "Exploit Blocked" event in the process.

Our micropatch has only 21 CPU instructions and should be easy to understand for anyone knowing x86 assembly and Windows API functions:

MODULE_PATH "..\Affected_Modules\PSEXESVC.exe_2.2_32bit\PSEXESVC.exe"
VULN_ID 6910

    PIT Kernel32.dll!CreateNamedPipeW,PSEXESVC.exe!0x4e7a,Kernel32.dll!CloseHandle,Kernel32.dll!ExitProcess
    ; 0x4e7a -> __swprintf

        ; first three instructions repeated from original code to make
        ; room for the patch JMP
        lea eax, [ebp-414h]        ; buffer for pipe name
        push eax                   ; buffer on stack
        call PIT_0x4e7a            ; call __swprintf
        push 0                     ; lpSecurityAttributes
        push 0                     ; nDefaultTimeOut, A value of zero will result
                                   ; in a default time-out of 50 milliseconds.
        push 10000h                ; nInBufferSize
        push 10000h                ; nOutBufferSize
        push 0ffh                  ; nMaxInstances (the same number must be specified
                                   ; for other instances of the pipe.)
        push 6                     ; dwPipeMode (The same type mode must be specified
                                   ; for each instance of the pipe.)
        push 80003h                ; dwOpenMode - FILE_FLAG_FIRST_PIPE_INSTANCE
        lea eax, [ebp-414h]        ; buffer for pipe name
        push eax                   ; lpName
        call PIT_CreateNamedPipeW  ; Creates an instance of a named pipe and returns
                                   ; a handle for subsequent pipe operations.
        mov edi, eax
        cmp eax, 0xFFFFFFFF        ; check if handle exists
        jne CONTINUE               ; if Handle != -1 (INVALID_HANDLE_VALUE) continue
                                   ; with normal execution
        call PIT_ExploitBlocked    ; Exploit blocked pop up
        push -1                    ; uExitCode
        call PIT_ExitProcess       ; Ends the calling process and all its threads.   
        push edi                   ; edi = pipe handle
        call PIT_CloseHandle       ; close the pipe handle.

Here's a video of the micropatch in action:

According to our guidelines, this micropatch is immediately available to ALL 0patch users for absolutely no cost. Note that no computer restart is needed for installing the agent or applying/un-applying any 0patch micropatches.


Frequently Asked Questions [Updated 1/28/2021]

Q: Which versions of PsExec does the micropatch fix?

A: Our micropatch currently applies to 32bit and 64bit PsExec versions 2.2 and 2.32. We might port it to older versions of PsExec as needed.
Q: How do we get the micropatch applied?
  1. Create a free 0patch account at
  2. Download and install 0patch Agent on all computers on which you're running executables with PsExec, then register it to your 0patch account.
  3. Make sure to use PsExec 2.2 or 2.32, or the micropatch won't get applied

Q: Does 0patch Agent have to be running on computers where we run PsExec, or remote computers where executables get launched using PsExec?
A: 0patch Agent needs to be running on the remote computers where executables get launched using PsExec. What PsExec does is copy PSEXESVC.EXE to the remote computer (into c:\Windows) and registers it remotely as a service on that computer, then launches that service. This remote PSEXESVC.EXE is what needs to be patched. Note that 0patch Agent can also safely be running on the computer where you run PsExec.
Q: Can we easily deploy this patch to multiple computers?
A: 0patch Agent supports silent (unattended) installation with auto-registration, and central management via 0patch Central. Please see User Manual for details and ask for an Enterprise trial.

Q: Will the micropatch also fix PsExec that is integrated into our enterprise product?

A: As long as PsExec used by the product is version 2.2 or 2.32, our micropatch will fix it. But again, 0patch Agent must be present on computers being managed by the enterprise product, not on the machine where said product is installed. If your enterprise product is using another version of PsExec and you cannot replace it, please contact

Q: Is there any other way to prevent exploitation of the described vulnerability?

A: Not to our knowledge. Until Microsoft issues a fixed version of PsExec, ours is the only patch that exists.

Q: Is this vulnerability a big deal?

A: Depends on your threat model. This vulnerability allows an attacker who can already run code on your remote computer as a non-admin (e.g., by logging in as a regular Terminal Server user, or establishing an RDP session as a domain user, or breaking into a vulnerable unprivileged service running on the remote computer) to elevate their privileges to Local System and completely take over the machine as soon as anyone uses PsExec against that machine. For home users and small businesses this is probably not a high-priority threat, while for large organizations it may be.

We'd like to thank  David Wells of Tenable for their excellent presentation of the vulnerability and an elegant proof-of-concept, which allowed us to create a micropatch.

While you're here: If your organization has Windows 7 or Server 2008 R2 machines with Extended Security Updates and wouldn't mind saving lots of money on less expensive security patches in 2021 that don't even need your machines to be restarted, proceed to our New Year's Resolution. The same applies if you're still using Office 2010 and want to keep patching critical vulnerabilities now that support has ended.

To learn more about 0patch, please visit our Help Center