flawopen.com/Teardowns/cve-2024-30051-windows-dwm-privilege-escalation
dwmcore.dll의 검증되지 않은 32비트 정수 덧셈이 축소된 힙 할당을 유발하여 메모리를 손상시키고 SYSTEM 권한으로 상승하게 된 과정을 분석합니다.
택배 회사에 '물품 5개 더하기 5개'라고 말해 10개만 들어가는 작은 상자를 받는 상황을 상상해 보세요. 하지만 계산기가 40억 단위를 넘으면 0으로 돌아가는 맹점을 이용해 '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를 초과하여 16바이트의 극소형 힙만 할당된 후 memcpy를 통해 메모리 오버플로우가 일어납니다.
낮은 권한의 로컬 악성코드(예: QakBot 드로퍼)가 데스크톱 창 관리자(DWM)와 IPC 연결을 시작합니다.
공격자는 commandHeader.offset + commandHeader.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;
}