Spraying arbitrary objects into the non-paged pool

Recently, I had some time to play around with HEVD [1], an extremly vulnerable Windows driver available for 32-bit and 64-bit systems.

Since exploits for all vulnerabilities of the 32-bit variant are publically available, I was wondering why this is not the case for the 64-bit version, especially for the pool corruption and UAF vulnerabilities.

After digging around a bit, it turned out that the reason is the following. HEVD uses a “special” sized object which is improperly handled such that a Use-After-Free vulnerability arises.

UseAfterFree.h [2]
typedef struct _USE_AFTER_FREE {
FunctionPointer Callback;
CHAR Buffer[0x54];

UseAfterFree.c [3]
// Allocate Pool chunk
UseAfterFree = (PUSE_AFTER_FREE)ExAllocatePoolWithTag(NonPagedPool, sizeof(USE_AFTER_FREE), (ULONG)POOL_TAG);

This “special” size is quite common in Windows non-paged pool spraying techniques due to the famous IoCompletionReserve object, which is 0x60 bytes large and easy to allocate in the non-paged pool using the NtAllocateReserveObject exposed by the windows API. This object was used quite often to de-randomize/massage/spray the kernel non-paged pool.

Let us come back to the HEVD object. The size of the object in 32-bit is 0x54+FunctionPointer Callback (4 byte) = 0x58 bytes which fits nicely
into the IoCompletionReserve object. However, on 64-bit we have an 8-byte FunctionPointer Callback, which expands the pure object size from 0x58 to 0x5C. In theory, this would still fit inside the IoCompletionReserve object, but due to 8-byte pointers the IoCompletionReserve object size has also changed to 0xC0.

Further, due to pool headers the total size of the allocated chunks is also different. On 32-bit the pool header is 0x8 bytes long and on 64-bit the header size is 0x10 bytes. This leaves the allocation of the HEVD object on 64-bit at a total chunk size of 0x70 bytes.

At this point, I had no idea of a creatable object to massage the non-paged pool to successfully exploit this Use-After-Free premise.

However, after digging a bit deeper, I found a little blogpost [4] wwhich describes the very same problem and pointed further to a blogpost [5] of Alex Ionescu. This blogpost described exactly what I needed. A generic way to spray the kernel pool using pipes.

Alex Ionescu also describes how to bypass SMEP and Kernel ASLR with this method. However, since I was working on a Windows 7 VM and had a UAF primitive this was not relevant to me.

Since booth blogposts released PoCs about allocating a single object of arbitrary size and not a whole spray/massage I decided to provide this and a working exploit for the HEVD 64-bit UAF bug.

The following python snippet displays the hole
filling process in the non-paged kernel pool [6]:

Furthermore, the next part displays growing the kernel pool with the same technique [7]:

The last step of the massage is to create holes for the UAF object [8]:

I hope this does help someone in the future when understanding and trying to exploit 64-bit Kernel drivers under Windows.
Btw this technique is still applicable on Windows 10 as the following screenshot displays (112 Byte allocation == 0x70):

The whole exploit triggering the UAF and exploiting it with a token stealing payload can be found in our github [9].

So long,

[1] –
[2] –
[3] –
[4] –
[5] –
[6] –
[7] –
[8] –
[9] –