Advisory July 8, 2025 By Chen Le Qi 3 min read
CVE: CVE-2025-47985
Affected Versions: Windows 11
CVSS3.1: 7.8 (High) — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
| Product | Windows Event Tracing (ETW) |
|---|---|
| Vendor | Microsoft |
| Severity | High — a local attacker may exploit this to elevate privileges to SYSTEM |
| Affected Versions | Windows 11 |
| Tested Versions | Windows 11 23H2 (Build 22631.4660) |
| CVE Identifier | CVE-2025-47985 |
| CVE Description | Insufficient validation in Event Tracing for Windows (ETW) in Microsoft Windows 11 may allow an unprivileged attacker to execute code as SYSTEM |
| CWE Classification(s) | CWE-125: Out-of-Bounds Read |
Base Score: 7.8 (High)
Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
| Metric | Value |
|---|---|
| Attack Vector (AV) | Local |
| Attack Complexity (AC) | Low |
| Privileges Required (PR) | Low |
| User Interaction (UI) | None |
| Scope (S) | Unchanged |
| Confidentiality (C) | High |
| Integrity (I) | High |
| Availability (A) | High |
Event Tracing for Windows (ETW) is a high-performance kernel-level tracing facility built into Windows. It provides user-mode applications and kernel-mode drivers with a mechanism to raise and log events, and is widely used for diagnostics, performance monitoring, and security tooling. ETW is implemented in ntoskrnl.exe and exposed to userspace through syscalls including NtTraceControl().
To start an ETW trace session, the usermode API StartTraceW() builds a WMI_LOGGER_INFORMATION structure and passes it to NtTraceControl() with opcode 0x1, eventually reaching EtwpStartLogger() in the kernel. One of the fields in this structure is TRACE_ENABLE_FLAG_EXTENSION, which holds an offset relative to the start of the WMI_LOGGER_INFORMATION object pointing to a TRACE_ENABLE_FLAG_EXT_HEADER:
struct _TRACE_ENABLE_FLAG_EXTENSION
{
USHORT Offset;
UCHAR Length;
UCHAR Flags;
};
struct _TRACE_ENABLE_FLAG_EXT_HEADER
{
USHORT LengthDWORDs;
USHORT Items;
};
struct TRACE_ENABLE_FLAG_EXT_ITEM
{
USHORT ItemLengthDWORDs;
USHORT Type;
};
On entry to EtwpStartLogger(), the kernel calls EtwpValidateFlagExtension() to validate the offset, ensuring it does not exceed the size of the structure in memory. However, the validation does not ensure that the offset avoids overlapping with other mutable fields of WMI_LOGGER_INFORMATION. This is significant because ETW modifies the structure during processing to communicate information back to usermode.
For example, EtwpStartLogger() clamps LoggerInfo->BufferSize if it exceeds 0x4000:
BufferSize = LoggerInfo->BufferSize;
if ( BufferSize )
{
if ( BufferSize > 0x4000 )
{
LoggerInfo->BufferSize = 0x4000;
BufferSize = 0x4000;
}
LoggerContext->BufferSize = BufferSize << 10;
}
By crafting TRACE_ENABLE_FLAG_EXTENSION.Offset such that the TRACE_ENABLE_FLAG_EXT_HEADER overlaps with LoggerInfo->BufferSize, an attacker can arrange for the clamping write to corrupt the LengthDWORDs field of the extension header to 0x4000 — far larger than the actual allocation — after validation has already passed.
The following PoC layout demonstrates the overlap:
loggerInfo->Wnode.Flags = 0x20003; // fake TRACE_ENABLE_FLAG_EXT_HEADER
loggerInfo->BufferSize = 0x10001; // fake item — LengthDWORDs corrupted to 0x4000 post-validation
loggerInfo->MinimumBuffers = 0x1; // fake item continuation
loggerInfo->EnableFlags.Length = 0xff;
loggerInfo->EnableFlags.Offset = (ULONG64)(&loggerInfo->Wnode.Flags) - (ULONG64)(loggerInfo);
loggerInfo->EnableFlags.Flags = 0xff;
Subsequently, EtwpGetFlagExtension() retrieves extension items trusting the already-validated (but now corrupted) header, with no further bounds checking:
_TRACE_ENABLE_FLAG_EXT_ITEM *EtwpGetFlagExtension(
cstmWMI_LOGGER_INFORMATION *WmiLoggerInformation, UINT16 ExtType)
{
offs = WmiLoggerInformation->EnableFlags;
start = (TRACE_ENABLE_FLAG_EXT_HEADER *)((char *)WmiLoggerInformation + offs.Offset);
item = (TRACE_ENABLE_FLAG_EXT_ITEM *)&start[1];
while ( ctr < start->Items )
{
if ( item->Type == ExtType )
return item;
++ctr;
item += item->Offset;
}
return 0LL;
}
With the corrupted LengthDWORDs of 0x4000, when EtwpGetFlagExtension() advances to the next item it walks 0x4000 * 4 = 0x10000 bytes past the current position — far beyond the pool allocation — returning an attacker-influenced item from adjacent pool memory. This out-of-bounds read is then consumed by callers such as EtwpUpdatePerProcessTracing(), where full control of the returned item enables further exploitation primitives. The function has several cross-references worth investigating for further exploitable paths:

The kernel crash confirms the out-of-bounds access:
*** Fatal System Error: 0x00000050
(0xFFFFE68BD0704052, 0x0000000000000000, 0xFFFFF801779B59F7, 0x0000000000000000)
The fix should ensure that TRACE_ENABLE_FLAG_EXTENSION.Offset cannot designate a region that overlaps with any mutable field of the WMI_LOGGER_INFORMATION structure.
Chen Le Qi of STAR Labs SG Pte. Ltd.