Skip to content
FreeRDP GitHub Security Advisory

FreeRDP GitHub Security Advisory

github.com September 19, 2026

A malicious RD Gateway aborts any FreeRDP client that uses the RPC-over-HTTP transport, with a 62-byte BIND_ACK, before a session exists. rts_read_result() validates two bytes and then reads four, and p_result_list.n_results is a byte the gateway supplies, so a gateway that announces one result and ends the fragment two bytes into it makes the parser read past the fragment it just received. On a default build the second read trips WINPR_ASSERT(Stream_GetRemainingLength(_s) >= typesize) inside stream_read_u16_le() , and winpr_int_assert() is WINPR_NORETURN , so the process aborts — 5 of 5 runs against a stock Release build over a real TLS connection. With verbose assertions disabled it is a two-byte over-read instead.

libfreerdp/core/gateway/rts.c . The wire form of p_result_t is result (2) + reason (2) + transfer_syntax (uuid 16 + version 4). The check covers the first field only; transfer_syntax is separately checked by rts_read_syntax_id() , so the gap is exactly and only the four-for-two in the middle. The loop count comes from the wire. ( // = max_recv_frag rejected), not measured; it points down , and it is the reason this is reported as a denial of service rather than as a heap overflow. The gateway here is ours; nothing shown here suggests a real RD Gateway product can be made to emit this PDU. Finally, this is not rts_read_auth_verifier : CVE-2026-33952 covers the auth_length assertion at rts.c:282 , which is fixed and which we do not reach; we searched all 100 published FreeRDP advisories full-text for rts_read_result , rts_read_result_list , p_result_list and n_results and found no match.

CVSS 3.1 (preferred): CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H — 6.5, Medium . UI:R carries it: the victim has to be induced to connect through the attacker's gateway.

CVSS 4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N — 7.1, High ; v4.0 bands availability-only network defects a step higher. We keep v3.1 preferred so this lines up with CVE-2026-33952 , which you graded Medium for the same shape in the same file.

CWE-617 (Reachable Assertion) in the default configuration, reached via CWE-1284 (Improper Validation of Specified Quantity in Input), and CWE-125 (Out-of-bounds Read) where verbose assertions are compiled out. Suggested primary: CWE-617 .

Found with an LLM-driven harness generator (afb) built on your own in-tree target TestFuzzCoreClient , extended to call the gateway parsers directly. libfreerdp/core/CMakeLists.txt:156 appends the gateway sources unconditionally, so they are linked into the 10 of your 14 in-tree fuzz targets that link freerdp and driven by none of them: each drives only what a default-initialised context can call, and OSS-Fuzz's coverage report has rts.c at 0 of 1868 lines. Everything used above is in this report.

Appendix: the two scripts

Builds the PDU and speaks enough ncacn_http to deliver it. Three details were measured rather than guessed, and each one silently produces "does not reproduce" if you get it wrong: the WebSocket attempt must be closed without an HTTP status (a 503 makes rdg_establish_data_connection fail the whole connect instead of falling back to RPC); both channels need a syntactically valid NTLM CHALLENGE, since ncacn_http.c hardcodes NTLM_SSP_NAME and has no anonymous path; and the OUT channel's 200 OK must not be sent until the IN channel has authenticated, or the client is in neither handled state and parses HTTP/1.1 200 OK as an RPC PDU.

freerdp_connect() through the gateway, nothing else. Any client routed through the gateway with the RPC transport reaches the same code; this exists because the build above has no display client. This exact listing was built and run against the build above: pov aborts 5 of 5, ctrl-empty and ctrl-full exit cleanly 3 of 3 each.