CVE-2026-42985 Deep Dive: Weaponizing the RDP Client Use-After-Free for Rogue-Server RCE

Most of the security world’s RDP anxiety is pointed in the wrong direction. Since BlueKeep in 2019, the default mental model has been “RDP server gets owned by an unauthenticated attacker on the internet.” CVE-2026-42985 inverts that entirely. Here, the client is the victim. A sysadmin opens mstsc.exe, connects to what they believe is a legitimate server, and the server fires back a crafted PDU sequence that triggers a use-after-free in mstscax.dll, giving the attacker code execution on the admin’s workstation. CVSS 8.8, “Exploitation More Likely,” patched June 2026. If you manage endpoints where people RDP outbound to anything, this one matters.

Before we go further: a correction. Early reporting (and even some advisory aggregators) labeled this a “heap-based buffer overflow.” It is not. CVE-2026-42985 is CWE-416, a use-after-free. The heap-overflow bugs in the same June 2026 RDP cluster are CVE-2026-47289, CVE-2026-42992, and CVE-2026-44799 (all CWE-122). The distinction matters for root-cause analysis, for detection logic, and for anyone building exploit primitives. We will cover both bug classes here, but the primary target is the UAF.


The June 2026 RDP Client Cluster: What Shipped and What It Means

Microsoft’s June 2026 Patch Tuesday included an unusually dense cluster of RDP client-side vulnerabilities, all sharing the same attack vector: a rogue RDP server exploiting the connecting client. The Remote Desktop ActiveX control, mstscax.dll, received five separate patches in a single cycle. That is not normal. It suggests either a coordinated audit by external researchers, a targeted internal review, or both.

Here is the primary CVE alongside its siblings:

CVECWECVSSAttack ComplexityNotes
CVE-2026-42985CWE-416 (UAF)8.8LowPrimary focus. No race condition required.
CVE-2026-44801CWE-416 (UAF)8.8LowUAF, requires additional preparatory actions
CVE-2026-47654CWE-416 (UAF)8.8HighRequires winning a race condition
CVE-2026-48563CWE-416 (UAF)8.8HighRequires winning a race condition
CVE-2026-47289CWE-122 (Heap Overflow)8.8LowCertificate parser overflow
CVE-2026-42992CWE-122 (Heap Overflow)8.8LowHeap overflow in channel processing
CVE-2026-44799CWE-122 (Heap Overflow)8.8LowHeap overflow variant

Several bugs in this cluster were credited to Kyeongmin Kim of KAIST Hacking Lab. The CVSS vector for CVE-2026-42985 is AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H. Two things jump out. UI:R means the victim must initiate the connection (click an .rdp file, type a hostname into mstsc, launch from a script). PR:N means the attacker needs zero authentication on the server side. Stand up a TCP listener that speaks enough RDP to get past the handshake, and you are in business.

RDP Protocol Internals: What the Wire Looks Like

Understanding where the vulnerability lives requires understanding the protocol layers the client walks through when connecting. RDP is not a simple request-response protocol. It is a deeply layered stack built on top of ITU-T T.125 (MCS) and X.224, wrapped in TPKT framing over TCP port 3389.

A standard connection sequence proceeds through these phases:

  1. TCP handshake to port 3389.
  2. X.224 Connection Request / Confirm (TPKT framing). The client proposes security protocols (TLS, NLA/CredSSP). The server confirms.
  3. TLS handshake (if negotiated). Wraps all subsequent traffic.
  4. NLA / CredSSP (if enforced). Client authenticates to the server before the full RDP session begins. Important caveat: NLA does not protect against CVE-2026-42985. The UAF triggers after NLA completes, during the capability/channel negotiation phase. NLA authenticates the client to the server; it does nothing to authenticate the server’s PDU content to the client.
  5. MCS Connect-Initial / Connect-Response (T.125 domain setup). The server sends GCC Conference Create Response containing TS_UD_SC_CORE (Server Core Data), TS_UD_SC_SEC (Server Security Data), and TS_UD_SC_NET (Server Network Data, which maps virtual channel IDs).
  6. MCS Erect Domain / Attach User / Channel Join sequence.
  7. Capability Exchange via TS_DEMAND_ACTIVE_PDU (server to client) containing an array of TS_CAPABILITY_SET structures. Each has a capabilitySetType (UINT16), lengthCapability (UINT16), and a variable-length payload. This is a historically rich attack surface.
  8. Virtual Channel Data begins flowing via MCS Send Data Indication (SDin) PDUs. Static channels like RDPDR (device redirection), CLIPRDR (clipboard), RDPSND (audio), and dynamic virtual channels like RDPGFX (graphics pipeline) all carry their own sub-protocol PDUs.

