Edit Template

Understanding Arbitrary Access Primitives in Windows Kernel

In this blog post, we will explore some of the most powerful and commonly abused vulnerabilities in kernel-mode: arbitrary access primitives. From reading kernel memory and hijacking execution flows, to directly interacting with physical memory or model-specific registers (MSRs), each of these primitives opens doors to high-impact, post-exploitation techniques. Whether you’re writing an exploit, doing rootkit research, or reverse-engineering drivers, understanding these vulnerabilities is essential.

There are five key types of arbitrary access vulnerabilities we’ll explore in this series:
arbitrary read, arbitrary write, physical read, physical write, and MSR read—each offering unique capabilities for kernel exploitation and post-exploitation.

Arbitrary Read

Arbitrary read allows an attacker to read memory from any address in kernel space. By supplying a user-controlled pointer, the kernel will read from that location and return the data. This can be used to leak kernel base addresses, tokens, or function pointers. It’s typically the first step toward bypassing KASLR or escalating privileges.

To demonstrate this vulnerability, we created a custom vulnerable driver exposing an IOCTL_ARBITRARY_READ operation:

This is vulnerable because it blindly reads from a user-supplied kernel address (rw->Address) without validating it first. If the pointer is invalid or points to sensitive memory, it may leak kernel information or crash the system.

Exploiting Arbitrary Read Vulnerability

Using WinDbg’s eq command, we identify the base address of ntoskrnl.exe as fffff80425ea5000.

By passing this address to our arbitrary read PoC, we successfully leak the MZ header (0x905a4d), confirming a valid kernel memory read. This demonstrates the ability to leak kernel pointers—a crucial step for bypassing KASLR.

This leak proves that our vulnerability allows reading any memory in the kernel address space—a powerful primitive for further exploitation.

Bonus Tip

Arbitrary read vulnerabilities often involve functions like RtlCopyMemory, memcpy, or memmove, where a driver copies data from a user-supplied kernel address without validation. Safer APIs like MmCopyMemory exist but are rarely misused. The root cause is usually the absence of checks like ProbeForRead or MmIsAddressValid, allowing attackers to read sensitive kernel memory. These bugs typically surface in DeviceIoControl handlers that directly trust user input like rw->Address.

Arbitrary Write

Arbitrary write lets a userland attacker overwrite any memory address in kernel space. It is often used to hijack execution, such as overwriting function pointers or token privileges. If the attacker knows what to write and where, they can gain full system access. Combined with read, it’s a devastating primitive for kernel exploitation.

To demonstrate this vulnerability, we created a custom vulnerable driver exposing an IOCTL_ARBITRARY_WRITE operation:

This IOCTL handler implements an arbitrary write vulnerability by directly writing a user-supplied value (rw->Value) to a user-specified kernel address (rw->Address) without validating the pointer. The lack of access checks allows attackers to overwrite sensitive kernel structures, potentially leading to privilege escalation or system instability.

Exploiting the Arbitrary Write Vulnerability  

To demonstrate the power of an arbitrary write vulnerability, we used WinDbg to locate the second entry in the HalDispatchTable.

For the purpose of the demo, we are taking HalDispatchTable. You can take any desired address where you want to write it.

Now, we will run our PoC to perform an arbitrary write. We’ll attempt to write the value 0x4141414 to the target kernel address fffff804262cd254. After executing the PoC, we can confirm that the value at this address has been successfully overwritten.

MSR Read

MSR (model-specific register) read vulnerabilities expose critical CPU-level settings. By using a vulnerable driver that allows arbitrary RDMSR calls, attackers can extract values like IA32_LSTAR (which stores the kernel’s syscall entry point). This breaks KASLR and can bypass syscall hooking mechanisms, making it a powerful primitive in both EDR evasion and advanced kernel exploitation.

This driver uses __readmsr(msr->MsrId) to read from a Model-Specific Register (MSR) based on a user-supplied ID and returns the result to user mode via msr->Value. MSRs store critical CPU configuration data, including pointers to kernel functions. Without validating the MSR ID, this gives attackers access to privileged information. Registers like IA32_LSTAR or IA32_SYSENTER_EIP can reveal kernel base addresses, enabling KASLR bypass.

Exploiting MSR Read Vulnerability

