Introduction

Welcome to the third part of Windows Kernel Exploitation series. In this part, we are going to exploit integer overflow in the HackSysExtremeVulnerableDriver.

What exactly is an integer overflow?

For those who do not know about integer overflows, you might be thinking how an integer can overflow?

Well, the actual integer does not overflow. CPU stores integers in fixed size memory allocations (we are not talking about heap or alike here). If you are familiar with C/C++ programming language or similar languages, you might recall data types and how each data type has specific fixed size.

On most machines and OSes, char is 1 byte and int is 4 bytes long. What that means is a char data type can hold values that are 8-bits in size, ranging from 0 to 255 or in case of signed values, -128 to 127. Same goes for integers, on machines where int is 4-bytes in size, it can hold values from 0 to 232 – 1 (in case of unsigned values).

Now, let us consider we are using an unsigned int whose largest value can be 232 – 1 or 0xFFFFFFFF. What happens when you add 1 to this? Since all the 32 bits are set to one, adding one will make it a 33-bit value but since the storage can hold only 32 bits, those 32 bits are set to 0.

When doing operations, CPU generally loads the number in a 32-bit register (talking about x86 here) and adding 1 will set Carry Flag and the register holds value 0 as all 32 bits are now 0.

Now, if there is a size check whether the value is greater than, let’s say 10, then the check will fail but if the size restriction was not there, then the comparison operation would return true.

To understand it in more detail, let us have a look the vulnerability and see how we can exploit integer overflow issue in HEVD to gain code execution in Windows Kernel.

Vulnerability

Now we have got it cleared, let us have a look at the vulnerable code (function TriggerIntegerOverflow located in IntegerOverflow.c).

Initially, the function creates an array of ULONGs which can hold 512 member elements (BufferSize is set to 512 in common.h header file).

Vulnerable function in IntegerOverflow.c

The kernel then checks if the buffer resides in user land and then it prints some information for us. Pretty helpful.

Once that has been done, the kernel then checks whether the size of the data (along with the size of Terminator, which is 4 bytes) is more than that of KernelBuffer. If it is, then it exits without copying the user-land buffer in kernel-land buffer.

Size checks

But, if that is not the case, then it goes ahead, and copies data to the kernel buffer.

Another thing to note here is that IF it encounters BufferTerminator in the user-land buffer, it stops copying and moves ahead. So, we need to put the BufferTerminator at the end of our user mode buffer.

Copying user-mode data to kernel-mode function stack

The Overflow

The problem in Line 100 of IntegerOverflow.c is that if we supply the size parameter as 0xFFFFFFFC and then it adds the size of BufferTerminator (which is 4 bytes), the effective size becomes – 0xFFFFFFFC + 4 = 0x00000000 which is less than the size of KernelBuffer and therefore, we pass the check of the data size and move to copying of the buffer to kernel mode.

Verifying the bug

Now, to verify this, we are going to send our buffer to the HEVD but passing 0xFFFFFFFC as the size of the buffer. For now, we will not place a huge buffer and crash the kernel, rather we will just send a small buffer and confirm.

PoC of triggering Integer Overflow

Since we know the buffer is of 512 ULONGs, we will just send this data and see what the kernel does.

Note: Here, the focus is on the 4th parameter of DeviceIoControl rather than on the actual data.

Finally, send this buffer to HEVD and see what happens.

Successfully triggered Integer Overflow

As you can see in the picture, the UserBuffer Size says 0xFFFFFFFC, but we still managed to bypass the size validity check and triggered integer overflow. 😊

We confirmed that by putting 0xFFFFFFFC, we can bypass the check size, now all it remains is to put a pattern (a unique pattern) after the UserBuffer and put the terminator after that to find saved return pointer overwrite.

If you do not know how to do that, please read Part 1 of this series where I have shown how to do this.

Let us move ahead and exploit it.

Exploiting the Overflow

All now remains is to use overwrite the saved return address with the TokenStealingPayloadWin7 shellcode provided in HEVD and you are done.

Note: You may need to modify the shellcode a bit to save it from crashing. This is your homework. 😉

Getting the shell

Let us first verify whether I am a regular user or not.

Regular User

As it can be seen, I am just a regular user.

After we run our exploit, I become nt authority/system.

Successful exploitation of Integer Overflow

That’s for this this part folks, see you in next part.

You can find whole code in my code repo here.

References