/*
 * CVE-2024-21338 -- appid.sys Untrusted Pointer Dereference PoC
 *
 * Description:
 *   Demonstrates the untrusted pointer dereference vulnerability in the
 *   Windows AppLocker driver (appid.sys). The driver's IOCTL 0x22A018
 *   handler (AipSmartHashImageFile) passes a user-controlled buffer through
 *   AppHashComputeFileHashesInternal → AppHashComputeImageHashInternal,
 *   where a function pointer from the buffer is called in kernel context.
 *
 *   This PoC is a BLUE TEAM DETECTION TRIGGER only. It:
 *   1. Checks if the AppID driver is loaded and accessible
 *   2. Verifies appid.sys version for patch status
 *   3. Sends a benign IOCTL 0x22A018 with a NULL function pointer to
 *      confirm the driver accepts the request (pre-patch) or rejects it
 *      (post-patch)
 *
 *   It does NOT perform PreviousMode modification, token swapping, or any
 *   actual privilege escalation.
 *
 * Build (MSVC x64 Native Tools Command Prompt):
 *   cl.exe /W4 /O2 /D_CRT_SECURE_NO_WARNINGS poc_cve_2024_21338.c /link kernel32.lib version.lib advapi32.lib ntdll.lib
 *
 * Usage:
 *   poc_cve_2024_21338.exe         (normal)
 *   poc_cve_2024_21338.exe /v      (verbose)
 *
 * Expected (pre-patch):  IOCTL accepted, reports VULNERABLE
 * Expected (post-patch): IOCTL rejected with validation error
 *
 * NOTE: The full exploit (hakaioffsec/CVE-2024-21338) uses ExpProfileDelete
 *       as a kCFG gadget to modify PreviousMode → kernel R/W → token swap.
 *       This PoC intentionally omits all exploitation steps.
 *
 * Author: OnlyFm252
 * Date:   2026-07-26
 * CVE:    CVE-2024-21338
 *
 * DISCLAIMER: FOR DEFENSIVE RESEARCH AND BLUE TEAM DETECTION TESTING ONLY.
 */

/* -- poc_common.h configuration -- */
#define POC_CVE     "CVE-2024-21338"
#define POC_BINARY  L"appid.sys"

static int g_verbose = 0;
#define POC_VERBOSE g_verbose

#include "poc_common.h"

#include <winternl.h>

/* ======================================================================
 *  IOCTL and structure definitions
 * ====================================================================== */

#define IOCTL_AipSmartHashImageFile  0x22A018

/* The IOCTL buffer layout for Win11 (0x20 bytes) */
typedef struct _AIP_SMART_HASH_IMAGE_FILE {
    PVOID FirstArg;               /* +0x00: pointer dereferenced → rcx */
    PVOID FileObjectPtr;          /* +0x08: FILE_OBJECT address */
    PVOID PtrToFunctionWrapper;   /* +0x10: → CFG_FUNCTION_WRAPPER */
    PVOID Unknown;                /* +0x18: NULL (Win11 only) */
} AIP_SMART_HASH_IMAGE_FILE;

typedef struct _CFG_FUNCTION_WRAPPER {
    PVOID FunctionPointer;
} CFG_FUNCTION_WRAPPER;

/* NtDeviceIoControlFile typedef */
typedef NTSTATUS (NTAPI *PNtDeviceIoControlFile)(
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG IoControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength
);

/* ======================================================================
 *  Version comparison for appid.sys
 *
 *  Vulnerable: appid.sys shipped before Feb 2024 patch
 *  Patched:   Feb 2024+ builds
 * ====================================================================== */

static int check_appid_version(void)
{
    DWORD verHi, verLo;
    DWORD major, minor, build, rev;

    if (!poc_get_binary_version(POC_BINARY, &verHi, &verLo)) {
        POC_WARN(L"Cannot read appid.sys version — driver may not be loaded");
        return -1;
    }

    poc_print_binary_version(POC_BINARY, verHi, verLo);

    major = (verHi >> 16) & 0xFFFF;
    minor = verHi & 0xFFFF;
    build = (verLo >> 16) & 0xFFFF;
    rev   = verLo & 0xFFFF;

    POC_DETAIL(L"Version: %u.%u.%u.%u", major, minor, build, rev);

    (void)major;
    (void)minor;

    /* Win11 23H2: vulnerable < .3085, patched >= .3085 (Feb 2024 CU) */
    if (build == 22631 && rev < 3085) {
        POC_WARN(L"Build %u.%u is PRE-PATCH (vulnerable)", build, rev);
        return 1;
    }
    /* Win11 22H2: vulnerable < .3085 */
    if (build == 22621 && rev < 3085) {
        POC_WARN(L"Build %u.%u is PRE-PATCH (vulnerable)", build, rev);
        return 1;
    }
    /* Win10 22H2: vulnerable < .3208 */
    if (build == 19045 && rev < 4046) {
        POC_WARN(L"Build %u.%u is PRE-PATCH (vulnerable)", build, rev);
        return 1;
    }
    /* Win Server 2022: vulnerable < .4169 */
    if (build == 20348 && rev < 2322) {
        POC_WARN(L"Build %u.%u is PRE-PATCH (vulnerable)", build, rev);
        return 1;
    }

    POC_OK(L"Build %u.%u appears PATCHED", build, rev);
    return 0;
}

