{"schema":"threatline.record/2","record":{"id":"GHSA-pvgq-84w2-93ph","aliases":["GHSA-pvgq-84w2-93ph"],"summary":"Windows cliprdr FILECONTENTS_SIZE response over-read","details":"## Executive Summary\r\n\r\nFreeRDP'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.\r\n\r\nI 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.\r\n\r\nI 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.\r\n\r\n## Background\r\n\r\nThe 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.\r\n\r\nThe 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.\r\n\r\nThe 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`.\r\n\r\n## Vulnerability Details\r\n\r\nThe 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`):\r\n\r\n```c\r\nif (cliprdr_send_request_filecontents(clipboard, (void*)instance,\r\n                                      instance->m_lIndex, FILECONTENTS_SIZE, 0,\r\n                                      8) == CHANNEL_RC_OK)\r\n{\r\n\tsuccess = TRUE;\r\n}\r\n\r\ninstance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);\r\nfree(clipboard->req_fdata);\r\n```\r\n\r\nThe 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:\r\n\r\n```c\r\nif (nreq != clipboard->req_fsize)\r\n\trc = ERROR_INVALID_DATA;\r\nreturn rc;\r\n```\r\n\r\nThe real response handler (`wf_cliprdr_server_file_contents_response` in the same file) trusts the peer-controlled length when it stores the response:\r\n\r\n```c\r\nclipboard->req_fsize = fileContentsResponse->cbRequested;\r\nclipboard->req_fdata = (char*)malloc(fileContentsResponse->cbRequested);\r\nCopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,\r\n           fileContentsResponse->cbRequested);\r\n```\r\n\r\nFor 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.\r\n\r\nThe 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.\r\n\r\nThe 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.\r\n\r\n## Exploitability Analysis\r\n\r\nThe 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.\r\n\r\nThe 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.\r\n\r\nThe 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.\r\n\r\n## Proof of Concept\r\n\r\nThe `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.\r\n\r\nThe 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.\r\n\r\nFrom the report directory, inspect the source-only reproducer with:\r\n\r\n```sh\r\ncd poc\r\nsed -n '1,220p' short_filecontents_size.c\r\n```\r\n\r\nThe 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.\r\n\r\n## Remediation\r\n\r\nThe 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`.\r\n\r\nA source-compatible shape for the fix is:\r\n\r\n```c\r\nconst UINT rc = cliprdr_send_request_filecontents(clipboard, (void*)instance,\r\n                                                  instance->m_lIndex, FILECONTENTS_SIZE, 0,\r\n                                                  sizeof(UINT64));\r\nif ((rc == CHANNEL_RC_OK) && (clipboard->req_fsize == sizeof(UINT64)) &&\r\n    clipboard->req_fdata)\r\n{\r\n\tinstance->m_lSize.QuadPart = *((LONGLONG*)clipboard->req_fdata);\r\n\tsuccess = TRUE;\r\n}\r\nfree(clipboard->req_fdata);\r\nclipboard->req_fdata = nullptr;\r\n```\r\n\r\nThe 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.\r\n\r\nNo 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.\r\n\r\n## Summary\r\n\r\nA 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.\r\n\r\nThe 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.","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-pvgq-84w2-93ph","source_modified_at":"2026-10-01T11:16:02Z","fetched_at":"2026-10-23T19:58:35.668311Z","package":{"ecosystem":"c","name":"FreeRDP"},"range":">= 3.30.0, <= 3.31.1"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-pvgq-84w2-93ph","source_modified_at":"2026-10-01T11:16:02Z","fetched_at":"2026-10-23T19:58:35.668311Z","package":{"ecosystem":"c","name":"FreeRDP"},"version":"3.32.0"}],"cvss":5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-01T11:16:02Z","modified_at":"2026-10-01T11:16:02Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-pvgq-84w2-93ph","fetched_at":"2026-10-23T19:58:35.668311Z","source_modified_at":"2026-10-01T11:16:02Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-01T11:16:02Z","content_hash":"876add3ab20a13b5652f5709ff24fddfdb20204b0ba7943244a00952378dffda"}}
