Windows Kernel Exploitation: Stack Overflow


This post is on exploiting a stack based buffer overflow in the HackSysExtremeVulnerableDriver.
There’s lot of background theory required to understand types of Windows drivers, developing drivers, debugging drivers, etc. I will only focus on developing the exploit while explaining some internal structures briefly. I would assume you have experience with assembly, C, debugging in the userland.
This driver is a kernel driver. A driver is typically used to get our code into the kernel. An unhandled exception will cause the famous BSOD. I will be using Windows 7 32-bit for this since it doesn’t support SMEP (Supervisor Mode Execution Prevention) or SMAP (Supervisor Mode Access Prevention). In simple words, I would say that when SMEP is enabled the CPU will generate a fault whenever the ring0 tries to execute code from a page marked with the user bit. Basically, due to this being not enabled, we can map our shellcode to steal the ‘System’ token. Check the Shellcode Analysis part for the analysis. Exploiting this vulnerability on a 64-bit system is different.
You can use the OSR Driver Loader to load the driver into the system.
If you want to debug the machine itself using windbg you can use VirtualKD or LiveKD

You can add a new serial connection using VirtualBox or VMware, so you can debug the guest system via windbg. I will be using a serial connection from VMware.
For kernel data structures refer to this. I have used it mostly to refer the structures.
After you have registered the driver you should see this in ‘msinfo32’.

If you check the loaded modules in the ‘System’ process you should see our kernel driver ‘HEVD.sys’.

In windbg you should see the debug output with the HEVD logo.

If you check the loaded modules HEVD should be visible.

The Vulnerability

The vulnerability lies in the ‘memcpy’ function. It’s well explained in the source.

Analyzing the Driver

For creating a handle to the driver we will use the ‘CreateFile’ API. To communicate with the driver from userland we use the ‘DeviceIoControl’ API. We have to specify the correct IOCTL code to trigger the Stack Overflow vulnerability. Windows uses I/O request packets (IRPs) to describe I/O requests to the kernel. IRP dispatch routines are stored in the ‘MajorFunction’ array. Windows has a pre-defined set of IRP major functions to describe each and every I/O request which comes from the userland. Whenever an I/O request comes for a driver from the userland the I/O manager calls the appropriate IRP major function handler. For example, some common dispatch routines would be, when ‘CreateFile’ is called the ‘IRP_MJ_CREATE’ IRP Major Code is used. When ‘DeviceIoControl’ is used ‘IRP_MJ_DEVICE_CONTROL’ IRP Major Code is used. In the DriverEntry of this driver, we can see the following.

To use the ‘DeviceIoControl’ we need to find the IOCTL code. We can do this by looking into source code since we have the source, or by reverse engineering the compiled driver. IOCTL means I/O Control Code. It’s a 32-bit integer that encodes the device type, operation-specific code, buffering method and security access. We use the CTL_CODE macro to define IOCTLS. To trigger the stack overflow vulnerability we have to use the ‘HACKSYS_EVD_IOCTL_STACK_OVERFLOW’ IOCTL code.

You can apply the above macro in the exploit or use IDA to locate the IOCTL code which jumps to the stack overflow routine located at the ‘IrpDeviceIoCtlHandler’ routine.

In windbg you can view the driver information for HEVD. At offset 0x38 you can see the ‘MajorFunction’ array.

To find the ‘IrpDeviceIoCtlHandler’ routine we can perform this pointer arithmetic. 0xe is the index of IRP_MJ_DEVICE_CONTROL. Once we unassembled the pointer we can see we found the correct routine.

We can analyze this routine in windbg to analyze further and let’s check where this 0x222003 IOCTL follows.

If we follow the jz instruction, it leads to the stack overflow routine which prints the debug message “****** HACKSYS_EVD_STACKOVERFLOW ******”

Triggering the Vulnerability

Since we know the IOCTL code let’s trigger the stack overflow vulnerability. I’m sending a huge buffer that would cause a BSOD.

This is the Python version.

Let’s change the buffer size to 0x900 and you can see we can see the EIP points to our buffer.

Developing an exploit for this driver is much similar to developing an exploit to a userland application. Now we have to find the offset where we overwrite the return address so that EIP will point to it. I’ll be using Mona to create a pattern of 0x900.

After we send this long buffer we can see that EIP contains the value 0x72433372 (r3Cr).

Let’s find the offset using Mona.

The offset is 2080. The offset to overwrite the EBP register would be 2080 – 4 = 2076. POP EBP, RET.