The attacker controls the server. That means every server-to-client PDU, from the X.224 Confirm onward, is attacker-controlled data. The capability sets, the virtual channel initialization responses, the GCC conference data, the certificate presented during TLS. All of it.

For Wireshark analysis, the key display filters are:

rdp                           # All RDP traffic
t125.ConnectMCSPDU            # MCS connection phase
rdp.serverCoreData            # TS_UD_SC_CORE fields
rdp.capabilitySetType         # Individual capability set types
tpkt                          # TPKT framing layer

In a lab capture against a rogue server, the malicious payload typically appears as an anomalously large or malformed TS_CAPABILITY_SET within the demand-active PDU, or as a crafted virtual-channel initialization response during the channel-join phase. The lengthCapability field is worth watching closely: a value that exceeds the actual PDU remaining bytes, or that conflicts with the channel’s expected structure, is a strong signal of exploitation attempts.

Flowchart of RDP client connection phases from TCP handshake through capability exchange, highlighting where the CVE-2026-42985 UAF is triggered after NLA authentication
NLA completes at phase four; the UAF fires in phase seven during capability exchange – well past the authentication layer.

Root Cause: The Use-After-Free in mstscax.dll

Microsoft has not published the specific internal symbol names or code paths for CVE-2026-42985. What we know with certainty from the advisory is: CWE-416, use-after-free, in the Remote Desktop Client, triggered by a malicious server during connection. The affected binary is mstscax.dll, the ActiveX control that hosts all client-side RDP protocol logic and is loaded by mstsc.exe via COM.

Based on the CWE classification, the attack vector, and historical precedent in this codebase (CVE-2021-38666, a UAF in RDPDR channel handling, followed a nearly identical pattern), the class-level vulnerability lifecycle looks like this:

Phase 1: Allocation. During the capability exchange or virtual channel initialization phase, the client parses a server-supplied PDU and allocates a heap object to represent a protocol structure. This could be a channel context object, a capability descriptor, a codec configuration, or a device redirection structure. The object is allocated via HeapAlloc (or indirectly through new / malloc) on the default process heap.

Phase 2: Premature Free. A subsequent server-crafted PDU triggers a code path that frees this object earlier than the client’s state machine expects. This might be an error-handling path (a malformed PDU causes cleanup), an explicit teardown command from the server, or a state transition that the parser does not correctly sequence. The pointer to the object is not nulled after the free. It becomes a dangling pointer.

Phase 3: Stale Dereference. A third server PDU causes the client to dereference the dangling pointer. If the freed memory has been reclaimed and filled with attacker-controlled content (via heap spray, covered below), the dereference reads attacker data. If the object contained a vtable pointer or a function pointer, the indirect call now jumps to an attacker-chosen address.

For binary diffing to locate the exact patched functions, the standard approach is:

# Using BinDiff or Diaphora against pre-patch and post-patch mstscax.dll
# Pre-patch: mstscax.dll from May 2026 cumulative update
# Post-patch: mstscax.dll from June 2026 cumulative update
python diaphora.py pre_patch_mstscax.sqlite post_patch_mstscax.sqlite

Look for functions with partial matches (0.7-0.95 similarity ratio) that contain added null-pointer checks, added reference counting, or restructured free/cleanup sequences. The patch for a UAF almost always takes one of three forms: nulling the pointer after free, adding a reference count to prevent premature free, or restructuring the object lifecycle so the free cannot happen while references remain live.

