CVE-2024-49138 — CLFS Heap-Based Buffer Overflow via Shadow Block pbImage Sharing
Last updated: 2026-04-10
Component: clfs.sys
Bug Class: Heap-based buffer overflow — shadow blockpbImageshares primary block’s memory; reference count discrepancy leads to invalid General Metadata Block with controlledpContainer
Patch: December 2024 Patch Tuesday
Exploited ITW: Yes (December 2024 disclosure)
Discoverer: HN Security
Related: Clfs, Cve 2022 37969, Heap Grooming
Tags:clfs,oob-write,shadow-block,pbimage,heap-overflow,kernel-mode,lpe
Summary
CVE-2024-49138 exploits a discrepancy between how primary and shadow metadata blocks are managed in CLFS. The primary and shadow General Metadata Blocks share the same pbImage pointer (same kernel memory), but have different reference counts (primary ≥ 1, shadow = 0). When ExtendMetadataBlock calls WriteMetadataBlock on the shadow block and it fails (via crafted BLF), ClfsReleaseMetadataBlock for the shadow checks the reference count, finds it is 0, and returns without decrementing — but the primary block still has its reference count inflated. The resulting state: an invalid General Metadata Block with reference count > 0 (preventing cleanup) and pContainer pointing to attacker-controlled user-mode memory.
Background: Shadow Block Reference Count Design
// AcquireMetadataBlock — only called for primary blocks (not shadow)
// m_rgcBlockReferences:
// Primary block (General = index 2): count starts at 1, incremented by AcquireMetadataBlock
// Shadow block (GeneralShadow = index 3): count is ALWAYS 0
// Both m_rgBlocks[2].pbImage and m_rgBlocks[3].pbImage point to same allocation
ExtendMetadataBlock calls AcquireMetadataBlock only on primary blocks (even indices). It then loops over ALL blocks (including shadow, odd indices) for WriteMetadataBlock. This asymmetry is the root cause.
Root Cause: WriteMetadataBlock Failure on Shadow Frees Nothing
ExtendMetadataBlock:
AcquireMetadataBlock(GeneralBlock) → m_rgcBlockReferences[2] = 2 (already 1, now +1)
WriteMetadataBlock(GeneralBlock) → succeeds, m_rgcBlockReferences[2] stays 2
WriteMetadataBlock(GeneralShadow) → FAILS (crafted BLF triggers this)
→ WriteMetadataBlock → ClfsEncodeBlock → invalidates block (checksum = 0)
→ ClfsDecodeBlock fails (block now invalid)
→ ClfsReleaseMetadataBlock(shadow)
→ reads m_rgcBlockReferences[3] = 0 → returns immediately (no decrement!)
→ WriteMetadataBlock returns error
→ ExtendMetadataBlock fails and returns
→ exit path: ReleaseMetadataBlock(GeneralBlock) → m_rgcBlockReferences[2] = 1 (> 0!)
Result: GeneralBlock is INVALID (tampered pbImage/pContainer) and reference count = 1 (won't be freed)
Triggering the Failure: DumpCount Parity Attack
The exploit crafts the BLF so that WriteMetadataBlock on the shadow block fails:
- Set
ullDumpCountto an odd value in the General Metadata Block - First
WriteMetadataBlock(on primary General block): oddullDumpCount→ just increments → becomes even;UsnNOT changed - Second
WriteMetadataBlock(on shadow, samepbImage): evenullDumpCount→UsnIS incremented ClfsDecodeBlockfails (Usn mismatch) → triggersClfsReleaseMetadataBlockwith refcount 0 → no-op- Shadow write failure causes
ExtendMetadataBlockto fail - Primary block survives in memory: tampered with controlled
pContainer
Making pContainer Controllable
Simultaneously, the SignaturesOffset field is modified so that the signatures array overlaps the CLFS_CONTAINER_CONTEXT structure in the block:
- During
WriteMetadataBlockfailure path, the sector signature bytes are written intopContaineratCLFS_CONTAINER_CONTEXT+0x18 - The sector signature bytes are attacker-controlled (from crafted BLF sectors)
- Result:
pContainer = attacker_controlled_address
Exploitation Path via ScanContainerInfo
After establishing an invalid General Metadata Block with controlled pContainer, subsequent API calls that traverse container records will dereference the fake pointer.
Target API: CreateLogContainerScanContext() → kernel CClfsBaseFile::ScanContainerInfo()
CClfsBaseFile::ScanContainerInfo(...) {
BaseLogRecord = GetBaseLogRecord(this);
while (v16 < containerCount) {
v21 = BaseLogRecord->rgContainers[v20];
GetSymbol(this, v21, v20, &v41); // retrieves CLFS_CONTAINER_CONTEXT
CClfsContainer::QueryContainerInfo(v41->pContainer, v24); // UAF/OOB dereference
}
}
v41->pContainer is the attacker-controlled value → QueryContainerInfo dispatches through vtable → arbitrary dispatch.
Crafted BLF Characteristics
| Field | Value | Purpose |
|---|---|---|
Usn | 1 | Odd starting value for DumpCount parity trigger |
ullDumpCount | 1 (odd) | Triggers DumpCount parity mechanism |
SignaturesOffset | Modified | Signatures array overlaps CLFS_CONTAINER_CONTEXT |
CLFS_CONTAINER_CONTEXT.pContainer | Shifted to overlap sector signature | Becomes attacker-controlled address |
cbSymbolZone | Large value | Forces AddSymbol → ExtendMetadataBlock path |
rgContainer[] offset | Updated | Points to new CONTAINER_CONTEXT location |
| CRC32 checksum | Recomputed | Passes checksum validation |
Key Difference from CVE-2023-28252
| Aspect | CVE-2023-28252 | CVE-2024-49138 |
|---|---|---|
| Trigger | ValidSectorCount=1 → ClfsEncodeBlock returns error | DumpCount parity → shadow write failure |
| Shadow selection | Shadow block selected due to corrupted primary checksum | Shadow write fails without corrupting primary checksum |
| Root cause | Ignored return value of ClfsEncodeBlock | Reference count discrepancy between primary/shadow |
| Block type | CONTROL block | GENERAL block |
| OOB index | iExtendBlock/iFlushBlock OOB m_rgBlocks | pContainer controls subsequent container scan |
Python CRC32 Utility (from HN Security PoC)
import binascii
def calculate_crc32(input_file):
with open(input_file, "r") as f:
hex_data = f.read().strip()
hex_numbers = hex_data.split()
byte_array = bytes(int(num, 16) for num in hex_numbers)
crc32_checksum = binascii.crc32(byte_array) & 0xFFFFFFFF
little_endian_hex = ' '.join(f"{b:02X}" for b in crc32_checksum.to_bytes(4, 'little'))
print(f"CRC32 (LE): {little_endian_hex}")
References
- HN Security — “CVE-2024-49138: Windows CLFS Heap-Based Buffer Overflow Analysis” (Parts 1+2) — hnsecurity.it, 2025-01
- Microsoft MSRC — CVE-2024-49138 advisory — December 2024
