ANT-2026-TAV704VS · freerdp/freerdp

rce high

CVE-2026-64624 GHSA-rq8f-9xjh-pr3m

Severity Claude high · Security research firm high · Maintainer -

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.

ANT-2026-TAV704VS: .rdp file lines injected as CLI options enabling command execution

freerdp_client_parse_rdp_file_buffer_int() at client/common/file.c:938 appends any .rdp line beginning with '/' to file->args, which is later passed wholesale to freerdp_client_settings_parse_command_line() (file.c:~2605). This exposes every CLI switch to the .rdp author, including /rdp2tcp:, which causes channels/rdp2tcp/client/rdp2tcp_main.c:97-108 to call CreateProcessA() on the attacker-controlled string during VirtualChannelEntryEx — before any TCP/TLS handshake. The rdp2tcp channel is built by default and argv[1] ending in .rdp is auto-parsed, so a victim merely opening an emailed .rdp file gets one-click arbitrary command execution. Additional injectable primitives include /cert:ignore, /drive:root,/ and /action-script.

Target

Project: freerdp/freerdp
Location: client/common/file.c:938
Discovery: static analysis — not yet dynamically reproduced

Technical Details

The .rdp parser falls back to treating any unmatched '/'-prefixed line as a raw argv option with no allowlist, collapsing the trust boundary between an externally-delivered interchange file and local command-line configuration. Because /rdp2tcp: is a valid CLI option whose value is handed straight to CreateProcessA()/fork+execve during pre-connect channel load, the .rdp author gains arbitrary local command execution with no server-side or TLS gating.

Reproduction

  1. file.c:938-943 appends any '/'-prefixed .rdp line verbatim to file->args via freerdp_client_add_option()
  2. file.c:2600-2610 passes file->args to freerdp_client_settings_parse_command_line() — the full CLI parser
  3. cmdline.c:5574-5577 accepts /rdp2tcp: and stores it in FreeRDP_RDP2TCPArgs
  4. cmdline.c:6386-6402 adds and loads the rdp2tcp static channel when FreeRDP_RDP2TCPArgs is set
  5. freerdp.c:135 → utils_reload_channels() → LoadChannels runs BEFORE rdp_client_connect() (pre-TLS)
  6. client.c:1548 invokes the channel's VirtualChannelEntryEx during load
  7. rdp2tcp_main.c:310 → init_external_addin() → rdp2tcp_main.c:97-108 passes the attacker string directly to CreateProcessA()
  8. On POSIX, winpr process.c:216,313 performs fork()+execve() on it

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Suggested Fix

Do not interpret unrecognised .rdp content as command-line switches; restrict .rdp parsing strictly to the documented Microsoft key:type:value grammar and ignore all other lines (or apply an explicit allowlist of safe options).

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-TAV704VS.


Reference: ANT-2026-TAV704VS
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics. The writeup below is the document the firm sent to the maintainer.

Verdict
true positive
Severity
high

Summary

FreeRDP's .rdp connection-file parser treats any line beginning with / as a raw command-line option and appends it verbatim to the argument vector later handed to the full CLI parser (freerdp_client_settings_parse_command_line). The documented .rdp grammar is strictly name:type:value (the Microsoft interchange format); this /-prefixed fallback is not documented in the FreeRDP man page or user manual — which show CLI options only after the file on the command line (xfreerdp connection.rdp /p:… /f) — and it silently exposes the entire xfreerdp option surface to whoever authored the file.

The concrete consequences, in rough order of how hard they are to dismiss as "intended," are:

Note on FreeRDP reacting to this

Compatibility

With 3.28.0 the option has been disabled by default, use -DWITH_EMBEDDED_CLI_IN_RDP_FILES=ON to enable this behavior again.

UPSTREAM FIX

The change that resolved this finding.

diff --git a/client/common/CMakeLists.txt b/client/common/CMakeLists.txt
index 79b2700cf3d5..8b0952ec2c1f 100644
--- a/client/common/CMakeLists.txt
+++ b/client/common/CMakeLists.txt
@@ -50,6 +50,11 @@ else()
   set(OPT_FUSE_DEFAULT OFF)
 endif()
 