Flow diagram showing the three-phase use-after-free lifecycle: object allocation, premature free with dangling pointer, heap spray reclaim, and stale dereference leading to code execution
The attacker orchestrates all three phases by controlling the sequence of server-sent PDUs across a single RDP connection.

Heap Internals: NT Heap, LFH, and Grooming Strategy

Exploiting a UAF requires controlling what occupies the freed memory slot before the stale dereference occurs. On modern Windows, this means understanding the NT Heap’s Low Fragmentation Heap (LFH) allocator.

The NT Heap operates through two tiers. The BackEnd allocator is the default and handles general allocations using freelists with FLink/BLink pointers in free chunks. The FrontEnd allocator (LFH) activates for a given size bucket after 18 consecutive allocations of similar size from that bucket. LFH handles allocations up to approximately 16KB (0x4000 bytes). Anything larger bypasses LFH entirely.

LFH organizes chunks into UserBlocks structures: contiguous pages sliced into equal-sized slots for a given bucket. The key property for exploitation is that LFH reuses freed slots within the same UserBlocks slab with relatively limited randomization (though Windows 10+ added RtlpLowFragHeapRandomData to introduce some entropy into slot selection).

The grooming strategy for CVE-2026-42985 follows this pattern:

Step 1: Ensure LFH is active for the target allocation size.
        Send 18+ PDUs that each trigger a same-sized allocation
        in mstscax.dll's heap. This activates LFH for that bucket.

Step 2: Trigger the premature free (Phase 2 of the UAF).
        The target object is freed but the pointer remains live.

Step 3: Spray replacement objects into the freed slot.
        Send PDUs that allocate objects of the same size,
        containing attacker-controlled data. One of these
        allocations will land in the freed slot.

Step 4: Trigger the stale dereference (Phase 3 of the UAF).
        The client reads from the now-reclaimed memory,
        interpreting attacker data as the original object's fields.

In WinDbg, the heap state around the corruption is observable:

# Attach to mstsc.exe before initiating connection
windbg -pn mstsc.exe

# Enable page heap for detailed tracking (pre-connection)
!gflag +hpa

# After triggering the free, inspect the freed chunk:
!heap -p -a <freed_object_address>
# Output will show: "free'd allocation" with call stack

# Dump the heap entry header:
dt ntdll!_HEAP_ENTRY <chunk_address>
#   Size, Flags, SmallTagIndex, PreviousSize

# Check which heap and front-end type:
!heap -s
# Look for FrontEndHeapType: 2 (LFH) on the relevant heap

# Set a hardware breakpoint on the freed address to catch reuse:
ba w8 <freed_object_address>
# When spray data lands, this breaks. Examine the new content.

# Set a read breakpoint to catch the stale dereference:
ba r8 <freed_object_address>
# When the dangling pointer is dereferenced, this breaks.
# At this point, examine the call stack and register state.

With page heap enabled (+hpa), the freed allocation is immediately detectable because the heap manager places guard pages around freed blocks. In production exploitation (without page heap), the attacker’s spray data silently occupies the slot, and the dereference proceeds with corrupted data.

Control-Flow Hijack: From Corrupted Object to Code Execution

If the freed object contained a vtable pointer (common in C++ objects within mstscax.dll), the spray replaces it with a pointer to attacker-controlled memory. When the stale dereference invokes a virtual function, the CPU follows the fake vtable to an attacker-chosen address.

On modern Windows, Control Flow Guard (CFG) is the primary mitigation against this. CFG maintains a bitmap of valid indirect call targets. When an indirect call instruction executes, the runtime checks the target address against the bitmap before transferring control. If mstscax.dll is compiled with CFG (it is on Windows 11 and Server 2022+), then a raw vtable overwrite pointing to arbitrary shellcode will be caught and the process will terminate.

This does not make the bug unexploitable. It raises the bar. The attacker needs either:

  1. A target address that is already in the CFG bitmap (a legitimate function that, when called with controlled arguments, yields a useful primitive, such as a stack pivot).
  2. A CFG bypass, which is a separate vulnerability class. Several have been published against specific Windows builds.
  3. An information leak to defeat ASLR, allowing the attacker to locate gadgets within loaded modules.

