Windows Kernel Exploitation Tutorial Part 7: Uninitialized Heap Variable


In the previous part, we looked into an Uninitialized Stack Variable vulnerability. In this part, we’ll discuss about another vulnerability on similar lines, Uninitialized Heap Variable. We’d be grooming Paged Pool in this one, so as to direct our execution flow to the shellcode.

Again, huge thanks to @hacksysteam for the driver.


Let’s analyze the UninitializedHeapVariable.c file:

Big code, but simple enough to understand. The variable UninitializedHeapVariable is being initialized with the address of the pool chunk. And it’s all good if UserValue == MagicValue, the value and callback are properly initialized and the program is checking that before calling the callback. But what if this comparison fails? From the code, it is clear that if it’s compiled as the SECURE version, the UninitializedHeapVariable is being set to NULL, so the callback won’t be called in the if statement. Insecure version on the other hand, doesn’t have any checks like this, and makes the callback to an uninitialized variable, that leads to our vulnerability.

Also, let’s have a look at the defined _UNINITIALIZED_HEAP_VARIABLE structure in UninitializedHeapVariable.h file:

As we see here, it defines three members, out of which second one is the Callback, defined as a FunctionPointer. If we can somehow control the data on the Pool Chunk, we’d be able to control both the UninitializedHeapVariable and Callback.

All of this is more clear in the IDA screenshot:

Also, IOCTL for this would be 0x222033.


As usual, let’s start with our skeleton script, and with the correct Magic value:

Everything passes through with no crash whatsoever. Let’s give some other UserValue, and see what happens.

We get an exception, and the Callback address here doesn’t seem to be a valid one. Cool, now we can proceed on building our exploit for this.

The main challenge for us here is grooming the Paged Pool with our user controlled data from User Land. One of the interfaces that does it are the Named Objects, and if you remember from previous post about Pool Feng-Shui, we know that our CreateEvent object is the one we can use here to groom our Lookaside list:

Most important thing to note here is that even though the event object itself is allocated to Non-Paged Pool, the last parameter, lpName of type LPCTSTR is actually allocated on the Paged Pool. And we can actually define what it contains, and it’s length.
Some other points to be noted here:

  • We’d be grooming the Lookaside list, which are lazy activated only two minutes after the boot.
  • Maximum Blocksize for Lookaside list is 0x20,  and it only manages upto 256 chunks, after that, any additional chunks are managed by the ListHead.
  • We need to allocate 256 objects of same size and then freeing them. If the list is not populated, then the allocation would come from ListHead list.
  • We need to make sure that the string for the object name is random for each call to object constructor, as if same string is passed to consecutive calls to object constructor, then only one Pool chuck will be served for all further requests.
  • We also need to make sure that our lpName shouldn’t contain any NULL characters, as that would change the length of the lpName, and the exploit would fail.

We’d be giving lpName a size of 0xF0, the header size would be 0x8, total 0xF8 chunks. The shellcode we’d borrow from our previous tutorial.

Combining all the things above, our final exploit would look like:

And we get our nt authority\system shell:

7 thoughts on “Windows Kernel Exploitation Tutorial Part 7: Uninitialized Heap Variable

    1. I can’t say about what’s a wild-character??
      But I think the exploit is failing due to encoding issues.
      CreateEventA uses ANSI character encoding, while CreateEventW uses Unicode character encoding.

      1. oh I’m sorry. I confused wild-character with Unicode.
        I have wonder if I can exploit using CreateEventA function. So tried using CreateEventA function to exploit but it’s failed. As you said, maybe it’s related with encoding. I’ll try again. Thank you.

        1. I tested with CreateEventA function and I could see that the lpName parameter Ansi string get conveted to Unicode string.

          lpName = “A” * (0xF0 / 2)
          hEvent = kernel32.CreateEventA(None, True, False, c_char_p(lpName))

          In windbg debugger)
          kd> dd eax
          af7c5e18 00000000 00410041 00410041 00410041
          af7c5e28 00410041 00410041 00410041 00410041
          af7c5e38 00410041 00410041 00410041 00410041
          af7c5e48 00410041 00410041 00410041 00410041
          af7c5e58 00410041 00410041 00410041 00410041
          af7c5e68 00410041 00410041 00410041 00410041
          af7c5e78 00410041 00410041 00410041 00410041

          So, it seems that it will always failed if I use A function.
          Thank you for your article. It’s very helpful and waiting the next posting.

Leave a Reply

Your email address will not be published. Required fields are marked *