All vulnerabilities
GHSA-pvgq-84w2-93ph

FreeRDP: Windows cliprdr FILECONTENTS_SIZE response over-read

FreeRDP
CVSS
5.0Medium
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed in3.32.0Fixed versionGitHub
Published1 Oct 2026Updated 1 Oct 2026GitHub

What fixes it

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Executive Summary

FreeRDP's Windows clipboard client can read eight bytes from a shorter server response while creating an IStream for a remote file. Mallory must operate a server to which a Windows FreeRDP client connects, offer a clipboard file, and have the user paste or otherwise open that remote file. Mallory's server can answer the client's FILECONTENTS_SIZE request with a response whose cbRequested is one, two, or four instead of the required eight. The response handler allocates that shorter buffer, but CliprdrStream_New still dereferences it as a LONGLONG. This crosses the response-length boundary into a heap out-of-bounds read and can terminate the client or leave an invalid stream size. It is not a session-theft or authentication bypass, and the evidence does not establish code execution.

I verified the vulnerable operation in the released 3.30.0, 3.31.0, and 3.31.1 source snapshots and in the assessed 3.31.x source snapshot. Those are the earliest releases I inspected, not a claimed first affected version, and I did not establish a complete affected range or a fixed release. The source history contains a July 2026 response-size check, but the caller in the assessed source still dereferences req_fdata after that check reports an error, so that change is not treated as a fix for this finding.

I reviewed the exact source and observed the unmodified Windows client path under Wine 9.0 with a guard-page allocator. The server response was injected at the real cliprdr response-handler boundary; the RDP/cliprdr transport was stubbed, so this report does not claim a full network-transport end-to-end run. Opensec Intelligence should receive credit for the report.

Background

The Windows FreeRDP client supports remote file transfer through the cliprdr channel. When a file-list descriptor lacks FD_FILESIZE, CliprdrStream_New requests the file size before exposing the remote item as an OLE IStream. The request uses the FILECONTENTS_SIZE flag, zero offset, and an eight-byte response size.

The protocol's ordinary behaviour is explicit in channels/cliprdr/cliprdr_common.c: a size request must use cbRequested == sizeof(UINT64), and the returned file size is a 64-bit unsigned integer. A normal server therefore returns eight data bytes. The relevant trust boundary is the remote server's response data into the Windows client's heap. The user interaction is necessary because the client creates the remote stream while the user pastes or opens the remote clipboard file.

The Windows-only path is distinct from the common/FUSE file handler. The latter checks that a size response is exactly sizeof(UINT64) before consuming it, but that sibling check does not protect client/Windows/wf_cliprdr.c.

Vulnerability Details

The trigger is a valid file-list clipboard exchange followed by a FILECONTENTS_SIZE response with a short payload. In the assessed source, CliprdrStream_New sends the request as follows (client/Windows/wf_cliprdr.c, CliprdrStream_New):

if (cliprdr_send_request_filecontents(clipboard, (void*)instance,
                                      instance->m_lIndex, FILECONTENTS_SIZE, 0,
                                      8) == CHANNEL_RC_OK)
{
	success = TRUE;
}

instance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);
free(clipboard->req_fdata);

The return value is used only to set success; the dereference occurs regardless of that value. In the assessed source, cliprdr_send_request_filecontents compares the requested eight bytes with the response length and returns ERROR_INVALID_DATA when they differ, but it does not clear or enlarge the response buffer before returning:

if (nreq != clipboard->req_fsize)
	rc = ERROR_INVALID_DATA;
return rc;

The real response handler (wf_cliprdr_server_file_contents_response in the same file) trusts the peer-controlled length when it stores the response:

clipboard->req_fsize = fileContentsResponse->cbRequested;
clipboard->req_fdata = (char*)malloc(fileContentsResponse->cbRequested);
CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
           fileContentsResponse->cbRequested);

For a one-byte response, the handler therefore allocates one byte and copies one byte. CliprdrStream_New then reads eight bytes from that allocation at the LONGLONG assignment. The first byte is within the object; the remaining seven bytes are outside it. The code frees the same buffer only after the invalid read, so cleanup does not prevent the fault.

The preserved runtime harness drove the unmodified CliprdrStream_New, the real request path, and the real response handler. Its control response allocated eight bytes and produced m_lSize = 0x1122334455667788 without a fault. Its one-byte response placed the allocation immediately before a no-access page, after which Wine reported an unhandled page fault on the first byte of that guard page at the eight-byte read. This confirms the source-level length mismatch and the sink reached by the short response. The harness injected the response at the handler boundary; it did not exercise a live RDP socket or prove that every server/client connection reaches this exchange.