For lab demonstration purposes with CFG disabled (debug builds or older Windows versions), the primitive is clean:

; x64 proof-of-concept shellcode (lab only, demonstrates RIP control)
; Pops calc.exe via WinExec
section .text
global _start
_start:
    xor  rcx, rcx
    push rcx
    mov  rcx, 0x6578652E636C6163  ; "calc.exe" reversed
    push rcx
    mov  rcx, rsp                  ; lpCmdLine -> "calc.exe"
    xor  rdx, rdx
    inc  rdx                       ; uCmdShow = SW_SHOWNORMAL
    ; Resolve WinExec address via PEB walk (omitted for brevity)
    call rax                       ; WinExec("calc.exe", 1)

The execution runs at the privilege level of the connecting user, which for sysadmins RDP’ing into servers is frequently a domain admin. No privilege escalation necessary in that scenario.

The End-to-End Rogue-Server Attack Chain

Putting the pieces together into a realistic attack scenario:

Delivery. The attacker sends a spearphishing email containing a .rdp file. The file specifies the attacker’s server IP (or a domain that resolves to it). The .rdp file format supports full address:s:attacker.example.com:3389 and can suppress certificate warnings with authentication level:i:0. Alternatively, the attacker compromises DNS or ARP to redirect a legitimate RDP hostname to their rogue server (more complex, but no user interaction beyond the connection they were already going to make).

Connection. The victim double-clicks the .rdp file. mstsc.exe loads mstscax.dll and initiates the connection. X.224 negotiation completes. TLS handshake completes (the attacker presents a self-signed certificate; if the victim clicks through the warning, the connection proceeds). NLA/CredSSP completes if enforced (the victim authenticates, which the attacker happily accepts).

Trigger. During capability exchange, the rogue server sends a crafted PDU sequence: first a normal-looking capability set that allocates the target object, then a malformed PDU that triggers the premature free, then a spray of same-sized PDUs filling the freed slot with a fake vtable, then a final PDU that triggers the virtual function call through the stale pointer.

Execution. The RDP client process now executes attacker-controlled code. The attacker’s payload establishes a reverse shell, loads a C2 implant into mstsc.exe‘s memory space, or spawns a child process.

Post-exploitation. From the compromised workstation, the attacker pivots. If the user is a domain admin, lsass.exe contains cached credentials for every domain they have authenticated to. The attacker dumps credentials, moves laterally via SMB or WinRM, and establishes persistence. The initial compromise of the RDP client was the beachhead; the real damage is lateral.

Illustration of a rogue server masquerading as a legitimate endpoint, luring an unsuspecting user through a network cable into a trap
The rogue-server attack inverts the classic RDP threat model: the victim initiates the connection, and the server strikes back.

Detection and Defense

Sigma Rules for Outbound RDP Anomalies

The most immediate detection opportunity is anomalous outbound RDP connections from mstsc.exe:

title: Outbound RDP Connection to Non-Internal Host
id: 7a3f8b21-4e92-4d1a-b5c3-9f2e8a1d6b4c
status: experimental
description: >
  Detects mstsc.exe connecting outbound to a host outside
  RFC 1918 address space. May indicate connection to a rogue server.
logsource:
  product: windows
  category: network_connection
detection:
  selection:
    EventID: 3
    Image|endswith: '\mstsc.exe'
    DestinationPort: 3389
  filter_internal:
    DestinationIp|cidr:
      - '10.0.0.0/8'
      - '172.16.0.0/12'
      - '192.168.0.0/16'
  condition: selection and not filter_internal
level: medium
tags:
  - attack.initial_access
  - attack.t1021.001

Post-exploitation indicators deserve a separate, higher-severity rule:

title: mstsc.exe Spawning Shell or Script Interpreter
id: 9c4e2d17-8f3a-4b6e-a1d5-3c7f9e2b8a4d
status: experimental
logsource:
  product: windows
  category: process_creation
detection:
  selection:
    EventID: 1
    ParentImage|endswith: '\mstsc.exe'
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\rundll32.exe'
      - '\mshta.exe'
      - '\wscript.exe'
  condition: selection