+option(WITH_EMBEDDED_CLI_IN_RDP_FILES "[dangrous] allow embedded cli arguments in rdp files" OFF)
+if(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+  add_compile_definitions(WITH_EMBEDDED_CLI_IN_RDP_FILES)
+endif()
+
 option(WITH_FUSE "Build clipboard with FUSE file copy support" ${OPT_FUSE_DEFAULT})
 if(WITH_FUSE)
   find_package(PkgConfig REQUIRED)
diff --git a/client/common/file.c b/client/common/file.c
index 91c170796788..8936dee45f36 100644
--- a/client/common/file.c
+++ b/client/common/file.c
@@ -27,6 +27,8 @@
 #include <winpr/file.h>
 #include <winpr/cast.h>
 
+#include <freerdp/utils/warnings.h>
+
 #include <freerdp/client.h>
 #include <freerdp/client/file.h>
 #include <freerdp/client/cmdline.h>
@@ -879,13 +881,17 @@ static BOOL parse_line(rdpFile* file, char* line, size_t length, rdp_file_fkt_pa
 
 	const char* beg = line;
 #if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
+#if defined(WITH_EMBEDDED_CLI_IN_RDP_FILES)
 	if (beg[0] == '/')
 	{
+		freerdp_warn_deprecated(WLog_Get(TAG), "Parsing CLI options within an RDP file",
+		                        "Will be removed in FreeRDP 4.0");
 		if (!freerdp_client_add_option(file, line))
 			return FALSE;
 
 		return TRUE; /* FreeRDP option */
 	}
+#endif
 #endif
 
 	char* d1 = strchr(line, ':');

https://github.com/FreeRDP/FreeRDP/commit/22c5deea52404f51a13276b3abda44e1e60704cf

TIMELINE

Recorded dates, in order.

  1. 2026-04-02 Discovered or logged
  2. 2026-07-06 Patch released
  3. 2026-07-22 Sent to maintainer
  4. 2026-07-22 Maintainer acknowledged
  5. 2026-09-28 Publicly revealed
PROVENANCE

SHA-3-512 hash:

9414feb034ec0b96d72acb97d9a689482815eddf76e8bb5ca1ffec484ed7f1332f728b0de81285e8cf7fe93b84d128b420fb8b71e4832301886d0ca16c5e14f6

Committed 2026-07-22 07:29 UTC

Revealed 2026-09-28 21:46 UTC

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-TAV704VS",
  "bug_class": "Argument Injection / Remote Code Execution",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-04-16T01:52:40+00:00",
  "description": "freerdp_client_parse_rdp_file_buffer_int() at client/common/file.c:938 appends any .rdp line beginning with '/' to file->args, which is later passed wholesale to freerdp_client_settings_parse_command_line() (file.c:~2605). This exposes every CLI switch to the .rdp author, including /rdp2tcp:<cmd>, which causes channels/rdp2tcp/client/rdp2tcp_main.c:97-108 to call CreateProcessA() on the attacker-controlled string during VirtualChannelEntryEx — before any TCP/TLS handshake. The rdp2tcp channel is built by default and argv[1] ending in .rdp is auto-parsed, so a victim merely opening an emailed .rdp file gets one-click arbitrary command execution. Additional injectable primitives include /cert:ignore, /drive:root,/ and /action-script.",
  "discovered_at": "2026-04-02T00:00:00+00:00",
  "location": "client/common/file.c:938",
  "poc_sha256": null,
  "preimage_version": 1,
  "project": "freerdp/freerdp",
  "reproduction": [
    "1. file.c:938-943 appends any '/'-prefixed .rdp line verbatim to file->args via freerdp_client_add_option()",
    "2. file.c:2600-2610 passes file->args to freerdp_client_settings_parse_command_line() — the full CLI parser",
    "3. cmdline.c:5574-5577 accepts /rdp2tcp:<value> and stores it in FreeRDP_RDP2TCPArgs",
    "4. cmdline.c:6386-6402 adds and loads the rdp2tcp static channel when FreeRDP_RDP2TCPArgs is set",
    "5. freerdp.c:135 → utils_reload_channels() → LoadChannels runs BEFORE rdp_client_connect() (pre-TLS)",
    "6. client.c:1548 invokes the channel's VirtualChannelEntryEx during load",
    "7. rdp2tcp_main.c:310 → init_external_addin() → rdp2tcp_main.c:97-108 passes the attacker string directly to CreateProcessA()",
    "8. On POSIX, winpr process.c:216,313 performs fork()+execve() on it"
  ],
  "technical_details": "The .rdp parser falls back to treating any unmatched '/'-prefixed line as a raw argv option with no allowlist, collapsing the trust boundary between an externally-delivered interchange file and local command-line configuration. Because /rdp2tcp: is a valid CLI option whose value is handed straight to CreateProcessA()/fork+execve during pre-connect channel load, the .rdp author gains arbitrary local command execution with no server-side or TLS gating.",
  "title": ".rdp file lines injected as CLI options enabling command execution",
  "vendor_severity": "high"
}