Shellcode Analysis

First of all, we save the state of all registers to avoid any BSODs. Next, we zero out the eax register and move the _KPCR.PcrbData.CurrentThread into eax. Let’s first explore the KPRC (Kernel Processor Control Region) structure. The KPCR contains per-CPU information which is shared by the kernel and the HAL (Hardware Abstraction Layer). This stores critical information about CPU state and information. This is located at the base address of the FS segment register at index 0 in 32-bit Windows systems, it’s [FS:0] and on 64-bit systems, it’s located in the GS segment register, [GS:0].
We can see at offset 0x120 it points to ‘PrcData’ which is of type KPRCB (Kernel Processor Control Block) structure. This structure contains information about the processor such as current running thread, next thread to run, type, model, speed, etc. Both these structures are undocumented.

If we explore the ‘PrcData’ _KPRCB structure we can find at offset 0x4 ‘CurrentThread’ which is of _KTHREAD (Kernel Thread) structure. This structure is embedded inside the ETHREAD structure. The ETHREAD structure is used by the Windows kernel to represent every thread in the system. This is represented by [FS:0x124].

Next _KTHREAD.ApcState.Process is fetched into EAX. Let’s explore the _KTHREAD structure. At offset 0x40 we can find ‘ApcState’ which is of _KAPC_STATE. The KAPC_STATE is used to save the list of APCs (Asynchronous Procedure Calls) queued to a thread when the thread attaches to another process.

If explore further more on _KAPC_STATE structure we can find a pointer to the current process structure at offset 0x10, ‘Process’ which is of _KPROCESS structure. The KPROCESS structure is embedded inside the EPROCESS structure and it contains scheduling related information like threads, quantum, priority and execution times. This is done in the shellcode as

I have observed the same method used in the ‘PsGetCurrentProcess’ function. This function uses the same instructions as this shellcode to get the current EPROCESS.

If we explore this structure, we can see at offset 0xb4 the ‘UniqueProcessId’ which has a value of 0x4 which means this is the PID of the ‘System’ process. At offset 0xb8 you can find ‘ActiveProcessLinks’ which is of _LIST_ENTRY data structure. At offset 0x16c ‘ImageFileName’ contains the value ‘System’.

The _LIST_ENTRY data structure is a double linked list. It’s head pointer is ‘Flink’ and the tail pointer is ‘Blink’. We can use ‘ActiveProcessLinks’ double linked list to traverse through the processes in the entire system and find the ‘System’ process. The _EPROCESS structure is also used in rootkits to hide processes to the userland. If you have done algorithms in C, it would be similar to removing a node from a double linked list. We simply change the Flink to the next node and the Blink to the previous node, leaving our process to be hidden away from the linked list. You might wonder how the process works. Processes are just a container of threads. The real deal is with the threads.

The following assembly code is used in the shellcode to traverse the double linked list and find the process ID of 0x4.

Once we find the ‘System’ process we replace our current process’s token with the token value of the ‘System’ process. The offset of ‘Token’ is at 0xf8. At the end we restore the state of the registers.

We can do this using our debugger on runtime. For example, I will open ‘notepad.exe’. You can see it’s running as a normal user.

Let’s check the pointer to the _EPROCESS structure of ‘notepad.exe’, its 853fed28.

The pointer to the _EPROCESS structure of ‘System’ is 8514a798.

Let’s check the value of the Token of the ‘System’ process. It’s 0x88e0124b.

We can calculate the value of the Token by unsetting the last 3 bits from 0x88e0124b. We can do this by performing bitwise AND by 0x3.
0x88e0124b &~ 3 = 0x88e01248

We can verify if our value is correct by the !process command.

After that we can enter the System token value into the Token offset at 0xf8 of the Notepad process.

Now if we check the Process Explorer we can see that Notepad.exe is running as ‘NT AUTHORITY/ SYSTEM’.

Final Exploit

We map the address of the shellcode function, so that EIP will jump to it and execute our shellcode. To make sure everything is correct we can analyze in the debugger. Let’s get the address of ‘lpInBuffer’.

At offset 2076 is the EBP overwrite and after that, it should contain the pointer to the shellcode function.

Let’s unassemble this pointer.

And yes, if everything is correct it should point to our shellcode function.
In python.

And w00t! Here’s the root shell ?

If we check the process you can see it’s running as NT AUTHORITY/SYSTEM.


7 thoughts on “Windows Kernel Exploitation: Stack Overflow

Leave a Reply