Saturday, April 7, 2012

Allocating (nearly) arbitary kernel pool blocks using win32k!NtUserCreateAcceleratorTable

Hopefully the title of this post is fairly self-explanatory. Being able to allocate blocks of a controlled size in this way has obvious implications in exploiting almost any kind of bug related to the kernel pool.
This technique is nice because you can allocate multiple controlled block sizes with no collateral overhead, leading to a cleaner pool 'massaging' process.

The relavent code from win32k.sys looks a little bit like this:

HACCEL NtUserCreateAcceleratorTable(LPACCEL paccel,int cAccel)
{
    ...
    CreateAcceleratorTable(paccel, cAccel * 6);
    ...
}

HANDLE CreateAcceleratorTable(LPACCEL paccel, int cAccel)
{
    ...
    HMAllocObject(gptiCurrent, 0, 8, cAccel + 12);
    ...
}


HMAllocObject will allocate an object of (cAccel + 12) in the kernel pool via HeavyAllocPool and attach it to the current (calling) process by placing the handle in the current user handle table. Due to user handle limitiations, you can only allocate a maximum of 65535 (0xffff) buffers using this technique. Windows 7 imposes a limit of 32767 (0x7FFF) bytes per allocation.

Allocations can be freed by using the NtUserDestroyAcceleratorTable system call (or the DestroyAcceleratorTable API call).

The code I use in my exploits is shown below. I generally compile it as a separate object and link it into those exploits that need it.

#include <windows.h>

#pragma comment(lib,"user32.lib")

HANDLE PoolAllocViaAccelTable(DWORD count)
{
    DWORD d;
    HANDLE ret;
    char *buff;
   
    d = (count - 12);
   
    if(d % 6 != 0)
    {
        /* Cannot allocate this amount directly */
        return((HANDLE)-1);
    }
   
    buff = (char *) malloc(count + 8);
   
    if(buff == NULL)
    {
        return((HANDLE)-1);
    }
   
    memset(buff, 0x0, count);
   
    ret = CreateAcceleratorTable(buff, (d / 6));
   
    free(buff);
   
    return(ret);
}