All posts
Unproven Execution

LiteLLM MCP Test Endpoints: Preview Equals Execute

LiteLLM's MCP test endpoints accept and run a full stdio server config — unauthenticated RCE via Starlette BadHost, CISA KEV listed June 9, 2026

Securityv0 Intelligence Team OWASP: ASI05 sv0 finding: unproven_execution
litellm mcp cve-2026-42271 unproven-execution kev asi05

The Incident

On 2026-05-08, BerriAI shipped litellm 1.83.7 to patch CVE-2026-42271 (CVSS 8.7), a command-injection flaw in the gateway’s MCP test-preview endpoints. The two endpoints — POST /mcp-rest/test/connection and POST /mcp-rest/test/tools/list — accept a full MCP stdio server configuration in the request body, including the command, args, and env fields used by the stdio transport, and execute that configuration to preview the server before saving. Affected versions span 1.74.2 through 1.83.6.

On 2026-06-01, Horizon3.ai disclosed unauthenticated in-the-wild exploitation. Attackers chained CVE-2026-42271 with CVE-2026-48710 — a Starlette host-header validation bypass dubbed “BadHost” — to reach the test endpoints without authentication and achieve remote code execution on the LiteLLM gateway host. CISA added CVE-2026-42271 to its Known Exploited Vulnerabilities catalog on 2026-06-09, citing evidence of active exploitation.

MITRE ATT&CK coverage: T1059 (Command and Scripting Interpreter), T1190 (Exploit Public-Facing Application), T1078.004 (Valid Accounts: Cloud Accounts).

The Authority Path That Failed

The identity carrying execution authority at the moment of failure is the LiteLLM gateway process — a Python service that operators deploy to route LLM traffic across providers (OpenAI, Anthropic, Cohere, and others). The scope it held is the union of whatever the operator granted it to do its job: read provider API keys from its environment or secret store, write logs, dial upstream LLM endpoints, and — crucially for this finding — fork child processes for MCP stdio transports it has been configured to use. The scope it exercised at the moment of failure is arbitrary command execution driven by the body of an HTTP request: the command, args, and env fields of an attacker-supplied MCP server config went straight into the stdio transport’s process spawn, with no allowlist of permitted binaries and no operator approval gate between “request received” and “process spawned.”

Two trust anchors failed in sequence. The first was the authentication layer: Starlette’s host-header check (CVE-2026-48710) could be bypassed with a crafted Host header, so requests that should have been rejected at the perimeter reached the test endpoints unauthenticated. The second — and this is the SV0-relevant anchor — was a preview-equals-execute design choice. The endpoints assume that any caller is an authorized operator configuring a trusted MCP server; there is no separation between “describe a server config” and “spawn a process from that config.” The gap between held scope (route LLM traffic) and exercised scope (run attacker-chosen argv) was inspectable in the source before Horizon3 found it in the wild: the test handlers wire an HTTP endpoint directly into subprocess-style execution with attacker-controlled argv and environment.

SecurityV0 Perspective

This fits unproven_execution / ASI05. The defect is not the Starlette bypass alone — that just determined who could reach the endpoint. The durable defect is the design choice to make a “test this MCP server” endpoint that spawns the server from request input. Operators deployed LiteLLM as an LLM router; they did not opt in to a gateway that spawns arbitrary processes from request bodies. The same product surface appeared in our March 2026 LiteLLM coverage with a different authority failure — a PyPI supply-chain compromise mapped to nhi_compromise — and the pattern of preview-or-test endpoints that execute the config extends across the broader category of AI-gateway and agent-builder products.

The evidence pack for this finding would enumerate, per LiteLLM deployment in the environment: the resolved set of MCP-related HTTP routes the gateway exposes, the source-field schema each route accepts (which routes accept command/args/env or other shell-bound input), the package and image hashes of every running gateway instance, the host-process identity under which the gateway runs (container user, mounted credential paths, and any cloud-role binding), and the network exposure of the management plane (publicly reachable, peer-reachable, or operator-only). Before exfiltration, that pack answers a specific question: which gateway routes would execute attacker-supplied argv if reached? After the fact, it answers the forensic question: which gateway instances in the affected 1.74.2–1.83.6 range served a POST to /mcp-rest/test/connection or /mcp-rest/test/tools/list during the exposure window, with what payload, and what child processes did the gateway spawn next?

What To Do

  • Upgrade litellm to 1.83.7 or later and pin the exact version. Move every gateway off the affected 1.74.2–1.83.6 range immediately; 1.83.7 tightens authorization on the MCP test endpoints. Pin the exact version in your image manifest and lock files — do not rely on a floating tag to pull the fix the next time you redeploy.
  • Pull the Starlette dependency forward in lockstep. CVE-2026-48710 is the auth bypass that turned an authenticated-user command-injection bug into unauthenticated RCE. Upgrade starlette to 1.0.1 or later across every FastAPI- and Starlette-based service in your AI stack, not just LiteLLM — any service that uses host-header allowlisting as an authentication boundary inherits the same bypass.
  • Close the management plane to the public internet. A LiteLLM admin API is not an end-user surface. Bind the gateway’s management routes to a private network or VPN reach, and require mTLS or a service-mesh identity in front of /mcp-rest/*. An exposed /mcp-rest/test/connection is the configuration that turned a logic bug into a KEV-listed incident.
  • Inventory every gateway route that accepts argv-shaped input. Walk the LiteLLM router definitions and produce a list of every HTTP handler whose request body contains command, args, env, cmd, entrypoint, or comparable subprocess inputs. Each such handler is an unproven-execution candidate until the operator can name the signed authorization that lets that route spawn a process.
  • Alert on outbound child processes spawned by the gateway PID. A LiteLLM container that forks /bin/sh, python -c, node -e, or anything outside its MCP allowlist is the signal CVE-2026-42271 generates. Feed gateway process trees into EDR with the LiteLLM container identity attached; treat any unknown child binary under the gateway parent PID as a Sev 1.

Sources