NULL Pointer Dereferences should have died few years ago but they are still being found and used in malware attacks. This post explores the internal details of CVE-2019-1132, which was used by Buhtrap group to target victims in Eastern Europe.


The vulnerability we are discussing in this post, NULL pointer dereference, resides in win32k.sys driver which leads to successful escalation of privileges (EoP) on Windows 7 and Windows Server 2008 OSes.

Microsoft addressed this vulnerability in July patch and the vulnerability was discussed previously by ESET in their blog as this vulnerability was used in targeted attacks in Eastern Europe.

This article focuses on analyzing the vulnerability and creating a working exploit on Windows 7 x86 environment with June patch installed.

Vulnerability Overview

The vulnerability resides in win32k!xxxMNOpenHierarchy function where the function does not check whether the pointer pointed by tagPOPUPMENU->ppopupmenuRoot is NULL or not.

As this field is accessed in various operations, if the attacker is able to set this field to NULL, it can lead to NULL Pointer Dereference.

To exploit this vulnerability, the attacker needs to map the NULL Page in specific way (crafts fake objects on the NULL Page), then it leads to successful EoP.

To set ppopupmenuRoot to NULL, we free the root popupmenu object pointed by this field. After that, we open a sub-menu (previously created) by root popupmenu, the sub-menu calls win32k!xxxMNOpenHierarchy in kernel-mode which creates a second sub-menu. At the creation of the second popupmenu, the ppopupmenuRoot field of the sub-menu of the root menu will contain NULL. When win32k!HMAssignmentLock function tries to access this field, a NULL Pointer Dereference operation is performed, leading to BSoD.

Triggering the vulnerability

To trigger the vulnerability, we used the approach described in the ESET blog. This can be summarized as:

  • We first create a window and 3 menu objects which we then append menu items.

Use TrackPopupMenuEx function to display the root popup menu. When TrackPopupMenuEx is called, it calls win32k!xxxTrackPopupMenuEx function to display the menu. After that it notifies the user via event of type EVENT_SYSTEM_MENUPOPUPSTART.

  • This triggers our event hook function xxWindowEventProc, where we store window handle of the menu objects each time it enters the function. By sending the MN_OPENHIERARCHY message, it ends up calling the function win32k!xxxMNOpenHierarchy.
  • When the function win32k!xxxMNOpenHierarchy is called, it calls the win32k!xxxCreateWindowEx function to create another popupmenu object. During the call to win32k!xxxCreateWindowEx function, WM_NCCREATE message is sent to the user, which we can catch in our WH_CALLWNDPROC hook function i.e. xxWindowHookProc.
  • Inside xxWindowHookProc function, we check whether the rootpopup menu object is created or not by checking the window handle of the root menu object and verify whether the next popup menu object window handle is NULL. We also verify whether the message is WM_NCCREATE or not.

Once all the above steps are taken, we send WM_CANCELMENUS to the root popupmenu object.

It ends up calling win32k!xxxMNCancel and sets the fDestroyed bit of the root popupmenu. It then calls win32k!xxxMNCloseHierarchy to close the sub-menus in the stack of the root popupmenu object.

Since the sub-menu has not yet been created, the function win32k!xxxMNCloseHierarchy skips the child menu object and does not set fDestroyed bit, destroying the root popupmenu object while the sub-menu is still present.

But now the tagPOPUPMENU->ppopupmenuRoot is set to NULL because the root popup menu of this sub-menu is destroyed, as can be seen in the screenshot.

ppopupmenu set to NULL

Exploiting the vulnerability

At this point, the ppopupmenuRoot is pointing to the NULL. In order to trigger the memory access from the NULL page, we send the MN_BUTTONDOWN message to sub-menu object. We initially tried to trigger the vulnerability using the approach suggested by ESET but failed to call win32k!xxxMNOpenHierarchy function by sending MN_BUTTONDOWN message.

There is another way to call win32k!xxxMNOpenHierarchy function via TrackPopupMenuEx with the sub-menu as root. So, we use TrackPopupMenuEx to call win32k!xxxMNOpenHierarchy function and it ends up accessing the NULL page.

Accessing the NULL Page

Here we see that the location 0x0000001c is getting accessed which points to the tagWND object of the freed root popup menu object. This address is then sent to win32k!HMAssignmentLock function.

But in ESET blog, they mentioned that the bServerSideWindowProc bit is set in the function win32k!HMDestroyedUnlockedObject. But again, after trying for a long time we failed in setting the bit of the attacking window.

So we used the decrement of the clockObj instruction to set the bServerSideWindowProc bit.

Let’s see the exploitation part step by step:

  • First we create another window which acts as an attacking window.
  • Then we allocate the memory at NULL page using the NtAllocateVirtualMemory.
  • Now we leak the address of the tagWND object of attacking window using the HMValidateHandle function technique.
  • Now we craft the fake popupmenu object at the NULL page to successfully satisfy the conditions that are needed to set the bServerSideWindowProc bit of the attacking window.

The address accessed by win32k!HMAssignmentLock function (0x0000001c) is pointing to the spwndActivePopup of our fake popupmenu object. Now we set spwndActivePopup field of our fake popup menu object to points towards address of tagWND + 0x12.

This is because the instruction to decrement the clockObj decrements the value at [eax + 4] and our bServerSideWindowProc bit is 18th bit in tagWND object. To set the bit, the (eax + 4) must point to the tagWND object + 0x16.

  • Now we access the field mapped at NULL page and verify that the bServerSideWindowProc bit of our attacking window is set or not.
Setting bServerSideWindowProc bit
  • At this point, bServerSideWindowProc bit is set. Now we can send the message to our attacking window that will be handled by xxMainWindowProc. Here it checks for the cs register. If cs register is equal to 0x1b then we are still in user mode, so our exploit fails otherwise we call our shellcode.

Once the shellcode is executed, we see our favorite:


The exploit code has been tested on Windows 7 x86, with June patch installed and can be accessed on my GitHub repo – here.

Recommended Reading

If you are new to Windows Kernel Exploitation, reading this article would have proved to be confusing. We recommend these articles as well so as to better understand this article as the authors relied knowledge obtained from the following articles to create this exploit.