CLFS — Common Log File System
Last updated: 2026-04-10
Related: Pool Internals, Use After Free, Integer Overflows, Race Conditions, Primitives
Tags:kernel-mode,uaf,integer-overflow,oob-read,oob-write,ntoskrnl
Summary
CLFS (clfs.sys) is the Windows Common Log File System — a kernel-mode structured logging facility used by KTM (Kernel Transaction Manager), TxF (Transactional NTFS), TxR (Transactional Registry), and several system components. It parses complex on-disk binary structures (Base Log Files, .blf) from a reachable attack surface: any logged-in user can create and manipulate CLFS files via public Win32 APIs. CLFS has been one of the most heavily exploited kernel components for LPE since ~2021, yielding dozens of in-the-wild CVEs. Understanding its internals is mandatory for elite Windows kernel research.
Primary source: Alex Ionescu — clfs-docs (full reverse-engineered structure definitions from Windows 11 SDK + symbols)
Attack Surface
Any user on a Windows system can:
CreateLogFile() // open/create a .blf
WriteRestartArea() // write controlled data into the CLFS log
AddLogContainer() // attach container files
ClfsCreateMarshallingArea() // create marshalling context
ScanLogContainers() // archive context
PrepareLogArchive() // scan context
All of these APIs feed user-controlled bytes through CLFS kernel parsing code — no driver installation, no special privileges required. This is why CLFS is the dominant LPE attack surface in recent years.
Entry points into parsing code:
CClfsBaseFilePersisted::CreateImage()— loads metadata from disk (untrusted input)- IRP handlers (
CClfsRequestparsing) — all IOCTL/IRP parameter surfaces - Symbol table traversal — hash + BST chains from on-disk offsets
Base Log File (BLF) Format
Sector Architecture
- Fixed sector size: 512 bytes (
CLFS_SECTOR_SIZE = 0x200) - Each sector’s final 2 bytes =
[SectorBlockType | BeginEndFlags][USN] - Multi-sector blocks use USN (Update Sequence Number) in each sector to validate atomic writes — if a torn write occurs, mismatched USN is detected
Six Metadata Blocks
The BLF contains exactly 6 metadata blocks (3 pairs of primary + shadow):
| Index | Block Type | Purpose |
|---|---|---|
| 0 | ClfsMetaBlockControl | Layout/extend/truncate state — 2 sectors |
| 1 | ClfsMetaBlockControlShadow | Recovery copy of Control |
| 2 | ClfsMetaBlockGeneral | Client, container, security symbol tables |
| 3 | ClfsMetaBlockGeneralShadow | Recovery copy of General |
| 4 | ClfsMetaBlockScratch | Client stream changes during truncation |
| 5 | ClfsMetaBlockScratchShadow | Recovery copy of Scratch |
Shadow block purpose: each primary block has a shadow copy so that a torn write (power loss mid-write) can be detected and the prior version recovered using CLFS_METADATA_RECORD_HEADER.ullDumpCount (monotonically incrementing sequence number — higher = more recent).
Log Block Header (CLFS_LOG_BLOCK_HEADER)
Offset Field Type Description
+0x00 MajorVersion UCHAR Always 0x15
+0x01 MinorVersion UCHAR Always 0x00
+0x02 Usn UCHAR Update sequence number (0-255, wraps)
+0x03 ClientId 2B Stream identifier
+0x05 TotalSectorCount USHORT Block size in sectors (fixed per block type)
+0x07 ValidSectorCount USHORT Sectors after truncation
+0x09 Padding ULONG Reserved
+0x0D Checksum ULONG CRC32 (poly 0x04C11DB7)
+0x11 Flags ULONG CLFS_BLOCK_* state flags
+0x15 CurrentLsn CLFS_LSN INVALID for base blocks
+0x1D NextLsn CLFS_LSN INVALID for base blocks
+0x25 RecordOffsets[16] ULONG[16] Record start offsets within block
+0x65 SignaturesOffset ULONG Location of stored sector-signature restoration array
Exploit relevance of SignaturesOffset: This field points to an array that stores the original 2-byte sector signatures that were overwritten during encoding. On load, CLFS reads this array to restore those bytes. A malicious/corrupted SignaturesOffset value causes the driver to read/write from an attacker-controlled offset — OOB read or write primitive.
Control Record (CLFS_CONTROL_RECORD)
Lives in the first 2 sectors of the BLF. Contains:
ullMagicValue = 0xC1F5C1F500005F1C— validated on open (bypass: forge it)eExtendState— block extension state machine (3 states: None, ExtendingFsd, FlushingBlock)cxTruncate— truncate state machine context (6 states — see below)rgBlocks[6]—CLFS_METADATA_BLOCKdescriptors for each metadata block
CLFS_METADATA_BLOCK Descriptor
Field Type Notes
pbImage PUCHAR **IN MEMORY ONLY** — kernel pointer; should NEVER be on disk
cbImage ULONG Block data size (bytes)
cbOffset ULONG Offset from start of control block
eBlockType USHORT Block type (0-5)
Critical vulnerability class: pbImage is a kernel pointer. If the driver ever reads this from disk without zeroing it first, it can be:
- An info leak (if returned to user-mode): reveals kernel addresses → KASLR defeat
- A privilege escalation (if used as a pointer): controlled kernel pointer dereference → AAR/AAW
Base Record: Symbol Tables and Object Arrays
The General block contains:
Symbol Table Hash + BST
Each symbol table (clients, containers, security) uses:
- Array of 11
ULONGLONGhash buckets — offsets relative to block start - Collisions resolved via binary search tree:
CLFSHASHSYM.ulBelow/ulAbove(also relative offsets) - PJW hash (Hollub variant) on uppercase Unicode symbol name
typedef struct _CLFSHASHSYM {
CLFS_NODE_ID cidNode; // type + size validation
ULONG ulHash; // PJW hash value
ULONG cbHash; // symbol data size
ULONGLONG ulBelow; // BST left child — OFFSET from block start
ULONGLONG ulAbove; // BST right child — OFFSET from block start
LONG cbSymName; // symbol name string offset
LONG cbOffset; // symbol data offset
BOOLEAN fDeleted; // soft-delete flag
} CLFSHASHSYM;
Exploit relevance:
- All offsets are relative and parsed without cycle detection → crafting circular BST (
ulBelow → X → ulAbove → X) causes infinite loop (DoS) - Negative or out-of-bounds offsets → OOB read into adjacent metadata block or beyond
cbOffset+cbHash> block size → OOB read/write during symbol data access
Object Arrays
rgClients[124] // ULONG offsets to CLFS_CLIENT_CONTEXT within block
rgContainers[1024] // ULONG offsets to CLFS_CONTAINER_CONTEXT within block
Exploit relevance: cNextContainer and cActiveContainers control iteration over these arrays. If either exceeds 1024 → OOB access into data past the array.
Container Context (CLFS_CONTAINER_CONTEXT)
Field Type Notes
cbContainer ULONGLONG Container size
cidContainer ULONG 0-based container ID
cidQueue ULONG Should equal cidContainer (normally)
pContainer PTR64 **IN MEMORY ONLY** — CClfsContainer* kernel pointer
usnCurrent ULONG Container USN
eState ULONG Container state flags
Same critical issue as pbImage: pContainer must never be read from disk. If it is, attacker controls a kernel pointer.
Client Context (CLFS_CLIENT_CONTEXT)
Key LSN fields that are attack-relevant:
lsnOwnerPage CLFS_LSN Points to owner page block
lsnArchiveTail CLFS_LSN Archive boundary
lsnBase CLFS_LSN Log start
lsnLast CLFS_LSN Log end
lsnRestart CLFS_LSN Restart area location
lsnPhysicalBase CLFS_LSN Physical base
hSecurityContext HANDLE In-memory only — security context pointer
Exploit relevance: Each LSN field is essentially a pointer into the container files. Forging LSNs can redirect CLFS to parse attacker-controlled data as valid log records → enables arbitrary parsing of memory regions.
Truncation State Machine
The truncate context is a 6-state machine embedded in the Control Record:
None → ModifyingStream → SavingOwner → ModifyingOwner →
SavingDiscardBlock → ModifyingDiscardBlock → [back to None]
If a BLF is crafted with eTruncateState set to an intermediate state, CLFS will attempt to resume the truncation operation — potentially processing partially-initialized CLFS_TRUNCATE_CLIENT_CHANGE and CLFS_SECTOR_CHANGE structures from attacker-controlled data.
CLFS_SECTOR_CHANGE contains a full 512-byte sector payload. A crafted truncate record with malicious sector changes could cause CLFS to overwrite arbitrary sectors.
Vulnerability Classes (Mapped to Exploit Primitives)
1. In-Memory Pointer Persistence (Kernel Pointer Leak / KASLR Defeat)
CLFS_METADATA_BLOCK.pbImageorCLFS_CONTAINER_CONTEXT.pContainerwritten to disk- Read back via user-mode CLFS API → kernel address returned → KASLR defeated
- Pattern in CVEs: “information disclosure” CLFS CVEs follow exactly this pattern
2. Reference Count Overflow (UAF)
m_rgcBlockReferencesisPUSHORT(16-bit, max 65535)- Ionescu notes: “no meaningful protection against overflow or underflow”
- Overflow USHORT → count wraps to 0 → “no references” → object freed while still in use → UAF
- Exploitation: standard UAF → pool grooming → controlled re-allocation → arbitrary dispatch
3. OOB Read/Write via Corrupt Offsets
- Symbol table
ulBelow/ulAbove,cbOffset,cbSymName→ relative offsets parsed without bounds check SignaturesOffsetin block header → used to restore sector bytes → OOB write with partially controlled datacNextContainerexceedsMAX_CONTAINERS_DEFAULT (1024)→ OOB access inrgContainers[]
4. State Machine Resumption with Attacker Data
- Craft BLF with non-zero
eTruncateStateoreExtendState - CLFS resumes interrupted operation using attacker-controlled state
CLFS_SECTOR_CHANGE.rgbSector[]contains 512 bytes of arbitrary data written to disk
5. Checksum Forgery (CRC32 bypass)
CLFS_LOG_BLOCK_HEADER.Checksumuses CRC32 poly0x04C11DB7- CRC32 is not a cryptographic integrity check — trivially forgeable
- Forge any block content, recalculate CRC32, BLF parses as valid
- Tools:
crcany, Pythoncrcmod
6. USN Wraparound
UsnisUCHAR(0-255)- If comparison doesn’t handle wraparound, can make modified block appear older than original
- Shadow recovery logic may select the malicious block as “more recent”
Known CVE Clusters (CLFS LPE)
CLFS has generated a sustained stream of ITW CVEs — many exploited by ransomware operators for SYSTEM:
| CVE | Year | Bug Class | Technique | Exploited ITW |
|---|---|---|---|---|
| Cve 2022 24521 | April 2022 | OOB write | CLIENT_CONTEXT/CONTAINER_CONTEXT overlap, arbitrary decrement → PreviousMode | Yes |
| Cve 2022 37969 | Sept 2022 | OOB write | SignaturesOffset corruption → cbSymbolZone bypass → OOB write → vtable dispatch | Yes |
| Unknown | Oct 2022 | OOB write | cbSymbolZone + AddLogContainer trigger (variant of CVE-2022-24521) | Yes |
| Cve 2023 23376 | Feb 2023 | OOB write | CONTROL block, DumpCount/sector #0 alias, malicious iExtendBlock/iFlushBlock=0x13 | Yes |
| Cve 2023 28252 | April 2023 | OOB increment | ValidSectorCount=1 → ClfsEncodeBlock failure → shadow fallback → OOB index | Yes (Nokoyawa ransomware) |
| Cve 2024 49138 | Dec 2024 | Heap buffer overflow | Shadow block pbImage sharing + DumpCount parity attack → invalid GeneralBlock + controlled pContainer | Yes (ITW) |
| CVE-2024-20683 | 2024 | Pool corruption | Win32k+CLFS chain | Yes |
| Cve 2025 29824 | April 2025 | Use-After-Free | CClfsLogCcb freed in CLEANUP before CLOSE; LookasideList depth 24 must be saturated; IOCTL race | Yes (Storm-2460/RansomEXX) |
| Cve 2026 20820 | 2026 | Integer overflow | ScanContainers: ContainerCount*0x240 missing 0x38 header → OOB write | Unknown |
Pattern: The first five CVEs (April 2022–April 2023) share the same exploit author and the same iExtendBlock/iFlushBlock → m_rgBlocks OOB index abuse pattern. Each patch narrowly fixed one callsite; the author moved to a sibling. 32+ CLFS CVEs total since 2018.
HMAC Mitigation: Microsoft added CLFS file authentication (Merkle tree + HMAC) that blocks ALL BLF-file-based exploits. Does NOT block IRP/IOCTL-based attacks (CVE-2025-29824, CVE-2026-20820). See Clfs Authentication.
Deep-dive CVE pages: Each CVE in the table above links to a full analysis page.
Exploitation Flow (General CLFS LPE)
1. Craft malicious .blf file
→ forge block headers (valid magic, correct CRC32)
→ set corrupt offset / size field in target structure
2. Open BLF via CreateLogFile() or similar API
→ triggers kernel parsing of attacker-controlled structures
3. Corrupt kernel pool via parsing bug
→ pool overflow from OOB write
→ or UAF from reference count overflow
4. Groom pool around parsing allocation
→ use named pipe buffers / registry value data for adjacent controlled objects
5. Gain primitive (AAR/AAW or controlled dispatch)
6. Token steal / privilege flag flip (HVCI-compatible path)
Tools and Resources
- ionescu007/clfs-docs: Complete structure definitions — authoritative reference
- libclfs: Earlier reverse-engineered CLFS parser (C)
- [DeathNote talk, Peter Hlavaty]: Initial CLFS fuzzing research, state machine manipulation
- WinDbg:
!pool,dt clfs!CLFS_LOG_BLOCK_HEADER— inspect live CLFS allocations - Python +
crcmod: Forge CRC32 for crafted BLF blocks
Crafting a BLF (PoC Starting Point)
import struct, crcmod
crc32_func = crcmod.predefined.mkCrcFun('crc-32')
def build_block_header(usn, client_id, total_sectors, valid_sectors, flags, record_offsets):
# ... build CLFS_LOG_BLOCK_HEADER
hdr = struct.pack('<BBBBHHIIQQ',
0x15, 0x00, # Major/MinorVersion
usn, client_id,
total_sectors, valid_sectors,
0, # Padding
0, # Checksum placeholder
flags,
# ... LSNs, RecordOffsets, SignaturesOffset
)
# Fix checksum:
checksum = crc32_func(hdr)
return hdr[:0x0D] + struct.pack('<I', checksum) + hdr[0x11:]
Defensive Mitigations in CLFS (What Exists)
| Mechanism | Effectiveness |
|---|---|
| Shadow blocks + dump count | Recovers from torn writes; does NOT stop malicious BLF |
| USN sector signatures | Detects power-loss corruption; forgeable by attacker (know USN) |
| CRC32 checksum | Non-cryptographic; trivially forgeable |
Executive Resource (PERESOURCE) | Prevents concurrent corruption but not single-threaded attack |
Magic value (0xC1F5C1F500005F1C) | Trivially forgeable; just a constant |
| CLFS HMAC Authentication (MORSE team) | Blocks all BLF-file-based exploits — Merkle tree + HMAC per-BLF; .cnpf files store keys; 90-day learning mode then enforcement. Does NOT block IRP/IOCTL attacks. See Clfs Authentication |
Bottom line for BLF-based attacks: With HMAC enforcement enabled, all historical CLFS BLF exploits are blocked. The remaining attack surface is IOCTL-based (UAF via IRP race, integer overflow in IOCTL parameters).
Exploit Relevance
CLFS is currently the most fertile single kernel component for LPE research:
- Large, complex parser with documented weak validation
- Reachable from low-privilege user with no special conditions
- Sustained CVE history shows patches fix symptoms not root causes
- Variant analysis is highly productive — fix one parser field → find adjacent unpatched field
Priority action: Study CVE-2023-28252 (Nokoyawa exploit) root cause → understand the OOB write pattern → search for similar patterns in other offset-to-pointer calculations throughout the BLF parser.
References
- Alex Ionescu — “clfs-docs” — github.com/ionescu007/clfs-docs (primary source)
- Peter Hlavaty — “Death Note” — CLFS fuzzing research (PoC 2019)
- Kaspersky GReAT — CVE-2023-28252 analysis — securelist.com
- MSRC — CLFS advisory history — msrc.microsoft.com
- “Common Log File System: Many Vulnerabilities, One Driver” — various researcher analyses
