flawopen.com/Teardowns/cve-2024-30051-windows-dwm-privilege-escalation
dwmcore.dll における未検証の32ビット整数加算が、過小なヒープ領域の確保とメモリ破損を引き起こし、ローカルから SYSTEM 権限への昇格を許した仕組みを解説。
運送会社の窓口で『荷物は5個と5個です』と言って10個入る小さな箱をもらう場面を想像してください。しかし、計算機が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 が 0xFFFFFFFF を超えてラップアラウンドし、極小のヒープが割り当てられた後に memcpy による境界外書き込みが発生しました。
低権限のマルウェア(QakBot ドロッパーなど)がデスクトップウィンドウマネージャーと IPC 接続を初期化します。
攻撃者は commandHeader.offset + length が 0xFFFFFFFF を超過するよう設計した DirectComposition メッセージを構築します。
パッチ適用前の dwmcore.dll がオーバーフローした小さな値で計算を行い、メガバイト単位ではなくわずか16バイトのヒープを確保します。
メッセージパーサーが大量のペイロードをその微小な領域へコピーし、隣接する関数ポインタを上書きして 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;
}