# Root Cause Analysis — CVE-2025-62455

## Overview

| | |
|---|---|
| CVE | CVE-2025-62455 |
| Binary | mqac.sys (Microsoft Message Queuing kernel driver) |
| Impact | Elevation of Privilege |
| CVSS | 7.8 (Important) — Exploitation Less Likely |
| CWE | CWE-20: Improper Input Validation |
| Pre-patch version | 10.0.17763.7919 |
| Post-patch version | 10.0.17763.8146 |
| KB | KB5071544 (December 2025) |
| Credit | [T0 (@t0zhang)](https://x.com/t0zhang) |

## Root Cause

The MSMQ kernel driver (`mqac.sys`) creates storage files on disk using `ZwCreateFile` with a **DOS path** (`C:\Windows\System32\msmq\storage\l%07u.mq`) that is resolved through the calling process's device map (`_EPROCESS.DeviceMap`). Both `ACpCreateBitmap` and `CMMFAllocator::Create` set `OBJECT_ATTRIBUTES.Attributes = 0x240` (`OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE`), **missing `OBJ_FORCE_ACCESS_CHECK` (0x400)**. Because `mqac.sys` runs at SYSTEM level, the `ZwCreateFile` calls bypass all access checks.

Any unprivileged process can redirect its device map via `NtSetInformationProcess(ProcessDeviceMap)`, remapping `\??\C:` to a symbolic link pointing to an attacker-controlled path. When the driver subsequently calls `ZwCreateFile` with the DOS path, the path resolution follows the redirected device map, causing SYSTEM-level file creation at an attacker-chosen location.

This is architecturally identical to CVE-2025-60705 (csc.sys) — both use `Attributes=0x240` on Zw* calls with caller-influenceable paths, but CVE-2025-62455 uses device map redirection rather than registry symlinks.

## Ghidra-Verified Call Chain

```
User-mode entry point:
  MQSendMessage / MQReceiveMessage (mqrt.dll)
    → DeviceIoControl to \\.\MSMQ
      → IRP_MJ_DEVICE_CONTROL

Kernel call chain (pre-patch mqac.sys 10.0.17763.7919):

  DriverEntry @ 0x1c0092754
    → Registers ACDeviceControl in MajorFunction[IRP_MJ_DEVICE_CONTROL]
      (DATA xrefs at 0x1c0012be2, 0x1c0012be9)

  ACDeviceControl @ 0x1c000ba10
    → IOCTL dispatch (big switch on IoControlCode)

    Path 1 — IOCTL 0x19651407 (ACAllocatePacket):
      → ACAllocatePacket @ 0x1c00053a8
        → CPacket::Create @ 0x1c0014e04
          → CPoolAllocator::malloc @ 0x1c0011ea4
            → CPoolAllocator::CreateAllocator @ 0x1c0011c24
              → ACpCreateBitmap @ 0x1c0010de4           [VULNERABLE]
                → ACpGenerateFileName(storagePath, index)
                → ZwCreateFile(handle, 0xc0100000,
                    &ObjAttr{Attributes=0x240}, ...)
              → CMMFAllocator::Create @ 0x1c0011254     [VULNERABLE]
                → ACpGenerateFileName(storagePath, index)
                → ZwCreateFile(handle, 0xc0100000,
                    &ObjAttr{Attributes=0x240}, ...)

    Path 2 — IOCTL 0x19651063 (ACRestorePackets):
      → ACRestorePackets @ 0x1c0009938
        → RestorePackets @ 0x1c0010828
          → ACpCreateBitmap @ 0x1c0010de4               [VULNERABLE]
          → CMMFAllocator::Create @ 0x1c0011254         [VULNERABLE]
```

### Vulnerable Code — ACpCreateBitmap

From Ghidra decompilation at `0x1c0010de4` (439 bytes pre-patch):

```c
CPingPong * ACpCreateBitmap(
    _DEVICE_OBJECT *param_1,
    wchar_t *param_2,       // storage path template (e.g. "C:\...\l%07u.mq")
    wchar_t **param_3,      // output: generated filename
    void **param_4)         // output: file handle
{
    // ... pool allocation ...

    pCVar4 = ACpGenerateFileName(param_2, *(uint *)(lVar1 + 0x148));
    // pCVar4 = L"C:\Windows\System32\msmq\storage\l0000001.mq"

    RtlInitUnicodeString(local_70, pCVar4);
    local_40 = local_70;           // ObjectName
    local_50[0] = 0x30;            // Length
    local_38 = 0x240;              // Attributes = OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE
                                   //              ← MISSING OBJ_FORCE_ACCESS_CHECK (0x400)
    local_30 = 0; uStack_28 = 0;  // SecurityDescriptor = NULL

    iVar2 = ZwCreateFile(&local_78,
        0xc0100000,                // DesiredAccess (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE)
        local_50,                  // ObjectAttributes with Attributes=0x240
        local_60,                  // IoStatusBlock
        &local_res8,               // AllocationSize = 0x2000
        0x80,                      // FILE_ATTRIBUTE_NORMAL
        1,                         // FILE_SHARE_READ
        3,                         // FILE_OPEN_IF
        0x2a,                      // CreateOptions
        0, 0);
}
```

### Vulnerable Code — CMMFAllocator::Create

From Ghidra decompilation at `0x1c0011254` (906 bytes pre-patch):

```c
CMMFAllocator * CMMFAllocator::Create(
    CPoolAllocator *param_1,
    wchar_t *param_2,       // storage path template
    int param_3)            // create flag
{
    pwVar4 = ACpGenerateFileName(param_2, ...);

    RtlInitUnicodeString(local_88, pwVar4);
    local_a8 = local_88;          // ObjectName
    local_b8[0] = 0x30;           // Length
    local_a0 = 0x240;             // Attributes = 0x240
                                  //              ← MISSING OBJ_FORCE_ACCESS_CHECK
    local_b0 = 0;
    local_98 = 0; uStack_90 = 0;

    iVar2 = ZwCreateFile(&local_res8,
        0xc0100000,               // GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE
        puVar7,                   // ObjectAttributes with Attributes=0x240
        local_78,                 // IoStatusBlock
        &local_res20,             // AllocationSize = g_ulHeapPoolSize
        0x80,                     // FILE_ATTRIBUTE_NORMAL
        1,                        // FILE_SHARE_READ
        3,                        // FILE_OPEN_IF
        0x8022,                   // CreateOptions (includes FILE_SYNCHRONOUS_IO_NONALERT)
        0, 0);

    // On success: creates section, maps memory, initializes allocator
    // On failure: logs via WPP tracing
}
```

### ACpGenerateFileName — Path Construction

```c
wchar_t * ACpGenerateFileName(wchar_t *param_1, uint param_2)
{
    // param_1 = L"C:\Windows\System32\msmq\storage\l%07u.mq"
    // param_2 = file index (masked to 28 bits)
    StringCchPrintfW(pwVar5, uVar6, param_1, (ulonglong)(param_2 & 0xfffffff));
    // Result: L"C:\Windows\System32\msmq\storage\l0000001.mq"
}
```

The DOS path `C:\Windows\System32\msmq\storage\l%07u.mq` begins with a drive letter. When the kernel resolves this path, it prepends `\??\` and looks up the drive letter through the process's device map. By default, `\??\C:` points to `\Device\HarddiskVolume3` (or similar), but `NtSetInformationProcess(ProcessDeviceMap)` can redirect it.

## The Fix

The patch adds a **feature-flag-gated `OBJ_FORCE_ACCESS_CHECK`** to both vulnerable functions:

1. Both `ACpCreateBitmap` (grew from 439→626 bytes) and `CMMFAllocator::Create` (grew from 906→1115 bytes) now call `EvaluateCurrentState(g_Feature_1840809274_58536133_FeatureDescriptorDetails)` before `ZwCreateFile`
2. When the feature flag is enabled, `Attributes` changes from `0x240` to `0x640` (adds `OBJ_FORCE_ACCESS_CHECK`)
3. `OBJ_FORCE_ACCESS_CHECK` forces the kernel to perform access checks against the **caller's security context** rather than SYSTEM, blocking device map redirection exploits
4. Error handling updated to distinguish `STATUS_ACCESS_DENIED` (0xC0000022) from other failures — the feature flag is expected to produce access-denied in exploit scenarios
5. Kill-switch path preserves old `0x240` behavior for controlled rollout

## Attack Surface Analysis

### Prerequisites
- MSMQ feature must be installed and running (not default on client SKUs, common on Server)
- Any local user (low-privileged)
- The attacker can install MSMQ via `DISM /Online /Enable-Feature /FeatureName:MSMQ-Server` if they have admin, or target systems where MSMQ is already installed

### Attack Steps
1. Call `NtSetInformationProcess(ProcessDeviceMap)` to redirect `\??\C:` to a symbolic link pointing to the attacker's chosen target directory
2. Open a handle to `\\.\MSMQ` via `CreateFile`
3. Send IOCTL `0x19651407` (ACAllocatePacket) — this triggers `CPoolAllocator::CreateAllocator` → `ACpCreateBitmap` / `CMMFAllocator::Create`
4. The driver calls `ZwCreateFile` with the DOS path `C:\Windows\System32\msmq\storage\l0000001.mq`
5. Path resolution follows the redirected device map → file is created at the attacker's target path under SYSTEM context
6. Result: arbitrary file creation as SYSTEM

### Impact
Full SYSTEM-level file write primitive. An attacker can:
- Plant a malicious DLL in a system directory for DLL hijacking
- Overwrite security-critical files
- Create files in protected locations for persistence
- Combined with other primitives, achieve full SYSTEM code execution

## Detection Rules

### YARA — PoC Detection

```yara
rule CVE_2025_62455_MSMQ_DeviceMap_EoP
{
    meta:
        description = "Detects PoC/exploit code for CVE-2025-62455 mqac.sys device map EoP"
        author = "OnlyFm252"
        date = "2026-07-19"
        cve = "CVE-2025-62455"
        severity = "high"

    strings:
        $device1 = "\\\\.\\MSMQ" ascii wide
        $device2 = "\\Device\\MSMQ" ascii wide
        $api1 = "NtSetInformationProcess" ascii
        $api2 = "ProcessDeviceMap" ascii wide
        $api3 = "DeviceIoControl" ascii
        $ioctl1 = { 07 14 65 19 }  // 0x19651407 little-endian (ACAllocatePacket)
        $ioctl2 = { 63 10 65 19 }  // 0x19651063 little-endian (ACRestorePackets)
        $path1 = "msmq\\storage" ascii wide nocase
        $path2 = "l%07u.mq" ascii wide

    condition:
        uint16(0) == 0x5A4D and
        (($device1 or $device2) and ($api1 or $api2)) or
        (($ioctl1 or $ioctl2) and ($path1 or $path2)) or
        ($api1 and $api2 and ($device1 or $device2))
}
```

### YARA — Vulnerable Binary Detection

```yara
rule CVE_2025_62455_Vulnerable_mqac_sys
{
    meta:
        description = "Detects pre-patch mqac.sys vulnerable to CVE-2025-62455"
        author = "OnlyFm252"
        date = "2026-07-19"
        cve = "CVE-2025-62455"

    strings:
        // ACpCreateBitmap / CMMFAllocator::Create: MOV DWORD [rbp+X], 0x240
        // This is Attributes = OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE
        $attr_set = { C7 45 ?? 40 02 00 00 }  // mov [rbp+offset], 0x240

        $driver = "mqac.sys" ascii wide
        $pool_tag = "MQHA" ascii  // 0x4841514d reversed

        // Feature flag call should NOT be present in vulnerable version
        $feature_flag = "Feature_1840809274" ascii wide

    condition:
        uint16(0) == 0x5A4D and
        $driver and
        $pool_tag and
        #attr_set >= 2 and
        not $feature_flag
}
```

### Sigma — MSMQ Device Map Manipulation

```yaml
title: Process Device Map Manipulation Targeting MSMQ
id: b4d7e182-9a3c-4f8b-a021-cve202562455a
status: experimental
description: |
    Detects processes that open the MSMQ device shortly after manipulating
    their process device map, which is the attack vector for CVE-2025-62455.
    NtSetInformationProcess(ProcessDeviceMap) redirects drive letter resolution,
    causing mqac.sys to create files at attacker-chosen paths.
author: OnlyFm252
date: 2026/07/19
references:
    - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-62455
logsource:
    category: file_event
    product: windows
detection:
    selection:
        TargetFilename|contains:
            - '\msmq\storage\'
        TargetFilename|endswith: '.mq'
    filter_normal_path:
        TargetFilename|startswith:
            - 'C:\Windows\System32\msmq\storage\'
    condition: selection and not filter_normal_path
level: critical
tags:
    - attack.privilege_escalation
    - attack.t1068
    - cve.2025.62455
```

### Sigma — Suspicious MSMQ Device Access

```yaml
title: Suspicious MSMQ Device Access from Non-Service Process
id: b4d7e182-9a3c-4f8b-a021-cve202562455b
status: experimental
description: |
    Detects non-standard processes accessing the MSMQ device, which is
    a prerequisite for triggering CVE-2025-62455. Normal MSMQ access
    comes from mqsvc.exe and legitimate MSMQ applications.
author: OnlyFm252
date: 2026/07/19
logsource:
    category: file_access
    product: windows
detection:
    selection:
        TargetFilename|contains: '\Device\MSMQ'
    filter_known:
        Image|endswith:
            - '\mqsvc.exe'
            - '\mmc.exe'
            - '\svchost.exe'
    condition: selection and not filter_known
level: medium
tags:
    - attack.privilege_escalation
    - attack.t1068
    - cve.2025.62455
```

### Sysmon Configuration

```xml
<!-- CVE-2025-62455: Monitor for MSMQ device map exploitation -->

<!-- Rule: Detect MSMQ storage file creation outside normal path -->
<RuleGroup name="CVE-2025-62455" groupRelation="or">
  <FileCreate onmatch="include">
    <TargetFilename condition="end with">.mq</TargetFilename>
  </FileCreate>
</RuleGroup>

<!-- Rule: Detect mqac.sys driver load -->
<RuleGroup name="CVE-2025-62455-Driver" groupRelation="or">
  <DriverLoad onmatch="include">
    <ImageLoaded condition="end with">mqac.sys</ImageLoaded>
  </DriverLoad>
</RuleGroup>

<!-- Rule: Detect MSMQ device access from unusual processes -->
<RuleGroup name="CVE-2025-62455-Access" groupRelation="or">
  <FileCreate onmatch="include">
    <TargetFilename condition="contains">msmq\storage</TargetFilename>
  </FileCreate>
</RuleGroup>
```

## References

- [MSRC — CVE-2025-62455](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-62455)
- Diff report: [mqac_sys-kb5071544.md](/data/patch_diffs/mqac_sys-kb5071544.md)
- Similar: CVE-2025-60705 (csc.sys — same Attributes=0x240 pattern, registry symlink variant)

---

<sub>Analysis by OnlyFm252 — Ghidra MCP call-flow analysis of mqac.sys 10.0.17763.7919 (pre-patch).</sub>