The same dereference is present in the 3.30.0, 3.31.0, and 3.31.1 release snapshots inspected. The earliest historical line for this operation predates those releases, but I did not inspect enough release snapshots to name a first affected release. No later release in the available history provided a caller-side check that prevents the dereference, so no fixed release is asserted.

Exploitability Analysis

The demonstrated primitive is a bounded heap out-of-bounds read of up to seven bytes when the response is shorter than eight bytes. The attacker is a malicious RDP server or other remote peer controlling the cliprdr response; the victim must connect to that server and perform the user action that causes the remote file stream to be created. No client-side privilege is needed beyond that normal connection and user interaction.

The guard-page run demonstrates a client crash when the short object ends at an unmapped page. Ordinary Windows heap allocation may place usable bytes after a one-byte allocation, so a crash is allocator- and layout-dependent; that does not make the read valid. The observed eight-byte control rules out a harness-wide failure in the stream construction path, while the short-response run reaches the fault only after the real response handler allocates the one-byte object. The evidence shows an invalid size value or a crash, but not a reliable information disclosure, arbitrary write, code execution, or privilege escalation.

The proof used a controlled response ordering inside the handler callback. It does not establish a race, a protocol authentication weakness, or reliable exploitation against a production network transport. The required configuration is the Windows client with remote file clipboard support active; the user paste/open action is the meaningful interaction condition.

Proof of Concept

The poc/short_filecontents_size.c file is a source-only reproducer sketch based on the observed response-handler boundary. It documents the exact malicious value and the vulnerable operation without embedding a local checkout path or pretending to be a standalone network client. It must be adapted to a FreeRDP source build if used for authorized testing; it is not a complete transport implementation.

The preserved validation used a Windows cross-build of the unmodified client code and Wine 9.0. The control invocation supplied eight response bytes and completed normally. The malicious invocation supplied one response byte and faulted at the guard page on the eight-byte LONGLONG read. Those observations came from the supplied run records; no crash output beyond that run is claimed here.

From the report directory, inspect the source-only reproducer with:

cd poc
sed -n '1,220p' short_filecontents_size.c

The sketch is intentionally non-destructive in its distributable form. An adapted executable can crash the test client when a guard page is used, so it should only be run against a disposable test process. It does not alter files, credentials, or server state.

Remediation

The Windows client must validate the response length before reading the size. The check must occur on the path that consumes req_fdata, not only in a helper whose error code the caller ignores. On any response other than exactly sizeof(UINT64) bytes for FILECONTENTS_SIZE, the client should free and clear the response buffer, reject the stream creation, and avoid assigning m_lSize.

A source-compatible shape for the fix is:

const UINT rc = cliprdr_send_request_filecontents(clipboard, (void*)instance,
                                                  instance->m_lIndex, FILECONTENTS_SIZE, 0,
                                                  sizeof(UINT64));
if ((rc == CHANNEL_RC_OK) && (clipboard->req_fsize == sizeof(UINT64)) &&
    clipboard->req_fdata)
{
	instance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);
	success = TRUE;
}
free(clipboard->req_fdata);
clipboard->req_fdata = nullptr;

The exact cleanup should follow the surrounding Windows client ownership conventions. A regression test should invoke the real response handler with eight bytes and with one-, two-, and four-byte responses, assert that the short cases fail without a size read, and retain a guard-page or sanitizer test so a future caller cannot reintroduce this length mismatch. The common/FUSE validation is a useful pattern, but the Windows caller needs its own check.

No fixed release is identified by this report. The July 2026 helper change that returns ERROR_INVALID_DATA for a size mismatch should be completed with caller-side rejection before release notes can be treated as a remediation for this issue.

Summary

A malicious RDP server can return a short FILECONTENTS_SIZE response while a Windows FreeRDP user creates a remote-file stream. The Windows response handler allocates the peer-supplied length, while CliprdrStream_New unconditionally reads eight bytes. The result is a demonstrated bounded heap out-of-bounds read and a guard-page client crash under Wine; a reliable disclosure or code-execution chain was not demonstrated.

The vulnerable code was verified in 3.30.0, 3.31.0, 3.31.1, and the assessed 3.31.x source snapshot. These are inspected versions, not a definitive affected range, and no fixed release was verified. The most useful remaining validation is a caller-side fix plus regression coverage through the real Windows cliprdr response handler, followed by a release snapshot check.

Text from GitHub Advisory Database

Affected versions 1

  • FreeRDP, c>= 3.30.0, <= 3.31.1GitHub