Windows Kernel Exploitation Tutorial Part 3: Arbitrary Memory Overwrite (Write-What-Where)

Overview

In the previous part, we looked into exploiting a basic kernel stack overflow 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).

Again, thanks to @hacksysteam for the driver and @FuzzySec for the awesome writeup on the subject.


Analysis

To analyze the vulnerability, let’s look into the ArbitraryOverwrite.c file in the source code.

Again, a really good job in explaining the vulnerability and the fix as well. The issue here is the lack of validation 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 part, we just looked into the IrpDeviceIoCtlHandler call for the IOCTL code. But this time, we’d look into the HackSysExtremeVulnerableDriver.h file 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:

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.

Working fine. Now let’s start building our exploit.

The first step to exploit this vulnerability is to find some address in kernel space to overwrite safely and reliably, without crashing the machine. Luckily, there’s a rarely used function in the kernel NtQueryIntervalProfile, that calls another function KeQueryIntervalProfile, 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 flow of the execution for the exploitation:

  1. Load the kernel executive ntkrnlpa.exe in userland in order to be able to get the offset of HalDispatchTable and then to deduce its address in kernelland.
  2. Retrieve the address of our shellcode.
  3. Retrieve the address of the syscall NtQueryIntervalProfile() within ntdll.dll.
  4. Overwrite the pointer at nt!HalDispatchTable+0x4 with the address of our shellcode function.
  5. Call the function NtQueryIntervalProfile() in order to launch the shellcode

Let’s analyze the flow to nt!HalDispatchTable+0x4 by disassembling the NtQueryIntervalProfile function:

Let’s go into the KeQueryIntervalProfile 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 NtQueryIntervalProfile, the execution flow 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Ā find the base name of the drivers through GetDeviceDriverBaseNameA function. And fetch the base name and address for ntkrnlpa.exe.

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.

Final step is to define our What-Where:

  • What –> Address to our shellcode
  • Where –> HalDispatchTable+0x4

Combining all of the above, with our shellcode taken from the previous part (the token stealing one), the final exploit looks like:

Run this, and enjoy a freshly brewed nt authority\system shell:

8 thoughts on “Windows Kernel Exploitation Tutorial Part 3: Arbitrary Memory Overwrite (Write-What-Where)

  1. First of all, I want to thank you for this awasome post. They are really helpful, and I’m learning a lot.
    But I didn’t get why did you sum 20 to the shell code address? (line 40 and 42).
    Thanks!

    1. id function in python returns the long int equivalent of the address of the variable. The address has certain space for the variable name, value.
      If you analyze it in a debugger, you’d see that a variable’s value (shellcode in this case) would always begin at an offset of 20 from the value returned by id.
      That’s why we do id(ptr) + 20 to directly call the address for our shellcode.

Leave a Reply

Your email address will not be published. Required fields are marked *