Edit Template

Understanding Out-Of-Bounds in Windows Kernel Driver

In this blog post, we will explore different types of out-of-bounds (OOB) vulnerabilities in Windows kernel drivers. Our objective is to demonstrate how simple mistakes in memory management, structure handling, and loops can introduce serious OOB vulnerabilities in custom kernel drivers. We will write realistic, purpose-built kernel drivers containing intentional vulnerabilities to showcase OOB reads, OOB writes, and loop-based overflow.

What is Out-of-Bounds?

Out-of-bounds (OOB) occurs when a program accesses memory outside the boundaries of a valid buffer or data structure. This can lead to information leaks, memory corruption, or privilege escalation in kernel-mode drivers. OOB bugs typically arise from incorrect index calculations, pointer mismanagement, or faulty loop conditions.

Out-of-Bounds Read Custom Network Driver

An out-of-bounds read (OOBR) happens when a program reads past the end of a buffer due to invalid size assumptions. In kernel mode, it can leak uninitialized memory or sensitive kernel pointers, bypassing mitigations like KASLR. Common causes include mismatched struct sizes, missing length checks, and bad loop bounds. These flaws can sometimes escalate into arbitrary read/write vulnerabilities.

We made a custom driver that takes a user-supplied structure containing SourcePort and DestPort, where DestPort is (mis)used as an offset into memory.

The DestPort field in the structure is user-controlled and treated as a raw offset. In the vulnerable code line, DestPort is directly added to the base pointer hdr without any validation or bounds checking. If an attacker supplies a large DestPort value, leakPtr will point outside the intended buffer. This results in an out-of-bounds memory access, which can leak sensitive kernel memory or cause a crash. The flaw stems from trusting unvalidated user input as a pointer offset.

PoC: Out-of-Bounds Kernel Memory Leak via DestPort Offset

This PoC opens a handle to the vulnerable device and crafts a fake TCP header structure. It repeatedly sets various large values to DestPort, which is used as an offset for an out-of-bounds read in the driver. By sending these crafted packets with DeviceIoControl, the PoC attempts to leak kernel memory.

The loop iterates through multiple DestPort values (offsets) from 0x80 to 0x1000 in steps of 0x80. This increases coverage to trigger out-of-bounds reads at various memory locations.

PoC Output: Memory Leak Results in dbgview

This output shows leaked 8-byte (QWORD) values from different memory offsets specified via DestPort. Some offsets reveal kernel pointers (e.g., 0xFFFF860D748E70A0), indicating successful out-of-bounds reads, while others return zero or unrelated data. This confirms arbitrary memory disclosure.

This !pool output shows the leaked address belongs to a non-paged pool allocation tagged AlIn, which is used for ALPC (Advanced Local Procedure Call) internal structures in the Windows kernel. The memory region is actively allocated and contains sensitive kernel data, confirming the seriousness of the out-of-bounds leak.

Out-of-Bounds Write Custom Driver

Out-of-bounds (OOB) write occurs when a program writes data past the end (or before the start) of a valid memory buffer. It corrupts adjacent memory regions, leading to crashes, data corruption, or potential code execution. This driver demonstrates a simple out-of-bounds (OOB) write vulnerability in kernel mode.

The driver defines a fixed-size array targetBuffer[10] (10 bytes). It blindly copies length bytes from a user-supplied buffer without checking if length > 10. If the user sends more than 10 bytes, RtlCopyMemory writes past the end of the array. This causes an out-of-bounds write, corrupting adjacent kernel memory and leading to crashes or exploitation

PoC — Triggering Out-of-Bounds Write via IOCTL

The PoC opens a handle to the vulnerable driver using CreateFileW targeting the OobArrayDevice. It constructs a malicious input buffer of 100 bytes, filling it entirely with the byte 0x41 (ASCII ‘A’).

This results in an out-of-bounds write of 90 extra bytes, overwriting adjacent kernel stack memory with 0x41 patterns — leading to memory corruption (and likely will result in a crash).

