Uninitialised memory disclosures
TL;DR: A kernel routine allocates a buffer, fills only some fields, and copies the whole thing back to user mode — leftover heap or stack contents (often kernel pointers) leak across the boundary and hand the attacker a KASLR bypass.
What it is
Kernel components frequently allocate output buffers, populate part of them, and memcpy the result to user space via Irp->AssociatedIrp.SystemBuffer or directly through ProbeForWrite + raw user pointer. If padding bytes, unused struct fields, or alignment slots are never zero-initialised, the buffer carries whatever residual data the underlying pool/stack page held before reuse — typically prior kernel pointers, encryption keys, or path strings. j00ru’s long-running research found dozens of such bugs in win32k, NT executive, and third-party drivers.
Preconditions / where it applies
- IOCTL or NT-syscall that returns a struct with sized output (
OutputBufferLength≥ struct size) - Struct contains unused fields, alignment padding, union members, or trailing reserved bytes
- Allocator is not zero-on-free — kernel pool, stack, and contiguous physical allocations all qualify; ExAllocatePoolZero / RtlZeroMemory absent
Technique
Classic bug shape (simplified):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct {
ULONG Length;
ULONG Flags;
UCHAR Reserved[12]; // padding — never written
HANDLE TargetHandle;
} OUTPUT;
NTSTATUS Handler(PIRP Irp) {
OUTPUT* out = (OUTPUT*)Irp->AssociatedIrp.SystemBuffer;
out->Length = sizeof(*out);
out->Flags = 0;
out->TargetHandle = SomeHandle;
// Reserved[] left at whatever was in the pool block
Irp->IoStatus.Information = sizeof(*out);
return STATUS_SUCCESS;
}
Attacker steps:
- Repeatedly call the IOCTL, recording the
Reserved[]bytes per call - Identify recognisable pointers (kernel-VA range
0xFFFF8…) or known sentinel values - Cross-reference offsets with leaked function names to derive
ntoskrnlbase - Feed the leak into kaslr-bypass follow-up — token swap, ROP, etc.
Common sources beyond IOCTLs:
NtQuerySystemInformationinfo classes returning struct arrays- ALPC port message replies leaking server-side stack
IRP_MJ_QUERY_INFORMATIONpaths that return union members for a non-active type- Graphics drivers returning shader compilation telemetry
Tooling: Microsoft’s PageHeap + Special Pool surface uninitialised reads as STATUS_DATATYPE_MISALIGNMENT/poison page faults; KFuzz harnesses with KMSAN-style shadow memory catch them under fuzz.
Detection and defence
- Always allocate with
ExAllocatePool2(POOL_FLAG_NON_PAGED | POOL_FLAG_ZERO_INIT, ...)(Win 10 2004+) or explicitlyRtlZeroMemorybefore populating - Pack output structs with no padding (
__pragma(pack(1))) or zero-init reserved fields - Driver Verifier with
Force IRQL Checking+ Special Pool catches some leaks; combine with KAFL/WTF fuzzers - Code review: any kernel function that takes
OutputBufferLengthfrom user and writes a struct must zero-init or assert full coverage
References
- j00ru: Windows kernel memory disclosures — long catalogue with bug bounties
- Microsoft Pool Zero-Init guidance —
ExAllocatePool2API - bochspwn-reloaded — instrumentation for finding kernel uninit-read bugs
Related: kaslr-bypass, double-fetch, kernel-objects-and-irps, type-confusion-kernel