
Use CVE-2016-3308 corrupt win32k desktop heap
author : @55-AA, Sept 18, 2016
##Introduction
Desktop heap is a kernel pool used by win32k, it can be exploited by user-mode application. Here I will describe in detail how to implement a reliable exploitation so that to read/write arbitrary address in kernel. This writeup and associated analysis are done on a win7_sp1_x86(build 17842) installation.
##Vulnerability
On August 9, 2016, Microsoft released MS16-098. The vulnerability code exists within the function win32k!xxxInsertMenuItem, the function prototype is :
BOOL xxxInsertMenuItem(
PMENU pMenu,
UINT wIndex,
BOOL fByPosition,
LPMENUITEMINFOW lpmii,
PUNICODE_STRING pstrItem
);
First let's look at the pseudo bug code in the xxxInsertMenuItem :
if (pMenu->cItems >= pMenu->cAlloced) {
if (pMenu->rgItems) {
pNewItems = (PITEM)DesktopAlloc(
pMenu->head.rpdesk,
(pMenu->cAlloced + CMENUITEMALLOC) * sizeof(ITEM),
DTAG_MENUITEM);
......
pMenu->cAlloced += CMENUITEMALLOC;
pMenu->rgItems = pNewItems;
if (wIndex != MFMWFP_NOITEM)
pItem = MNLookUpItem(pMenu, wIndex, fByPosition, &pMenuItemIsOn);
......
pMenu->cItems++;
if (pItem != NULL) {
RtlMoveMemory(pItem + 1, pItem, (pMenu->cItems - 1) *
sizeof(ITEM) - ((char *)pItem - (char *)pMenu->rgItems));
} else {
In the code above, When the 9th(from 1st) item was added into the pMenu, DesktopAlloc() was called to re-allocate a new pMenu->rgItems. Then MNLookUpItem() was called to get the item’s location in the pMenu->rgItems. But the returned pItem by MNLookUpItem() is a rgItems of another pSubMenu, instead of the pMenu, so when the RtlMoveMemory() was called, the pItem of pSubMenu and followed bytes would be overwrote due to the mistaken size of moving.
The following is the disassembly code about the bug, which would trigger a heap overwrite, it can be leveraged to build a fake trunk:
0: kd> u win32k!xxxInsertMenuItem+0x1f5 l8
win32k!xxxInsertMenuItem+0x1f5:
95d295af 6bc06c imul eax,eax,6Ch
95d295b2 2bc3 sub eax,ebx
95d295b4 034634 add eax,dword ptr [esi+34h]
95d295b7 50 push eax
95d295b8 8d436c lea eax,[ebx+6Ch]
95d295bb 53 push ebx
95d295bc 50 push eax
95d295bd e85ea40100 call win32k!memmove (95d43a20)
To track the bug, I use these breakpoints in WinDbg :
ba e1 win32k!xxxInsertMenuItem
ba e1 win32k!xxxInsertMenuItem+0xf3
95d294e3 e843e70200 call win32k!DesktopAlloc (836d7bf5)
ba e1 win32k!xxxInsertMenuItem+0x129
95d294e3 e80de70200 call win32k!DesktopAlloc (836d7bf5)
ba e1 win32k!xxxInsertMenuItem+0x1f5
95d295af 6bc06c imul eax,eax,6Ch
In order to trigger the bug, the following stages need be done:
Desktop heap is a global pool used by all GUI process. All GUI objects, such as Window, Menu, are stored in the desktop heap, and are managed by the kernel heap allocator. The kernel heap allocator uses familiar functions such as RtlAllocateHeap and RtlFreeHeap. Unlike the user-mode heap, desktop heap do not employ any front-end allocators, so no Low Fragmentation Heap, no Lookaside list, etc. Also there is not Heap Encoding until Windows 8 and later. The following is the structure of trunk on win7_sp1_x86:
typedef struct _HEAP_ENTRY {
USHORT Size;
UCHAR Flags;
UCHAR SegmentIndex;
USHORT PreviousSize;
UCHAR SegmentOffset;
UCHAR UnusedBytes;
} HEAP_ENTRY, *PHEAP_ENTRY;
The Size and PreviousSize fields represent the chunks size right-thifted HEAP_GRANULARITY_SHIFT(defined as 3 in 32-bit system) bits, the Size field specifies the current chunk, and the PreviousSize specifies the front one. The lowest bit of the Flags is usually set to HEAP_ENTRY_BUSY(0x01), represent the chunk is in use, if not to 0x00.
The following figure demonstrates the relationship between these fields and these chunk block. The second green underline WORD (0x000f) represents that the current chunk size is 0x78 bytes, The second black underline WORD (0x0003) represents that the front chunk size is 0x18 bytes, and The red underline WORDs (0x0001) represent that the current chunks are in use. Here, the chunk size include header size, the header is defined as HEAP_ENTRY structure above.

It is the most important feature for heap corruption, which heap allocator always get the chunk released recently. This means that we can actually allocate a chunk with any size and at certain location we want to.
By leveraging the bug, I can overwrite some bytes in desktop heap, thus I'll get a fake chunk, that replace a normal chunk, then I release the replaced chunk, so that the fake chunk was pushed to the top of the free chunk list. subsequently the fake chunk was reused, I can write any bytes in it, the write-able region overlap several normal chunks, but it cannot cover whole kernel-land. So I need to build another R/W primitive in the overlapped region, that take advantage of the tagWND.strName to write arbitrary address. The pointer of strName.Buffer can led us to anywhere including kernelland and userland. Of course, our target is only the nt!HalDispatchTable.
The following figure shows the procedure of heap changing:

According to the demonstrates, I corrupt the desktop heap step by step, and implement the exploitation by the following stages: