Part 1 Windows Kernel Exploitation Stack Overflow
Intro
This is my first time looking into Kernel Exploitation so i decided to practice on HEVD Driver by @hacksys. I’ve written this blog post to better understand the kernel space and upgrade my skills. Since this is my first time doing kernel exploitation, i’ll be going with the stack overflow. Our setup is:
- Windows 10 x64 bit
- HEVD 3.0
- Windbg Preview
- IDA
Looking into source code
Because i’m rookie on kernel exploitation, i want to look at the source code before reversing the driver.
NTSTATUS BufferOverflowStackIoctlHandler(
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IrpSp
)
{
SIZE_T Size = 0;
PVOID UserBuffer = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
UNREFERENCED_PARAMETER(Irp);
PAGED_CODE();
UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
Size = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (UserBuffer)
{
// If we manipulate the size of user buffer we'll trigger stackoverflow
Status = TriggerBufferOverflowStack(UserBuffer, Size);
}
return Status;
}
NTSTATUS
TriggerBufferOverflowStack(
_In_ PVOID UserBuffer,
_In_ SIZE_T Size
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG KernelBuffer[BUFFER_SIZE] = { 0 };
// ... other codes
DbgPrint("[+] Triggering Buffer Overflow in Stack\n");
// This function will copy the UserBuffer into the KernelBuffer without
// validating the size i.e., if the UserBuffer size is greater than
// size of the KernelBuffer and this will cause the overflow
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
}
From the Above code we can see that if the UserBuffer
is not null then it’ll call the function TriggerBufferOverflowStack(UserBuffer,Size)
. Inside this function there’s a insecure version of code, in there RtlCopyMemory
function copies the UserBuffer into the KernelBuffer without proper size check. This will help us on exploiting this vulnerability.
Disassembling the driver
There is the IrpDeviceIoCtlHandler() function which contains the jump table for each IOCTL code.
Inside the DriverEntry function we can see that IoCreateDevice()
is being called. Drivers and user-mode components access most system-defined objects through handles. To obtain a handle to a driver object, a driver needs to expose at least one device object which is done through IoCreateDevice.
//The IoCreateDevice routine creates a device object for use by a driver
//and saves a pointer to it in the DeviceObject variable
NTSTATUS IoCreateDevice(
PDRIVER_OBJECT DriverObject, //DriverObject
ULONG DeviceExtensionSize, //DeviceExtensionSize
PUNICODE_STRING DeviceName, //DeviceName
DEVICE_TYPE DeviceType, //DeviceType
ULONG DeviceCharacteristics, //DeviceCharacteristics
BOOLEAN Exclusive, //Exclusive
PDEVICE_OBJECT *DeviceObject //DeviceObject
);
Inside the DriverEntry we can see the DeviceName as \\Device\\HackSysExtremeVulnerableDriver
.
Let’s find BufferOverflowStackIoctlHandler
inside IrpDeviceIoCtlHandler
call. As we can see if the IOCTL is 0x222003h
, it’ll jumps to the code block where BufferOverflowStackIoctlHandler
function is present.
From the BufferOverflowStackIoctlHandler
function TriggerBufferOverflowStack
function will be called if the UserBuffer is not empty.
If we open up the TriggerBufferOverflowStack
function in IDA we can see the buffersize that KernelBuffer
accepts which is 2048
.
Now we know that the KernelBuffer accepts the buffer size of 2048
, and there is no buffer size checks while copying UserBuffer into KernelBuffer. So, to trigger the bug we need to send buffer with size greater than 2048
.
Talking to driver
Since this is the kernel exploitation, we need a way to talk to the kernel driver because we cannot directly touch the kernel mode objects and device driver directly from the user mode. To communicate from the userland, We need to have a handle to that particular device. We can simply create a handle to the driver object using CreateFileA
.
HANDLE CreateFileA(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
Then we can interact with the driver by sending IOCTL codes using DeviceIoControl()
Function. Each calls to the DeviceIoControl()
function causes I/O manager to create an I/O Request Packet (IRP) whose major function is IRP_MJ_DEVICE_CONTROL and sends it to the corresponding dispatch routine of the device driver.
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
Writing Exploit
Till now we know three important things to begin with, DeviceName (\\Device\\HackSysExtremeVulnerableDriver
), the length that the buffer accepts (0x800 or 2048
), and the IOCTL code (0x222003h
).
Triggering the crash