Jan 31, 2026·6 min read·12 visits
Critical RCE (CVSS 9.8) in Windows Remote Desktop Licensing Service via Heap Overflow. Unauthenticated, Zero-Click, SYSTEM privileges. Exploit involves sending malformed Base64 data to RPC Opnum 49.
MadLicense (CVE-2024-38077) is a textbook example of why legacy code in obscure Windows services is a goldmine for attackers. It is a critical Heap-based Buffer Overflow residing in the Windows Remote Desktop Licensing Service, a component often enabled by default on servers handling Terminal Services. With a CVSS score of 9.8, it allows unauthenticated attackers to execute arbitrary code as NT AUTHORITY\SYSTEM simply by sending a malformed RPC packet. No user interaction, no authentication credentials, just pure, unadulterated remote root access. It affects a massive range of Windows Server versions, from 2000-era relics up to Server 2025.
In the grand theater of Windows exploitation, we usually focus on the stars: SMB, RDP (BlueKeep), or Exchange. But lurking in the background are the stagehands—boring, administrative services that run with the highest privileges imaginable. Enter the Windows Remote Desktop Licensing Service (TermServLicensing).
Its job is mundane: managing Client Access Licenses (CALs) for RDP connections. It’s the bureaucrat of the operating system, counting heads and checking paperwork. Because it needs to manage system-wide licenses, it runs as NT AUTHORITY\SYSTEM. That is the absolute highest privilege level on a Windows machine—higher than Administrator. If you own this service, you own the box, the domain credentials stored in memory, and arguably the entire network segment.
Here’s the kicker: this service exposes an RPC interface over a named pipe (\pipe\TermServLicensing). Historically, this pipe was accessible anonymously. While modern Windows versions have tightened up named pipe permissions, many enterprise configurations (or legacy domains) still leave these pipes exposed to the network. An attacker doesn't need a password; they just need to ask the Licensing Service a question it doesn't know how to answer.
The vulnerability (CVE-2024-38077) is a classic Heap-based Buffer Overflow located in a function named CDataCoding::DecodeData(). As the name suggests, this function is responsible for decoding data—specifically, Base64 encoded strings passed via RPC messages. This feels like a vulnerability from the late 90s that somehow survived two decades of code review.
The root cause is an integer handling error during the calculation of the required buffer size. When the service receives a Base64 string, it tries to calculate how much heap memory it needs to allocate to store the decoded binary output. In a proper implementation, you calculate the size, allocate the buffer, and then decode.
However, MadLicense fails to account for specific malformed input lengths correctly. It allocates a buffer that is too small for the resulting decoded data. When the decoding loop runs, it happily writes past the end of the allocated chunk. This isn't a stack overflow where we just smash a return address; this is a heap overflow. We are corrupting the dynamic memory of the application, which makes exploitation slightly more artistic but equally deadly.
Let's look at the logic flow (simplified for clarity) to understand why the heap creates a crater. The vulnerable code path is triggered via the RPC method TLSRpcTelephoneRegisterLKP (Opnum 49).
// Pseudocode of the vulnerable logic
void CDataCoding::DecodeData(char* inputBase64, int inputLen) {
// 1. Calculate size.
// The flaw: This calculation underestimates size for specific
// malformed Base64 inputs with padding anomalies.
int allocSize = CalculateDecodedSize(inputLen);
// 2. Allocate Heap Memory
BYTE* buffer = (BYTE*)HeapAlloc(GetProcessHeap(), 0, allocSize);
// 3. Decode
// The decoding routine doesn't strictly check bounds against allocSize
// It trusts the math from step 1.
Base64Decode(inputBase64, inputLen, buffer);
}The CalculateDecodedSize function likely assumed standard Base64 padding rules. By providing a string that violates these assumptions (or by leveraging integer overflow in the size calculation itself depending on the specific patch version), the allocSize ends up being smaller than the actual bytes written by Base64Decode.
When Base64Decode writes to buffer, it continues writing past the end of the allocated chunk. In the Windows Heap Manager, the data immediately following your chunk usually contains Heap Metadata (headers that tell Windows where the next free chunk is). By overwriting this metadata, an attacker can trigger a "write-what-where" condition when the heap manager tries to coalesce or free chunks later. This allows the attacker to overwrite function pointers (like Virtual Function Tables) elsewhere in the application memory.
To exploit this, we don't need a complex phishing campaign. We just need network access to the RPC port (usually 135) or SMB (445). The attack chain is embarrassingly straightforward for a vulnerability this critical.
First, we establish a DCE/RPC connection to the TermServLicensing pipe. We send a Bind request to the interface UUID. Once bound, we call TLSRpcConnect (Opnum 1) to get a context handle. This basically tells the server, "Hi, I'm a client, please allocate some memory for our session."
Next, we invoke TLSRpcTelephoneRegisterLKP (Opnum 49). This function expects a "telephone number" or license key pack identifier, but it accepts a variable-length character array. We pass a massive, malformed Base64 string designed to trigger the miscalculation described above.
> [!NOTE] > Heap Grooming is Key: In a real-world exploit, we wouldn't just send the overflow packet immediately. We would first spray the heap with other RPC calls to arrange memory chunks in a predictable pattern. We want our vulnerable chunk to sit right next to a C++ object with a Virtual Table (vtable).
When the overflow occurs, we overwrite the vtable pointer of the adjacent object. The next time the application tries to call a method on that object (e.g., Object->Destructor()), it instead jumps to an address we control—typically a ROP (Return-Oriented Programming) chain that disables Data Execution Prevention (DEP) and executes our shellcode.
The impact cannot be overstated. We are talking about Remote Code Execution as SYSTEM. This is the "God Mode" of Windows. If an attacker lands this on a Domain Controller (where this service is often running), they can dump the NTDS.dit file, extract every password hash in the domain, and create a Golden Ticket.
Even on a standalone server, the attacker has total control. They can install ransomware, install persistent backdoors, or use the machine as a pivot point to attack the rest of the internal network. Because the exploit happens in memory and creates no files on disk (initially), it can bypass traditional antivirus signatures.
With an EPSS score hovering around 87%, the automated bots are already sniffing for this. The only thing stopping widespread wormable chaos is that many firewalls block RPC ports from the internet. However, inside a corporate network, those ports are almost always open between servers.
Microsoft released patches in July 2024. If you haven't applied them, stop reading this and go do it. The patch modifies CDataCoding::DecodeData to correctly validate the buffer size against the maximum possible decoded length and adds strict bounds checking during the copy operation.
Immediate Mitigation Strategy:
TermServLicensing service immediately.This vulnerability is a stark reminder: unused services are unmanaged risks. Turn them off.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Windows Server 2022 Microsoft | < 10.0.20348.2582 | 10.0.20348.2582 |
Windows Server 2019 Microsoft | < 10.0.17763.6054 | 10.0.17763.6054 |
Windows Server 2016 Microsoft | < 10.0.14393.7159 | 10.0.14393.7159 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2024-38077 |
| Type | Heap-based Buffer Overflow |
| CVSS v3.1 | 9.8 (Critical) |
| EPSS Score | 87.07% (99.42nd Percentile) |
| Attack Vector | Network (RPC over SMB/TCP) |
| Privileges Required | None |
| Impact | RCE as SYSTEM |
A heap-based buffer overflow occurs when a product calculates a buffer size incorrectly, allocating a heap chunk that is smaller than the data written to it.