In this this PoC, we demonstrate a classic exploitation technique by leaking the value of the IA32_LSTAR MSR (Model-Specific Register) located at 0xC0000082. This register holds the address of the kernel’s SYSCALL entry point, typically pointing to the nt!KiSystemCall64 function within ntoskrnl.exe. By reading its value from user mode via an MSR read vulnerability, we effectively bypass Kernel Address Space Layout Randomization (KASLR), a crucial Windows security mechanism.

To confirm the leak in WinDbg, use !address 0xfffff804261f8180 and ensure the leaked address falls within the memory range of ntoskrnl.exe.

Physical Read & Physical Write

Physical memory access vulnerabilities let attackers bypass virtual memory protections to read or write raw RAM directly. With physical read, one can inspect memory-mapped devices, firmware, or hidden kernel structures—useful for uncovering secrets or debugging hardware-level code. Physical write is even more potent, allowing direct tampering with hardware registers or kernel memory, potentially disabling security features or planting persistent backdoors. While dangerous and often system-crashing if misused, in expert hands, these primitives are essential tools in advanced kernel exploitation, rootkit development, and hypervisor-level research.

These handlers allow user-mode input to map arbitrary physical memory addresses using MmMapIoSpace() without validating the rw->Address field. In both read and write cases, the driver maps and accesses the physical memory directly using the user-provided address.

This is vulnerable because attackers can specify sensitive or protected physical addresses and read secrets (e.g., kernel code, credentials) or write malicious values (e.g., patching kernel code, disabling protections).

Vulnerable MSR IOCTL Handler in AMDPowerProfiler.sys

Today we’re diving into a real-world example of a vulnerable kernel driver—AMDPowerProfiler.sys. This driver exposes unsafe access to Model-Specific Registers (MSRs) via an IOCTL handler. By accepting a user-controlled pointer without validation, it gives attackers powerful read/write primitives to sensitive CPU registers directly from user mode.

a1 is a pointer passed from user mode, likely via DeviceIoControl, but it’s never validated (e.g., no ProbeForRead, ProbeForWrite, or try/except). If the first byte at a1 is non-zero (*(_BYTE *)a1), the driver calls __readmsr on the user-supplied MSR ID at a1 + 8 and writes the result to a1 + 16. Otherwise, it calls __writemsr with both the MSR ID and value directly from user memory. This provides full arbitrary MSR read/write access from user mode, bypassing privilege checks.

Vulnerable Physical Memory Mapping in DirectIO64.sys

Let’s look at another dangerous real-world kernel vulnerability involving direct mapping of physical memory using ZwMapViewOfSection. Found in the DirectIO64.sys driver, this code gives user-mode processes access to \Device\PhysicalMemory, allowing attackers to directly view or manipulate kernel memory, completely bypassing the Windows virtual memory protection model.

The driver opens the \Device\PhysicalMemory section using ZwOpenSection, a legacy NT API that provides access to the entire physical memory space of the system. It then references this handle via ObReferenceObjectByHandle and maps the physical memory into the calling process’s address space using ZwMapViewOfSection. Critical parameters such as SectionOffset, ViewSize, and the target pointer at a1 + 36 are directly controlled by user-mode input, without any validation, bounds checking, or access control. The access rights passed to ZwOpenSection and ObReferenceObjectByHandle are determined by the value at a1 + 44, which can toggle between read (SECTION_MAP_READ) or read/write (SECTION_MAP_WRITE), effectively giving full control over mapping permissions. The ZwMapViewOfSection call is made with ViewShare and memory protection flags (516 for RW, 514 for RO) directly based on user-controlled input, meaning a user-mode process can map and write to any region of physical memory if a1 + 44 != 0.

Conclusion

In this post, we broke down five critical arbitrary access primitives—Arbitrary Read, Arbitrary Write, Physical Read, Physical Write, and MSR Read—commonly found in vulnerable Windows drivers. We looked at how each primitive works under the hood, real-world vulnerable code patterns, and how attackers can abuse them to bypass protections, leak secrets, or fully compromise the kernel. Understanding these vulnerabilities is crucial for anyone doing kernel exploitation, driver auditing, or low-level security research.

References

  1. Rdmsr:- https://www.felixcloutier.com/x86/rdmsr
  2. Wemsr:- https://www.felixcloutier.com/x86/wrmsr

Edit Template