Stealing Tokens In Kernel Mode With A Malicious Driver
I’ve recently been working on expanding my knowledge of Windows kernel concepts and kernel mode programming. In the process, I wrote a malicious driver that could steal the token of one process and assign it to another. This article by the prolific and ever-informative spotless forms the basis of this post. In that article he walks through the structure of the
_TOKEN kernel mode structures, and how to manipulate them to change the access token of a given process, all via WinDbg. It’s a great post and I highly recommend reading it before continuing on here.
The difference in this post is that I use C++ to write a Windows kernel mode driver from scratch and a user mode program that communicates with that driver. This program passes in two process IDs, one to steal the token from, and another to assign the stolen token to. All the code for this post is available here.
About Access Tokens
A common method of escalating privileges via buggy drivers or kernel mode exploits is to the steal the access token of a SYSTEM process and assign it to a process of your choosing. However this is commonly done with shellcode that is executed by the exploit. Some examples of this can be found in the wonderful HackSys Extreme Vulnerable Driver project. My goal was to learn more about drivers and kernel programming rather than just pure exploitation, so I chose to implement the same concept in C++ via a malicious driver.
Every process has a primary access token, which is a kernel data structure that describes the rights and privileges that a process has. Tokens have been covered in detail by Microsoft and from an offensive perspective, so I won’t spend a lot of time on them here. However it is important to know how the access token structure is associated with each process.
Processes And The
Each process is represented in the kernel by a doubly linked list of
_EPROCESS structures. This structure is not fully documented by Microsoft, but the ReactOS project as usual has a good definition of it. One of the members of this structure is called, unsurprisingly,
Token. Technically this member is of type
_EX_FAST_REF, but for our purposes, this is just an implementation detail. This
Token member contains a pointer to the address of the token object belonging to that particular process. An image of this member within the
_EPROCESS structure in WinDbg can be seen below:
As you can see, the
Token member is located at a fixed offset from the beginning of the
_EPROCESS structure. This seems to change between versions of Windows, and on my test machine running Windows 10 20H2, the offset is
Given the above information, the method for stealing a token and assigning it is simple. Find the
_EPROCESS structure of the process we want to steal from, go to the
Token member offset, save the address that it is pointing to, and copy it to the corresponding
Token member of the process we want to elevate privileges with. This is the same process that Spotless performed in WinDbg.
In lieu of exploiting a kernel mode exploit, I write a simple test driver. The driver exposes an IOCTL that can be called from user mode. It takes struct that contains two members: an unsigned long for the PID of the process to steal a token from, and an unsigned long for the PID of the process to elevate.
The driver will find the
_EPROCESS structure for each PID, find the
Token members, and copies the target process token to the destination process.
The User Mode Program
The user mode program is a simple C++ CLI application that takes two PIDs as arguments, and copies the token of the first PID to the second PID, via the exposed driver IOCTL. This is done by first opening a handle to the driver by name with
CreateFileW and then calling
DeviceIoControl with the correct IOCTL.
The Driver Code
The code for the token copying is pretty straight forward. In the main function for handling IOCTLs,
HandleDeviceIoControl, we switch on the received IOCTL. When we receive
IOCTL_STEAL_TOKEN, we save the user mode buffer, extract the two PIDs, and attempt to resolve the PID of the target process to the address of its
Once we have the
_EPROCESS address, we can use the offset of
0x4b8 to find the
Token member address:
We repeat the process once more for the PID of the process to steal a token from, and now we have all the information we need. The last step is to copy the source token to the target process, like so:
The Whole Process
Here is a visual breakdown of the entire flow. First we create a command prompt and verify who we are:
Next we use the user mode program to pass the two PIDs to the driver. The first PID, 4, is the PID of the
System process, and is usually always 4. We see that the driver was accessed and the PIDs passed to it successfully:
In the debug output view, we can see that
HandleDeviceIoControl is called with the
IOCTL_STEAL_TOKEN IOCTL, the PIDs are processed, and the target token overwritten. Highlighted are the identical addresses of the two tokens after the copy, indicating that we have successfully assigned the token:
Finally we run
whoami again, and see that we are now SYSTEM!
We can even do the same thing with another user’s token:
Kernel mode is fun! If you’re on the offensive side of the house, it’s well worth digging into. After all, every user mode road leads to kernel space; knowing your way around can only make you a better operator, and it expands the attack surface available to you. Blue can benefit just as much, since knowing what you’re defending at a deep level will make you able to defend it more effectively. To dig deeper I highly recommend Pavel Yosifovich’s Windows Kernel Programming, the HackSys Extreme Vulnerable Driver, and of course the Windows Internals books.