flawopen.com/Teardowns/cve-2024-30051-windows-dwm-privilege-escalation
深入剖析 dwmcore.dll 中未校验的 32 位整数加法如何导致堆缓冲区分配过小,从而引发内存破坏并最终提权至 SYSTEM 权限。
想象一家物流仓库根据你的申报单给你准备了一个只能放 10 件货物的超小纸箱,因为你填报了‘5 件加 5 件’。然而你故意利用了计数器在 42 亿时溢出归零的规则,谎报‘40 亿加 10 件等于 10 件’。仓库准备了小盒子,你却强行推进 40 亿件货物,堆满并冲垮了整个仓库过道,进而控制了整个工厂的安防系统。
Desktop Window Manager (dwmcore.dll)Integer Wrap-Around / OverflowPool / Heap CorruptionSafe Integer Arithmetic (ULongAdd)根本原因在于 dwmcore.dll 的 CInteraction::ProcessInputArray 函数中存在未经检查的 32 位整数溢出,offset + count 发生回绕后导致仅分配 16 字节堆内存,随后由 memcpy 引发严重堆溢出。
低权限本地恶意进程(如 QakBot 木马投放器)与 Desktop Window Manager 建立 IPC 管道连接。
攻击者精心构造 DirectComposition 绘图指令,使 commandHeader.offset 与 length 之和恰好跨越 0xFFFFFFFF 边界。
未打补丁的 dwmcore.dll 执行未保护加法,计算出极小数值,分配了一个仅 16 字节的微型堆缓冲区。
消息解析器将大量载荷数据 memcpy 拷入该微型堆块,线性溢出并覆盖邻近函数指针,最终获得 SYSTEM 权限。
// VULNERABLE: Decompiled C logic from dwmcore.dll before May 2024 patch
HRESULT CInteraction::ProcessInputArray(BYTE* pBuffer, ULONG offset, ULONG count) {
// CRITICAL ROOT CAUSE:
// Raw addition wraps around 32-bit integer boundaries!
// If offset = 0xFFFFFFF0 and count = 0x20:
// 0xFFFFFFF0 + 0x20 = 0x00000010 (16 bytes!)
ULONG totalAllocationSize = offset + count;
// Allocates a tiny 16-byte heap buffer!
BYTE* pDestArray = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalAllocationSize);
if (!pDestArray) {
return E_OUTOFMEMORY;
}
// Copies original oversized byte count, overflowing heap chunks!
memcpy(pDestArray + offset, pBuffer, count);
return S_OK;
}
// SECURE: Decompiled C logic from dwmcore.dll after Patch Tuesday
#include <intsafe.h>
HRESULT CInteraction::ProcessInputArray(BYTE* pBuffer, ULONG offset, ULONG count) {
ULONG totalAllocationSize = 0;
// 1. Enforce safe integer arithmetic check
// Returns INTSAFE_E_ARITHMETIC_OVERFLOW if sum exceeds ULONG_MAX
if (FAILED(ULongAdd(offset, count, &totalAllocationSize))) {
return E_INVALIDARG;
}
// 2. Validate upper bound threshold limits
if (totalAllocationSize > MAX_ALLOWED_INPUT_SIZE) {
return E_INVALIDARG;
}
BYTE* pDestArray = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalAllocationSize);
if (!pDestArray) {
return E_OUTOFMEMORY;
}
memcpy(pDestArray + offset, pBuffer, count);
return S_OK;
}