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
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
- file.c:938-943 appends any '/'-prefixed .rdp line verbatim to file->args via freerdp_client_add_option()
- file.c:2600-2610 passes file->args to freerdp_client_settings_parse_command_line() — the full CLI parser
- cmdline.c:5574-5577 accepts /rdp2tcp: and stores it in FreeRDP_RDP2TCPArgs
- cmdline.c:6386-6402 adds and loads the rdp2tcp static channel when FreeRDP_RDP2TCPArgs is set
- freerdp.c:135 → utils_reload_channels() → LoadChannels runs BEFORE rdp_client_connect() (pre-TLS)
- client.c:1548 invokes the channel's VirtualChannelEntryEx during load
- rdp2tcp_main.c:310 → init_external_addin() → rdp2tcp_main.c:97-108 passes the attacker string directly to CreateProcessA()
- 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
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:
/cert:ignore— silent TLS-validation bypass. A.rdpfile can disable certificate checking with no prompt, so a victim connecting to a man-in-the-middled or spoofed server gets no warning. There is no legitimate reason an interchange file should be able to silently weaken the client's transport security./drive:...— local filesystem exposure. A.rdpfile can mount the user's local directories into the (attacker-controlled) remote session./rdp2tcp:<command>— local command execution. Therdp2tcpchannel is designed to launch a helper process, so its argument goes straight toCreateProcessA()(POSIX:fork()+execve()). Because channel loading happens insidefreerdp_connect()before the TCP/TLS handshake, merely opening the file runs an attacker-named local command — no server, no certificate, no authentication, even if no RDP server exists. This is the most severe outcome but also the one most easily read as "that option is meant to spawn a process"; the report is really about the file → full-CLI exposure that makes it (and the two above) reachable from an untrusted file. The/rdp2tcpvariant additionally requires the channel to be compiled in (OFF in the bare upstream cmake default, but ON in Debian/Ubuntu packaging); the/cert:ignoreand/drivevariants are present in every client build.
Note on FreeRDP reacting to this
-
Deprecation of arbitrary
CLIparsing: The behavior where /-prefixed lines in a.rdpfile are forwarded to theCLIparser will be disabled by default in the next (=3.28.0) release and is officially marked for complete removal in a future major version.FreeRDPalready supports the /args-from parameter for users who explicitly intend to load command-line arguments from a local file (orstdin). -
Untrusted paradigm for
.rdpfiles: Because.rdpfiles are widespread, standard and expected default options must continue to work. However, we recognize that even standard properties (such as drive redirection drivestoredirect) can be used maliciously to expose local data. Unless a.rdpfile is signed and verified via a trusted chain, it must be treated as untrusted. For future versions, we will be discussing a model where only a "safe default set" of options is allowed automatically, while risky or potentially problematic options will require an explicit confirmation (such as a specific command-line flag provided by the user). Signature verification isn't implemented yet. -
/rdp2tcp: The ability for a
.rdpfile to spawn a helper process via /rdp2tcp is a possible risk. With the argument parsing disabled this should be mitigated. We will be adding a build-time notice regarding this behavior and are planning a future architecture rewrite. On a long term our goal is to deprecate the external binary requirement entirely and move toward an internal tunneling mechanism (similar to SSH -L and -R port forwarding). We've already add a deprecation warning for the next release.
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.
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
Recorded dates, in order.
- 2026-04-02 Discovered or logged
- 2026-07-06 Patch released
- 2026-07-22 Sent to maintainer
- 2026-07-22 Maintainer acknowledged
- 2026-09-28 Publicly revealed
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"
}