In MSDN's Process Security and Access Rights article there is an interesting remark:
... if it can be duplicated, it can duplicate it.
If it is free to translate it into Russian, then it says that having a descriptor to the process with the PROCESS_DUP_HANDLE access right we can, using the DuplicateHandle (...) function, get a descriptor with the maximum allowed access masks for this process.
The source code that exploits this feature is quite simple:
#include <Windows.h> int wmain(int argc, PWSTR argv[]) { HANDLE ProcessAllAccessHandle; HANDLE ProcessDuplicateHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, _wtoi(argv[1])); if (ProcessDuplicateHandle) { if (DuplicateHandle(ProcessDuplicateHandle, GetCurrentProcess(), GetCurrentProcess(), &ProcessAllAccessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { CloseHandle(ProcessAllAccessHandle); } CloseHandle(ProcessDuplicateHandle); } return 0; }
As a result of compilation and linking, we get a test utility that takes as an argument the target process identifier (PID). Then the utility opens the specified process with the right PROCESS_DUP_HANDLE . Thus, we model the necessary condition for the presence of a handle to a process with the right PROCESS_DUP_HANDLE (== 0x40).
As a demonstration, I will be tracing the compiled utility in WinDbg:
0:000> lsa @$ip 0,3 > 13: if (ProcessDuplicateHandle) 14: { 15: if (DuplicateHandle(ProcessDuplicateHandle, 0:000> !handle @@C++(ProcessDuplicateHandle) 3 Handle 80 Type Process Attributes 0 GrantedAccess 0x40: None DupHandle HandleCount 9 PointerCount 260518
And then flick of the wrist By calling DuplicateHandle (...) we get the second descriptor for the same process, but with the broadest possible permissions:
0:000> lsa @$ip 0,3 > 23: CloseHandle(ProcessAllAccessHandle); 24: } 25: CloseHandle(ProcessDuplicateHandle); 0:000> !handle @@C++(ProcessAllAccessHandle) 3 Handle 84 Type Process Attributes 0 GrantedAccess 0x1fffff: Delete,ReadControl,WriteDac,WriteOwner,Synch Terminate,CreateThread,,VMOp,VMRead,VMWrite,DupHandle,CreateProcess,SetQuota,SetInfo,QueryInfo,SetPort HandleCount 10 PointerCount 292877
The key point is the GrantedAccess value, which in the new descriptor is 0x1fffff, which corresponds to PROCESS_ALL_ACCESS . Unfortunately, WinDbg does not display the PID of the target process. But to make sure that the descriptor is received for the desired process, you can look at the Process Explorer's handles (after having specified the PID in the command line arguments in the debugger):
0:000> dx argv[1] argv[1] : 0x1b7c2e2412c : "21652" [Type: wchar_t *]
In the screenshot, the utility opens descriptors on the running notepad.exe.
Firstly, when duplicating a descriptor, if the object access mask does not expand (and we specifically specify the operation flag DUPLICATE_SAME_ACCESS ), it does not check that the process (in which the duplicated descriptor will be created) has access to this object. It only checks that the process descriptors passed to the DuplicateHandle (...) function have the PROCESS_DUP_HANDLE access mask enabled. And then the descriptor is copied between processes without checking the access rights (I repeat: if the new descriptor has a mask of permissions not wider than the original duplicate descriptor).
And then it should be noted that the call to GetCurrentProcess () returns a constant, the same pseudo-handle (pseudo handle) mentioned at the very beginning of this publication. There are two documented pseudo-descriptors with constant values that are physically absent from the process descriptor table. But these descriptors are processed by all functions of the kernel (along with the usual descriptors from the process descriptor table):
Macro | Value | Description |
---|---|---|
ZwCurrentProcess / NtCurrentProcess | (HANDLE) -1 | Current process descriptor |
ZwCurrentThread / NtCurrentThread | (HANDLE) -2 | Current thread descriptor |
It is the value NtCurrentProcess (== -1) that returns the call to GetCurrentProcess () .
This pseudo-descriptor within the framework of a specific process means an object of this process itself with PROCESS_ALL_ACCESS rights (in fact, there are nuances, but the article is not about them). It turns out such a link to himself, but through a descriptor:
That is, our DuplicateHandle call (ProcessDuplicateHandle, GetCurrentProcess (), ...) will be interpreted as follows: from the open (target) process, duplicate the descriptor with a value of -1. And for the target process (the one to which we have the descriptor in the ProcessDuplicateHandle variable) the value is -1 and will refer to this very target process with PROCESS_ALL_ACCESS rights. Therefore, as a result, we get a descriptor on the target process with maximum rights.
I repeat the idea written at the very beginning: if someone gets a descriptor to a process with the right PROCESS_DUP_HANDLE , then within the framework of the Windows security model, he can get another descriptor to the same process, but with the rights to PROCESS_ALL_ACCESS (and do whatever he wants with the process ).
Thanks to everyone who read the publication to the end. I invite everyone to take a survey to find out how such publications can be interesting / useful to the audience.
Source: https://habr.com/ru/post/448472/
All Articles