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 (CClfsRequest parsing) — 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):

IndexBlock TypePurpose
0ClfsMetaBlockControlLayout/extend/truncate state — 2 sectors
1ClfsMetaBlockControlShadowRecovery copy of Control
2ClfsMetaBlockGeneralClient, container, security symbol tables
3ClfsMetaBlockGeneralShadowRecovery copy of General
4ClfsMetaBlockScratchClient stream changes during truncation
5ClfsMetaBlockScratchShadowRecovery 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_BLOCK descriptors 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:

  1. An info leak (if returned to user-mode): reveals kernel addresses → KASLR defeat
  2. 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 ULONGLONG hash 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.pbImage or CLFS_CONTAINER_CONTEXT.pContainer written 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_rgcBlockReferences is PUSHORT (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
  • SignaturesOffset in block header → used to restore sector bytes → OOB write with partially controlled data
  • cNextContainer exceeds MAX_CONTAINERS_DEFAULT (1024) → OOB access in rgContainers[]

4. State Machine Resumption with Attacker Data

  • Craft BLF with non-zero eTruncateState or eExtendState
  • 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.Checksum uses CRC32 poly 0x04C11DB7
  • CRC32 is not a cryptographic integrity check — trivially forgeable
  • Forge any block content, recalculate CRC32, BLF parses as valid
  • Tools: crcany, Python crcmod

6. USN Wraparound

  • Usn is UCHAR (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:

CVEYearBug ClassTechniqueExploited ITW
Cve 2022 24521April 2022OOB writeCLIENT_CONTEXT/CONTAINER_CONTEXT overlap, arbitrary decrement → PreviousModeYes
Cve 2022 37969Sept 2022OOB writeSignaturesOffset corruption → cbSymbolZone bypass → OOB write → vtable dispatchYes
UnknownOct 2022OOB writecbSymbolZone + AddLogContainer trigger (variant of CVE-2022-24521)Yes
Cve 2023 23376Feb 2023OOB writeCONTROL block, DumpCount/sector #0 alias, malicious iExtendBlock/iFlushBlock=0x13Yes
Cve 2023 28252April 2023OOB incrementValidSectorCount=1 → ClfsEncodeBlock failure → shadow fallback → OOB indexYes (Nokoyawa ransomware)
Cve 2024 49138Dec 2024Heap buffer overflowShadow block pbImage sharing + DumpCount parity attack → invalid GeneralBlock + controlled pContainerYes (ITW)
CVE-2024-206832024Pool corruptionWin32k+CLFS chainYes
Cve 2025 29824April 2025Use-After-FreeCClfsLogCcb freed in CLEANUP before CLOSE; LookasideList depth 24 must be saturated; IOCTL raceYes (Storm-2460/RansomEXX)
Cve 2026 208202026Integer overflowScanContainers: ContainerCount*0x240 missing 0x38 header → OOB writeUnknown

Pattern: The first five CVEs (April 2022–April 2023) share the same exploit author and the same iExtendBlock/iFlushBlockm_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)

MechanismEffectiveness
Shadow blocks + dump countRecovers from torn writes; does NOT stop malicious BLF
USN sector signaturesDetects power-loss corruption; forgeable by attacker (know USN)
CRC32 checksumNon-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