CVE-2021-40449 Win32k提权漏洞及POC分析
字数 1509 2025-08-29 08:31:41
CVE-2021-40449 Win32k提权漏洞分析与利用
漏洞概述
CVE-2021-40449是卡巴斯基实验室在2021年8月下旬到9月上旬发现的Windows提权漏洞,存在于win32kfull.sys驱动中。攻击者可以利用该漏洞在Windows系统中完成从普通用户权限(User)到系统权限(System)的权限提升。
基本概念
内核对象
- 内核对象存在于内核空间,只能由内核分配和访问
- 应用程序通过操作系统提供的API间接操作内核对象
引用计数
- 内核对象创建时引用计数为1
- 调用CloseHandle()时引用计数减1
- 引用计数为0时对象被释放
- 类似于Java GC的引用计数法
句柄(Handle)
- 应用程序操作内核对象的间接标识符
- 内核对象创建后,操作系统返回句柄给应用程序
- 应用程序通过API操作句柄,内核将句柄映射到实际的内核对象
句柄表
- 进程初始化时分配句柄表
- 创建内核对象时,内核在句柄表中设置对象指针
- 返回句柄作为索引
DC(Device Context)
- 内核对象,全称设备上下文对象
- 用于图形设备接口(GDI)操作
HDC
- DC对象的句柄
释放后重用(Use-After-Free)
- 内存被释放后变为空闲状态
- 如果立即申请内存,可能分配到刚释放的内存
- 如果释放前有指针指向该内存,释放后指针未置空
- 后续使用该指针可能导致非预期行为
漏洞分析
漏洞位置
漏洞存在于win32kfull!GreResetDCInternal函数中,该函数会获取DC对象内的函数指针并执行,但未检查DC对象是否有效。
漏洞原理
- 函数获取DC对象中的函数指针并执行
- 未检查DC对象是否已被释放
- 攻击者可以在调用函数指针前释放DC对象
- 重新申请内存并构造恶意函数指针
- 导致任意内核函数执行
关键代码
v10 = *(_QWORD *)(v8 + 48); // 获取DC对象指针
v15 = *(void (__fastcall **)(_QWORD, _QWORD))(*v10 + 2768); // 获取函数指针
if (v15)
v15(*(_QWORD *)(v10 + 1824), *(_QWORD *)(v14[6] + 1824i64)); // 调用函数指针
漏洞触发流程
- 调用
ResetDC函数进入内核 - 内核调用
NtGdiResetDC和GreResetDCInternal GreResetDCInternal调用hdcOpenDCWhdcOpenDCW执行用户模式回调函数- 在回调函数中再次调用
ResetDC - 第二次调用
GreResetDCInternal时释放DC对象 - 重新申请内存并构造恶意函数指针
- 第一次调用继续执行被篡改的函数指针
漏洞利用
利用条件
- 释放DC对象
- 重新申请原DC对象的内存空间
- 构造内存布局,修改函数指针
利用步骤
-
第一次调用
ResetDC:- 创建DC对象和DCO对象
- 获取DC对象中的函数指针
-
在
hdcOpenDCW回调中:- 第二次调用
ResetDC - 释放原始DC对象
- 第二次调用
-
内存布局:
- 使用
CreatePalette申请释放的内存 - 精确构造内存覆盖函数指针
- 使用
-
执行:
- 第一次调用继续执行被篡改的函数指针
- 实现任意内核函数调用
关键函数调用链
ResetDC (用户态)
-> NtGdiResetDC (内核)
-> GreResetDCInternal (漏洞函数)
-> hdcOpenDCW
-> 用户模式回调
-> ResetDC (第二次调用)
-> NtGdiResetDC
-> GreResetDCInternal
-> 释放DC对象
-> 调用DC对象函数指针 (此时已被篡改)
补丁分析
补丁增加了对DC对象引用计数的检查:
if (*(_WORD *)(v30 + 6) > 1u) {
// 引用计数异常处理
EngSetLastError(6);
// ...
}
补丁逻辑:
- 正常情况下DC对象引用计数不应大于1
- 如果发现引用计数异常,抛出错误
- 防止在回调中重复调用导致的UAF
调试分析
关键断点
win32kfull!NtGdiResetDC
win32kfull!NtGdiResetDC+0xc1 (调用GreResetDCInternal)
win32kfull!GreResetDCInternal+0x3a (调用DCOBJ构造函数)
win32kfull!GreResetDCInternal+0x116 (调用hdcOpenDCW)
win32kfull!GreResetDCInternal+0x136 (第二次DCOBJ)
win32kfull!GreResetDCInternal+0x1b5 (调用DC对象函数指针)
win32kfull!GreResetDCInternal+0x1d1 (调用HmgSwapLockedHandle)
win32kfull!GreResetDCInternal+0x20d (调用bDeleteDCInternal)
内存布局计算
函数指针 = ((DCO + 0x30) + 0xAD0)
其中DCO + 0x30指向DC对象
内存申请
使用CreatePalette申请释放的DC对象内存:
- DC对象大小通常为0xE30
CreatePalette会调用ExAllocatePoolWithTag申请接近大小的内存
参考链接
- https://www.secrss.com/articles/35266
- https://mp.weixin.qq.com/s/AcFS0Yn9SDuYxFnzbBqhkQ
- https://bbs.pediy.com/thread-269930.htm