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


  1. You dont state if the "malicious local process" on the target system, that pre-creates the expected named pipe, has to be started as administrator or if it can be started as a normal user. Only the latter exposes a real concern.

    1. Thank you for pointing out this omission. It can be a non-admin (normal user) process. The article was updated accordingly. Thanks!

  2. Hi, I’m a student of cybersecurity and I try this with an old version of PsExec (2.22). I found the cpp code in:

    That code must be compiled? I don't find the PsExecPoc.exe. It's available for download in another web?


    1. It is generally a good idea to build a PoC from a public (untrusted, as far as you're concerned) source after reviewing the course code a bit first. It is not uncommon for fake, even malicious, PoCs to be available on various sites, causing harm to those launching them.