flawopen.com/Teardowns/cve-2024-30051-windows-dwm-privilege-escalation

● CVE-2024-30051 · CVSS 9.8 · 심각
보안 연구 · FlawOpen

CVE-2024-30051: Windows DWM 코어 라이브러리 힙 오버플로우 권한 상승 심층 분석

dwmcore.dll의 검증되지 않은 32비트 정수 덧셈이 축소된 힙 할당을 유발하여 메모리를 손상시키고 SYSTEM 권한으로 상승하게 된 과정을 분석합니다.

💡 알기 쉬운 설명 (ELI5)

택배 회사에 '물품 5개 더하기 5개'라고 말해 10개만 들어가는 작은 상자를 받는 상황을 상상해 보세요. 하지만 계산기가 40억 단위를 넘으면 0으로 돌아가는 맹점을 이용해 '40억 개에 10개를 더하면 10개'라고 속입니다. 컴퓨터는 손바닥만 한 상자를 준비하지만, 공격자가 40억 개의 데이터를 억지로 쏟아부어 물류창고 전체를 마비시키고 시설 통제권을 장악하는 것과 같습니다.

핵심 개념 및 용어

Desktop Window Manager (dwmcore.dll)
시각적 창 프레임 합성, DirectComposition 렌더링 및 데스크톱 그래픽을 처리하는 Windows 핵심 하위 시스템.
Integer Wrap-Around / Overflow
32비트 부호 없는 정수 산술에서 0xFFFFFFFF + 1은 0으로 순환합니다. 힙 버퍼 할당 전 이를 계산하면 요청보다 훨씬 작은 버퍼가 생성됩니다.
Pool / Heap Corruption
힙 청크 경계를 넘어 인접한 할당자 구조체에 데이터를 덮어써 NT AUTHORITY\SYSTEM 권한으로의 로컬 권한 상승을 가능케 합니다.
Safe Integer Arithmetic (ULongAdd)
덧셈 결과가 오버플로우되는지 확인한 후 합계를 반환하는 Microsoft의 안전한 정수 연산 라이브러리 함수.

근본 원인 분석 (Root Cause)

원인은 dwmcore.dll의 CInteraction::ProcessInputArray 함수 내 검증되지 않은 32비트 정수 오버플로우입니다. offset + count가 0xFFFFFFFF를 초과하여 16바이트의 극소형 힙만 할당된 후 memcpy를 통해 메모리 오버플로우가 일어납니다.

단계별 공격 실행 흐름

Step 1

1. 로컬 악성코드 실행 및 IPC 채널 개방

낮은 권한의 로컬 악성코드(예: QakBot 드로퍼)가 데스크톱 창 관리자(DWM)와 IPC 연결을 시작합니다.

Step 2

2. 조작된 대형 합성 명령 전달

공격자는 commandHeader.offset + commandHeader.length의 합이 의도적으로 0xFFFFFFFF를 넘어가도록 DirectComposition 메시지를 만듭니다.

Step 3

3. 축소된 힙 크기 할당

패치되지 않은 dwmcore.dll이 오버플로우된 값을 바탕으로 단 16바이트 크기의 힙 청크만 할당합니다.

Step 4

4. 메모리 덮어쓰기 및 SYSTEM 권한 탈취

메시지 파서가 전체 페이로드를 극소형 힙에 복사하면서 인접한 함수 포인터를 덮어쓰고 SYSTEM 권한을 획득합니다.

소스 코드 비교: 취약한 구현 vs 보안 패치

✕ 취약한 구현
// 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;
}

엔지니어링 및 시스템 보안 강화 체크리스트