/* ======================================================================
 *  Check if AppID driver is accessible
 * ====================================================================== */

static HANDLE try_open_appid_device(void)
{
    HANDLE hDevice = CreateFileW(
        L"\\\\.\\AppID",
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );

    if (hDevice == INVALID_HANDLE_VALUE) {
        /* Try the NT device path via NtCreateFile */
        POC_DETAIL(L"CreateFileW failed (error %u), trying NtCreateFile", GetLastError());

        /* Load NtCreateFile from ntdll */
        typedef NTSTATUS (NTAPI *PNtCreateFile)(
            PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
            PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);

        HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
        if (!hNtdll) return INVALID_HANDLE_VALUE;

        PNtCreateFile pNtCreateFile = (PNtCreateFile)GetProcAddress(hNtdll, "NtCreateFile");
        if (!pNtCreateFile) return INVALID_HANDLE_VALUE;

        UNICODE_STRING devName;
        devName.Buffer = L"\\Device\\AppID";
        devName.Length = (USHORT)(wcslen(devName.Buffer) * sizeof(WCHAR));
        devName.MaximumLength = devName.Length + sizeof(WCHAR);

        OBJECT_ATTRIBUTES oa;
        memset(&oa, 0, sizeof(oa));
        oa.Length = sizeof(oa);
        oa.ObjectName = &devName;
        oa.Attributes = 0x40; /* OBJ_CASE_INSENSITIVE */

        IO_STATUS_BLOCK ioStatus;
        memset(&ioStatus, 0, sizeof(ioStatus));

        NTSTATUS status = pNtCreateFile(
            &hDevice, GENERIC_READ | GENERIC_WRITE,
            &oa, &ioStatus, NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            FILE_OPEN, 0, NULL, 0);

        if (status != 0) {
            POC_DETAIL(L"NtCreateFile to \\Device\\AppID failed: 0x%08X", status);
            return INVALID_HANDLE_VALUE;
        }
    }

    return hDevice;
}

/* ======================================================================
 *  Send a benign IOCTL probe
 *
 *  We send the IOCTL with a zeroed buffer. On pre-patch systems, the
 *  driver will attempt to process it (and likely crash or return an error
 *  due to NULL pointers, but WON'T validate the buffer structure).
 *  On post-patch systems, the driver validates the buffer and rejects it
 *  before dereferencing any pointers.
 *
 *  NOTE: We use NtDeviceIoControlFile to avoid SEH issues with
 *  DeviceIoControl wrapper.
 * ====================================================================== */

static int probe_ioctl(HANDLE hDevice)
{
    HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
    if (!hNtdll) {
        POC_WARN(L"Cannot load ntdll.dll");
        return -1;
    }

    PNtDeviceIoControlFile pNtDeviceIoControlFile =
        (PNtDeviceIoControlFile)GetProcAddress(hNtdll, "NtDeviceIoControlFile");

    if (!pNtDeviceIoControlFile) {
        POC_WARN(L"Cannot resolve NtDeviceIoControlFile");
        return -1;
    }

    /*
     * Send a probe with an intentionally small/invalid buffer.
     * Pre-patch: driver accepts the IOCTL and tries to process → returns
     *            an error from the hash computation (but accepts the code path)
     * Post-patch: driver validates buffer and rejects with STATUS_INVALID_PARAMETER
     *             before entering the vulnerable code path
     */
    BYTE probe_buffer[8];
    memset(probe_buffer, 0, sizeof(probe_buffer));

    IO_STATUS_BLOCK ioStatus;
    memset(&ioStatus, 0, sizeof(ioStatus));

    NTSTATUS status = pNtDeviceIoControlFile(
        hDevice,
        NULL, NULL, NULL,
        &ioStatus,
        IOCTL_AipSmartHashImageFile,
        probe_buffer,
        sizeof(probe_buffer),
        NULL, 0
    );

    POC_DETAIL(L"IOCTL probe returned NTSTATUS: 0x%08X", status);

    /*
     * STATUS_INVALID_PARAMETER (0xC000000D) from the patched driver means
     * the new validation caught the malformed buffer.
     * STATUS_ACCESS_VIOLATION or other errors from pre-patch means the
     * driver tried to process it without validation.
     *
     * Note: We use a small buffer intentionally so even pre-patch systems
     * won't crash — the buffer is too small for METHOD_BUFFERED to copy
     * enough data to reach the dereference.
     */
    if (status == 0xC000000D) {
        /* STATUS_INVALID_PARAMETER — could be either patch validation
         * or just buffer size check. Both are safe. */
        POC_INFO(L"Driver returned STATUS_INVALID_PARAMETER");
        POC_INFO(L"This is expected for both patched and buffer-size-rejected cases");
        return 0;
    } else if (status == 0) {
        /* STATUS_SUCCESS with our garbage buffer = very suspicious */
        POC_WARN(L"Driver accepted the probe buffer — PRE-PATCH behavior");
        return 1;
    } else {
        POC_DETAIL(L"Driver returned status 0x%08X", status);
        return 0;
    }
}

