
Master's Thesis research on CVE-2024-30051 (Windows DWM Heap Overflow). Features a high-reliability exploit with automated heap spray optimization, real-time logging, and empirical success-rate analysis. Portfolio piece demonstrating advanced Windows binary exploitation, heap layout manipulation, and LPE via Desktop Window Manager.
Heap-based Buffer Overflow in Windows Desktop Window Manager (
dwmcore.dll)
Local Privilege Escalation → Integrity Level SYSTEM via DWM process
Build target: Windows 11 22H2 (10.0.22621.3447) · Patch: KB5037771
This repository contains my Master's Thesis research on CVE-2024-30051, a
High-severity (CVSS 7.8) Elevation of Privilege vulnerability in the Windows Desktop
Window Manager Core Library (dwmcore.dll).
The vulnerability originates from an integer division size miscalculation in
CCommandBuffer::Initialize. The size used for new() and the size used for memcpy()
diverge due to this miscalculation, producing a heap overflow of 0x8F bytes. A
successful exploit causes dwm.exe to load an attacker-controlled DLL, executing
arbitrary code under the window manager\dwm-1 account with Integrity Level SYSTEM.
| Aspect | Description |
|---|---|
| Patch diffing | Complete BinDiff analysis identifying CCommandBuffer::Initialize as the exact vulnerability locus (similarity score 0.32 vs global 0.98 across 14,062 matched functions) |
| WinDbg dynamic analysis | Step-by-step verification of all 4 hooks, size field overwrite, and payload construction |
| Empirical heap spray analysis | 50 controlled sessions across two RAM configurations (8,192 MB and 4,096 MB) with formal statistical tests |
| Statistical findings | Mann-Whitney U (p = 0.031) and Welch's t-test (p = 0.011) confirm RAM effect; observed means 12.9–19× better than theoretical prediction of ~64 attempts |
| Payload DLL path centralized | Extracted hardcoded path to #define PAYLOAD_DLL_PATH preprocessor constant |
| Session logging | Full timestamped log per session written to %TEMP%\cve_30051_log.txt |
| Academic documentation | Root cause, 50-session heap spray analysis, and CVE timeline |
CVE-2024-30051-Masters-Thesis/
├── README.md
├── LICENSE
├── setup.bat # Copies s11.dll to required location
│
├── exploit/
│ ├── C21.sln # Visual Studio 2022 solution
│ ├── exploit_src/
│ │ ├── c26f.vcxproj
│ │ ├── c26f.filters
│ │ └── main.cpp # Exploit — heap spray + hooking + overflow
│ └── payload/
│ ├── payload.vcxproj
│ ├── payload.vcxproj.filters
│ ├── dllmain.cpp # Payload DLL — spawns SYSTEM shell + cleanup
│ ├── framework.h
│ ├── pch.h
│ └── pch.cpp
│
└── docs/
├── screenshots/ # Patch diffing, WinDbg, and forensic captures
└── analysis/
├── 01-root-cause.md # Integer division bug in CCommandBuffer::Initialize
├── 02-heap-spray.md # 50-session empirical data and statistical findings
└── 03-timeline.md # Discovery, disclosure, and patch chronology
Open C21.sln in Visual Studio 2022. Build the payload project in Release x64.
Run setup.bat from the repository root. It copies s11.dll to
C:\Users\Public\Documents\s11.dll (the path defined by PAYLOAD_DLL_PATH).
⚠️ The DLL must be at this exact path before running
C26f.exe. Placing it next to the executable will not work.
Build the C26f project in Release x64.
x64\Release\C26f.exe
Run from a standard (non-elevated) CMD. The exploit auto-retries up to 10 times.
On success, dwm.exe loads s11.dll and spawns a CMD with SYSTEM Integrity Level.
A session log is written to %TEMP%\cve_30051_log.txt.
#define MAX_ATTEMPTS 10 // Max auto-retry attempts per session
#define SPRAY_STEP 0x10 // Hole spacing index (1024 holes)
#define SPRAY_RANGE_START 0x3000 // Spray range start index
#define SPRAY_RANGE_END 0x7000 // Spray range end index
#define SLEEP_POST_SPRAY 0xC8 // ms wait after spray (200ms)
#define SLEEP_POST_HOLES 0xC8 // ms wait after freeing holes (200ms)
#define PAYLOAD_DLL_PATH "C:\\Users\\Public\\Documents\\s11.dll"
In CCommandBuffer::Initialize (dwmcore.dll 10.0.22621.3447),
CD2DSharedBuffer::GetBufferSize is called twice. The size passed to new() undergoes
integer division by 0x90 before multiplication, while memcpy() uses the raw value:
buffer_size = GetBufferSize() → e.g. 0x23F
size_new = (0x23F / 0x90) * 0x90 = 0x1B0 ← allocated
size_memcpy = 0x23F ← copied
overflow = 0x23F - 0x1B0 = 0x8F bytes
BinDiff comparison between build 10.0.22621.3447 (vulnerable) and 10.0.22621.3593 (patched):
| Metric | Value |
|---|---|
| Global similarity | 0.98 |
| Confidence | 0.99 |
| Matched functions | 14,062 (99.1%) |
CCommandBuffer::Initialize similarity | 0.32 |
| Basic blocks — vulnerable version | 4 |
| Basic blocks — patched version | 20 (16 validation blocks added) |
The anomalous score of 0.32 against a global similarity of 0.98 is the direct signature of the vulnerability locus.
1. Hook RtlCreateHeap → capture dwmcore heap handle
2. Hook RtlAllocateHeap → capture base chunk address
3. Hook NtDCompositionCreateChannel → capture MappedAddress (shared memory region)
4. Hook NtDCompositionCommitChannel → overwrite size field (0x120 → 0x23F)
inject additional batch commands
5. Heap spray 0x10000 CHolographicInteropTexture objects (size=0x1B0)
6. Free holes every 0x10 indices → create gaps for overflow landing
7. Write payload into overflow buffer → KCBTable+0x388 + LoadLibraryA + DLL path
8. Release all spray objects → trigger overflow → LoadLibraryA("s11.dll")
9. dwm.exe loads payload DLL → spawns CMD as SYSTEM integrity
Does available RAM influence the number of attempts required for successful heap spray?
Two blocks of 25 sessions each. Protocol per session: restore clean post-boot snapshot → wait 100 seconds stabilization → launch exploit → record success attempt.
| Block | RAM | Sessions | Successes | Failures |
|---|---|---|---|---|
| A | 8,192 MB | 25 | 24 | 1 |
| B | 4,096 MB | 25 | 24 | 1 |
| Block | RAM | Mean attempts | Std deviation | 95% CI | Factor vs theory (64) |
|---|---|---|---|---|---|
| A | 8,192 MB | 3.375 | 1.135 | [2.896; 3.854] | ≈ 19× faster |
| B | 4,096 MB | 4.958 | 2.645 | [3.841; 6.076] | ≈ 12.9× faster |
| Test | Statistic | p-value | Conclusion |
|---|---|---|---|
| Mann-Whitney U | U = 184.0 | p = 0.031 | Rejects H₀ |
| Welch's t-test | t(23) = −2.695 | p = 0.011 | Rejects H₀ |