This is a report on behalf of Julien Lair, Security Researcher at Quarkslab (quarkslab.com)
Remote code execution on the RDP client, when chained with the memory leak from Vuln2. Without that leak the same bug is a denial of service (the client crashes). The attacker is the RDP server the victim connects to. No credentials are needed.
FreeRDP, the whole 2.x and 3.x line. We confirmed the bug by reading the source at 2.0.0, 2.11.7, 3.0.0, 3.5.0, 3.10.0, 3.24.2, the latest release 3.28.0 and current master.
Remmina 1.4.x is affected through the system libfreerdp it links (it ships no RDP code of its own).
Other clients that link a libfreerdp client are very likely affected too (GNOME Connections, KRDC, Apache Guacamole guacd). We did not verify those by version.
FreeRDP 993499447 (unmodified client xfreerdp 3.27.2-dev0 , on Ubuntu 26.04 with glibc 2.43-2ubuntu2), ASLR + NX on: RCE ( id = root).
FreeRDP 3.24.2 and Remmina 1.4.40 (its libfreerdp): leak + DoS confirmed dynamically.
Technical description
The bug is reached in five steps. The line numbers below are from the tested build 993499447 (same build as the PoC). The overflow itself, steps 3 and 4, is byte for byte identical from 2.0.0 to current master ( a6dad4b66 ). Nothing on the code path bounds the length of the attacker-controlled field before it is copied into a fixed 512-byte stream, so a malicious server gets a direct heap overflow whose contents it fully controls.
Step 1: the server sends the redirection cookie
A malicious server sends a Server Redirection PDU whose LB_LOAD_BALANCE_INFO field is fully attacker controlled. The client reads the field (raw bytes plus a server-chosen length), performs no validation on its content, and stores it verbatim. The field is opaque, and the even notes it need not be NUL terminated:
The server controls every byte and the full length, and those bytes travel unchanged to the overflow in step 4.
Step 2: on reconnect the cookie becomes the RoutingToken
The redirection makes the client reconnect, and on the new connection it hands the stored cookie to the negotiation layer:
Step 3: verbatim copy into nego, no length check
The copy is exact and no upper bound is enforced.
Stream_Write copies straight into the buffer at the cursor with no capacity check (nothing calls Stream_EnsureRemainingCapacity first). 501 bytes are free after the header, so any RoutingToken longer than that writes past the end of the 512 byte allocation. The bytes written are the attacker's, from step 3.
Step 5: from overflow to code execution
The chunk right after the negotiation buffer is, deterministically (cliprdr allocation order), the clipboard request_table, a wHashTable. It is present when clipboard file redirection (FUSE) is compiled in, which is the default on Linux desktop builds. At session teardown:
HashTable_Free walks the buckets and calls each entry's free callback. By overwriting the wHashTable fields (numOfBuckets, bucketArray, fnObjectFree) through the overflow, the attacker redirects that call to system(). The address could be obtained by leveraging another vuln (reported separately) to defeat ASLR.
The overflow in nego.c has never been bounded and is present on current master. The correct fix is to bound RoutingTokenLength (or size the stream dynamically) in nego.c, so an oversized cookie can never write past the 512 byte buffer.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