/* ======================================================================
 *  Demonstrate the exploitation chain (informational only)
 * ====================================================================== */

static void explain_exploitation_chain(void)
{
    POC_INFO(L"=== Exploitation Chain (CVE-2024-21338) ===");
    POC_INFO(L"1. Open handle to \\Device\\AppID");
    POC_INFO(L"2. Leak ETHREAD via NtQuerySystemInformation(SystemHandleInformation)");
    POC_INFO(L"3. Calculate PreviousMode address: KTHREAD+0x232");
    POC_INFO(L"4. Find kCFG gadget: ExpProfileDelete (ntoskrnl.exe PAGE section)");
    POC_INFO(L"5. Send IOCTL 0x22A018 with:");
    POC_INFO(L"   - FirstArg     = PreviousMode + 0x30");
    POC_INFO(L"   - FileObjectPtr = leaked FILE_OBJECT address");
    POC_INFO(L"   - FunctionPtr  = ExpProfileDelete address");
    POC_INFO(L"6. ExpProfileDelete → ObfDereferenceObjectWithTag");
    POC_INFO(L"   → lock xadd [rsi-0x30] decrements PreviousMode (1→0)");
    POC_INFO(L"7. NtWriteVirtualMemory now bypasses address validation");
    POC_INFO(L"8. Copy SYSTEM token (EPROCESS+0x4b8) to current process");
    POC_INFO(L"9. Restore PreviousMode to 1 (prevent BSOD)");
    POC_INFO(L"10. Spawn cmd.exe with SYSTEM privileges");
}

/* ======================================================================
 *  Main
 * ====================================================================== */

int wmain(int argc, wchar_t *argv[])
{
    int result = -1;
    int verResult;
    HANDLE hDevice;

    g_verbose = poc_parse_verbose(argc, argv);

    /* Unbuffer stdout */
    setvbuf(stdout, NULL, _IONBF, 0);

    poc_banner(L"appid.sys AipSmartHashImageFile Untrusted Pointer Dereference");

    /* Pre-flight */
    {
        static const poc_cfr_info cfr[] = { { 0, NULL } };
        poc_preflight(POC_BINARY, L"AppIDSvc", cfr, 0);
    }

    /* -- Step 1: Check appid.sys version -- */
    POC_STEP("Check appid.sys version for patch status");

    verResult = check_appid_version();
    if (verResult > 0) {
        result = 1;
    } else if (verResult == 0) {
        result = 0;
    }

    /* -- Step 2: Try to open AppID device -- */
    POC_STEP("Open handle to AppID device");

    hDevice = try_open_appid_device();
    if (hDevice == INVALID_HANDLE_VALUE) {
        POC_WARN(L"Cannot open \\Device\\AppID — AppIDSvc may not be running");
        POC_INFO(L"The AppLocker driver must be loaded for exploitation");
        POC_INFO(L"Try: sc start AppIDSvc (requires admin)");
    } else {
        POC_OK(L"Successfully opened handle to AppID device: 0x%p", hDevice);

        /* -- Step 3: Send benign IOCTL probe -- */
        POC_STEP("Send benign IOCTL 0x22A018 probe");

        int probeResult = probe_ioctl(hDevice);
        if (probeResult > 0 && result <= 0) {
            result = 1;
        }

        CloseHandle(hDevice);
    }

    /* -- Step 4: Display exploitation chain -- */
    POC_STEP("Display exploitation chain (informational)");
    explain_exploitation_chain();

    /* -- Step 5: Evaluate results -- */
    POC_STEP("Evaluate results");

    if (result == 0) {
        POC_OK(L"System is PATCHED — appid.sys validates IOCTL buffer before dereferencing");
    } else if (result > 0) {
        POC_WARN(L"System appears VULNERABLE — appid.sys version is pre-patch");
        POC_INFO(L"Exploitation uses ExpProfileDelete kCFG gadget for PreviousMode modification");
        POC_INFO(L"See: github.com/hakaioffsec/CVE-2024-21338");
    } else {
        POC_INFO(L"Could not determine patch status — check appid.sys version manually");
    }

    poc_results(result);
    return result > 0 ? 1 : 0;
}
