CVE-2023-23376 — CLFS CONTROL Block OOB via DumpCount/Sector Signature Overlap

Last updated: 2026-04-10
Component: clfs.sys
Bug Class: OOB write — corrupt index bypass via CONTROL block signature overlap
Patch: February 2023 Patch Tuesday
Exploited ITW: Yes (discovered by Microsoft MSTIC/MSRC as zero-day)
Exploit Author: Same as CVE-2022-24521, CVE-2022-37969 (Exploit #3), CVE-2023-28252
Related: Clfs, Cve 2022 24521, Cve 2023 28252, Primitives
Tags: clfs, oob-write, control-block, iextendblock, iflushblock, kernel-mode, lpe


Summary

CVE-2023-23376 is Exploit #4 in the Kaspersky 5-CVE series. The October 2022 CLFS patch hardened validation of the GENERAL block, so the exploit author switched to the CONTROL block. Root cause: ExtendMetadataBlock does not validate iExtendBlock/iFlushBlock indexes — only OpenImage does, and only on initial open. A crafted BLF causes these indexes to become malicious after the initial check, resulting in OOB access into m_rgBlocks[] with a fully controlled pointer.


Background: CLFS_CONTROL_RECORD Fields

CLFS_CONTROL_RECORD key fields:
  eExtendState     ULONG    // 0=None, 1=Extending, 2=Flushing
  iExtendBlock     USHORT   // index of block being extended
  iFlushBlock      USHORT   // index of block being flushed
  cNewBlockSectors ULONG    // new block size (sectors)
  cExtendStartSectors ULONG // original block size
  cExtendSectors   ULONG    // sectors to add
  rgBlocks[6]      CLFS_METADATA_BLOCK[]  // all 6 block descriptors

ExtendMetadataBlock uses iExtendBlock and iFlushBlock as indexes into m_rgBlocks[6]. Without bounds checking, an index of 0x13 = 0x13 * sizeof(CLFS_METADATA_BLOCK) bytes past m_rgBlocks[0] → OOB read → controlled pointer.


Root Cause: Index Check Only in OpenImage

// CClfsBaseFilePersisted::OpenImage checks:
if (iExtendBlock >= 6 || iFlushBlock >= 6)
    return error;

// ExtendMetadataBlock does NOT check:
block = m_rgBlocks[iExtendBlock];  // OOB if iExtendBlock > 5

Attack vector: Get malicious iExtendBlock/iFlushBlock values past the OpenImage check but before a second call to ExtendMetadataBlock. This is achieved by making RecordOffsets[0] and DumpCount alias the same bytes.


Exploit Mechanism: Sector Signature Overlap

Setup Patches (crafted BLF):

  1. Move CLFS_CONTROL_RECORD to offset 0x1FF within the CONTROL block so that the DumpCount field aligns with where the sector #0 signature (at 0x1FE) will be written during ClfsDecodeBlock

  2. Set eExtendState, iExtendBlock, iFlushBlock to trigger ExtendMetadataBlock from OpenImage (first call — malicious values blocked by index check, but legitimate values used)

  3. Build malicious CLFS_CONTROL_RECORD at offset 0x2FF with iExtendBlock = iFlushBlock = 0x13 (or another large OOB value)

  4. Patch SignaturesOffset: Change from 0x3F8 to 0x28 — moves the signatures restoration array from the last sector to the block header itself (sector #0 signature will alias CLFS_CONTROL_RECORD->DumpCount)

  5. Increase cbSymbolZone in the GENERAL block (same as prior exploits) so ExtendMetadataBlock is called again when AddLogContainer is called

Execution Flow

1. OpenImage opens BLF:
   → ClfsDecodeBlock restores sector #0 signature bytes
   → Sector #0 signature (2 bytes at 0x1FE in block = DumpCount offset) gets value 0x1FF
   → OpenImage checks iExtendBlock/iFlushBlock → still valid → ExtendMetadataBlock runs
   → ExtendMetadataBlock increments DumpCount → word at 0x1FE becomes 0x2FF
   → FlushMetadata → WriteMetadataBlock → ClfsEncodeBlock
   → ClfsEncodeBlock copies updated sector #0 original bytes back to RecordOffsets[0]
   → RecordOffsets[0] (alias of sector #0 original byte at 0x50+0) = 0x2FF

2. AddLogContainer triggers cbSymbolZone extension:
   → ExtendMetadataBlock called again
   → GetControlRecord reads from RecordOffsets[0] = 0x2FF → malicious CLFS_CONTROL_RECORD
   → iExtendBlock = iFlushBlock = 0x13 — no check here!
   → WriteMetadataBlock uses m_rgBlocks[0x13] (OOB, attacker-controlled pointer)
   → DumpCount field of OOB block is incremented → arbitrary increment primitive

Post-Corruption Exploitation

Arbitrary increment primitive: m_rgBlocks[0x13].pbImage points to attacker spray. WriteMetadataBlock increments DumpCount at spray_address + DUMP_COUNT_OFFSET.

Win10 path: Use arbitrary increment to decrement KTHREAD.PreviousMode (1 → 0) → NtRead/WriteVirtualMemory bypass → token steal

Win11 path: Corrupt PipeAttribute structure (AttributeValueSize field) to build AAR/AAW primitive via NtFsControlFile(0x11003C/0x110038):

  1. Spray pipe attributes with known kernel address of AttributeValueSize
  2. Use increment to set AttributeValue to point to target kernel address
  3. Read/write kernel data via pipe attribute I/O

BLF Patches Required

LocationFieldChange
CONTROL block +0x1FFCLFS_CONTROL_RECORD moved hereOffset shift for DumpCount ↔ signature alias
CONTROL block +0x2FFMalicious CLFS_CONTROL_RECORDiExtendBlock = iFlushBlock = 0x13, forged pointer
CONTROL block headerSignaturesOffset0x3F8 → 0x28 (signatures alias block header)
GENERAL block +0x1B98cbSymbolZoneLarge value to trigger second ExtendMetadataBlock
CONTROL/SHADOWDumpCountSHADOW lower than CONTROL (so CONTROL is selected initially)

CVE Series Context

  • The October 2022 patch added CClfsBaseFile::Validate* functions for GENERAL block bounds checking
  • The exploit author simply switched to the CONTROL block (which lacks equivalent validation)
  • CVE-2023-28252 (Exploit #5) uses a completely different trigger for the same ultimate goal of making iExtendBlock/iFlushBlock OOB at the moment ExtendMetadataBlock runs

References

  • Kaspersky GReAT / Boris Larin — “Windows CLFS and five exploits (Exploit #4 — CVE-2023-23376)” — securelist.com, 2023-12-21
  • Exodus Intelligence — “Exploiting CVE-2021-36955/36963/38633” — first description of CONTROL_RECORD targeting pattern, March 2022