level: critical
tags:
  - attack.execution
  - attack.t1059

ETW and Event Log Telemetry

The Microsoft-Windows-TerminalServices-RDPClient/Operational log provides Event ID 1024 every time the client initiates a connection, including the destination IP. Forward this to your SIEM and alert on connections to IPs outside your known RDP destination allowlist.

For deeper protocol-level telemetry, the ETW provider Microsoft-Windows-TerminalServices-ClientActiveXCore ({28AA95BB-D444-4719-A36F-40462168127E}) emits events from within mstscax.dll itself, including channel setup events. The Microsoft-Windows-RemoteDesktopServices-RdpCoreTS provider ({1139C61B-B549-4251-8ED3-27250A1EDEC8}) covers transport-layer events.

Sysmon Event ID 3 (Network Connection) with Image filtering on mstsc.exe is the workhorse detection. Event ID 7 (Image Loaded) catches unexpected DLLs loaded into the mstsc process post-exploitation. Event ID 10 (Process Access) with SourceImage as mstsc.exe and TargetImage as lsass.exe is a critical-severity indicator that should never fire in normal operations.

MITRE ATT&CK Mapping

TechniqueIDRole in This Chain
Spearphishing AttachmentT1566.001Delivery of malicious .rdp file
Exploitation for Client ExecutionT1203CVE-2026-42985 UAF trigger
Remote Desktop ProtocolT1021.001Attack vector (outbound RDP)
OS Credential Dumping: LSASS MemoryT1003.001Post-exploitation pivot
Command and Scripting InterpreterT1059Post-exploitation shell spawning

Hardening Priorities

Patch. The June 2026 cumulative update addresses all seven CVEs in this cluster. There is no workaround that fully mitigates the UAF without the patch.

Block outbound RDP at the perimeter for any endpoint that does not have a documented business need to connect to external RDP servers. This is the single highest-impact preventive control. Most organizations have no reason for workstations to RDP to anything outside their own network.

Restrict .rdp file execution. Use AppLocker or WDAC to block .rdp file launches from user-writable paths (Downloads, Temp, email attachment caches). This cuts the primary phishing delivery path.

Enforce server authentication. Set AuthenticationLevelOverride to 2 in HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client to require valid server certificates. This forces a hard failure on self-signed certificates rather than a bypassable warning dialog.

Restrict mstsc.exe launch. If only a subset of users need RDP client access, use AppLocker to restrict execution to those groups. There is no reason a finance analyst’s workstation needs to be able to launch mstsc.exe.

Enable CFG system-wide and verify that mstscax.dll is compiled with CFG on your deployed Windows builds. On fully patched Windows 11 and Server 2022+, it is. On older builds, check with dumpbin /headers mstscax.dll | findstr "Guard".

Illustration of a firewall gate blocking outbound RDP traffic, cutting the attack chain before it can reach a rogue server
Blocking outbound TCP/3389 at the perimeter is the single highest-leverage control – it severs the attack chain at step one.

Key Takeaways

  • CVE-2026-42985 is CWE-416 (use-after-free), not a heap overflow. The distinction matters for root-cause analysis and detection engineering. The heap-overflow siblings are CVE-2026-47289, CVE-2026-42992, and CVE-2026-44799.
  • The attack is client-side. The victim connects to the attacker’s server, not the other way around. This inverts the typical RDP threat model and makes .rdp file phishing the primary delivery mechanism.
  • NLA does not help here. The UAF triggers after authentication, during capability and channel negotiation. NLA authenticates the client to the server; it does nothing to validate server-supplied protocol data.
  • Outbound RDP firewall rules are the highest-leverage preventive control. If your endpoints cannot reach arbitrary TCP/3389 destinations, the attack chain breaks at step one.
  • Five patches to mstscax.dll in a single Patch Tuesday is a signal. This codebase received serious scrutiny. Assume that skilled researchers are still looking at it, and that similar bugs may surface in future cycles. Invest in detection and access control now, not just patching.

Related Tutorials