WinDbg Analysis—Confirming Out-of-Bounds Write in Kernel Stack

After triggering the vulnerability, the driver printed the kernel address of targetBuffer: targetBuffer @ FFFFFE0378E377F0. In WinDbg, the dq command (dq FFFFFE0378E377F0) dumps 64-bit quadwords starting from the buffer address.

We clearly observe repeated 0x41414141 patterns (ASCII ‘A’) overflowing well beyond the 10-byte array—confirming an out-of-bounds write onto adjacent kernel stack memory. This explains why a blue screen of death (BSOD) or kernel memory corruption occurs; we’ve clobbered critical stack data following the array.

Mimicking a Vulnerability Inspired by RTKVHD64.sys

In this post, we demonstrate a simplified but realistic mimic of a vulnerability pattern observed in Realtek’s RTKVHD64.sys audio driver. While the original vulnerability details and reverse engineering insights will be covered in later posts, here we focus on replicating a kernel out-of-bounds arbitrary write bug within a custom Windows driver.

Our vulnerable driver (VulnAudioDriver.sys) exposes a controlled IOCTL interface, closely resembling patterns seen in RTKVHD64.sys handling of kernel structures and user-controlled indices.

The MEVT structure contains an array of 63 EVT objects. Without a bounds check, an attacker can specify input_index >= 63, leading to memory accesses beyond the array, corrupting adjacent kernel memory. Such unchecked indexed access patterns, especially when manipulating kernel object fields, can lead to privilege escalation, kernel memory corruption, or even arbitrary kernel writes.

This code directly casts the user-supplied input_index into an array access (gMevt->array[input_index]) without validating bounds, enabling out-of-bounds memory access. The attacker controls both the spinlock pointer (KSPIN_LOCK* spinlock) and the event pointer (PVOID* event_ptr), allowing manipulation of arbitrary kernel addresses. When KeAcquireSpinLock(spinlock, &oldIrql) is called on a user-controlled pointer, it can trigger memory corruption or cause a controlled kernel crash.

[Note: The following line is added purely for blog demonstration purposes and is not part of the original vulnerable code.]
*event_ptr = userInput->value_to_write; demonstrates how an attacker-controlled value can be written to an arbitrary kernel memory location, showcasing a powerful arbitrary write primitive for exploitation or crashing.

Proof-of-Concept (PoC) Trigger for VulnAudioDriver Arbitrary Write

This simple user-mode PoC crafts a malicious USER_INPUT structure with an out-of-bounds index (input_index = 10000) and sends it to the vulnerable driver via DeviceIoControl. The attacker controls both the memory location and the value to be written.

Setting input_index = 10000 deliberately exceeds the valid bounds of the array[63], which only permits indices 0–62. This triggers an out-of-bounds access, letting the attacker compute a pointer far outside the intended object. As a result, the attacker gains an arbitrary kernel memory write primitive by overwriting unintended kernel memory. By exploiting the unchecked array access, the driver writes to arbitrary kernel memory, leading to corruption.

When executed, this reliably causes a blue screen of death (BSOD) due to illegal memory access, demonstrating the severity of the arbitrary write primitive.

Conclusion

To identify out-of-bounds read and write vulnerabilities in kernel drivers, focus on patterns where user-controlled indices are used to access fixed-size arrays without proper bounds validation. Carefully examine IOCTL handlers and input structures where fields like input_index directly influence array offsets or pointer dereferences. Unchecked indexed access into kernel structures—especially arrays of objects or pointers—can lead to memory corruption if user-supplied indices exceed the allocated range. Pay close attention when the indexed elements are pointers, function tables, or kernel object fields, as these can escalate simple OOB bugs into powerful arbitrary read or write primitives. Always trace how user inputs flow into array accesses and check if validation (e.g., index < array_size) is missing or insufficient.

Reference

RTKVHD64.sys: https://hackyboiz.github.io/2021/06/29/l0ch/2021-06-29/

Edit Template