CVE-2025-21297 — RD Gateway (aaedge.dll) Singleton Race → Use-After-Free RCE
Last updated: 2026-07-02
Severity: Critical (CVSS 8.1)
Component:aaedge.dll— Windows Remote Desktop Gateway (RD Gateway) service
Bug Class: Race Condition (unsynchronised singleton init) → reference-count corruption → Use-After-Free
Privilege: Remote, pre-auth → RCE in the RD Gateway service context
Patch: January 2025 Patch Tuesday — mutex added around singleton initialisation
Related: Use After Free, Race Conditions, CVE-2024-38148 (Schannel UAF), Windows RPC
Vulnerability Summary
A remote, unauthenticated use-after-free in the Windows Remote Desktop Gateway service (aaedge.dll). The root cause is an unsynchronised “lazy singleton” initialisation of a global instance pointer, CTsgMsgServer::m_pMsgSvrInstance. Multiple concurrent client connections can race the initialiser, corrupting the singleton’s reference count and leaving a dangling pointer that a later connection dereferences — a virtual-call UAF that is exploitable for remote code execution.
Affected Code Path
Client connect (HandShakeRequest / TunnelRequest)
→ CTsgMsgServer::GetCTsgMsgServerInstance() // lazy singleton getter
a1: m_pMsgSvrInstance = new CTsgMsgServer(...) // assign to GLOBAL before init/refcount settle
a2: return m_pMsgSvrInstance // returns the GLOBAL, not a local
Key function: CTsgMsgServer::GetCTsgMsgServerInstance in aaedge.dll.
Affected global: CTsgMsgServer::m_pMsgSvrInstance.
Root Cause Analysis
The getter follows the classic broken double-checked-locking pattern without any lock:
- Null-check the global
m_pMsgSvrInstancewith no synchronisation. - Heap-allocate a
CTsgMsgServerinstance. - Assign the pointer to the global (position
a1). - Return the value read back from the global (position
a2), not from a local variable.
Because both the store and the return read the shared global, two threads that pass the null-check simultaneously each allocate their own instance and stomp the global in turn. The reference count kept on the instances no longer matches the number of live users:
- Socket 1 and Socket 2 both enter before the global is set; each allocates a separate block.
- Socket 1 writes its block to the global first.
- Socket 2 immediately overwrites the global with its own block. Socket 1’s block is now unreferenced by the global but Socket 1 still holds/uses it; Socket 2’s block has
ref = 1while two threads increment/decrement it. - The mismatched refcount lets a block be freed while a pointer to it is still live.
- Socket 3 connects later and dereferences the dangling pointer (virtual method call) → UAF.
The heap crash confirms it: address …bfb4f90 found in free-ed allocation, faulting on a virtual dispatch through the freed object.
Exploitation Technique
RD Gateway is internet-facing (RPC-over-HTTP / the TSGU tunnelling protocol), and the initialisation path is reached pre-authentication via the handshake/tunnel-setup messages. The PoC opens many concurrent sockets sending HandShakeRequest and TunnelRequest messages, tightly synchronised to widen the race window during CTsgMsgServerInstance initialisation, then uses a third connection to operate on the freed object. Controlling the contents of the reclaimed allocation (heap grooming) turns the dangling virtual-call into control of execution — remote code execution in the gateway service.
Patch Analysis
Microsoft added a mutex around the singleton initialiser so only one thread can run the allocate-and-publish sequence at a time; concurrent callers block and then observe the fully-initialised global. This is the standard fix for the broken-lazy-init class and removes the refcount race entirely.
Variant Notes
The bug is a textbook instance of “lazy singleton without a lock” — a pattern worth hunting across other Get*Instance getters in the same and sibling services (any if (!g_x) g_x = new …; return g_x; reachable from multiple threads). See Race Conditions.
References
- VictorV (@V-V), “Windows Remote Desktop Gateway (RD Gateway) CVE-2025-21297 的介绍”, v-v.space, 2025-05-15 — https://v-v.space/2025/05/15/CVE-2025-21297/
- MSRC Advisory: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-21297
