LiteLLM Proxy Unauthenticated Auth Bypass via Host-Header Route Confusion (CVE-2026-49468)
by BiiTts (Caio Fabrício) · 2026-07-05
- Severity
- Critical
- CVE
- CVE-2026-49468
- Category
- web
- Affected product
- LiteLLM (BerriAI) proxy
- Affected versions
- < 1.84.0 (verified by the source repo on v1.83.14-stable); fixed in 1.84.0
- Disclosed
- 2026-07-05
- Patch status
- patched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-07 |
| Author / Researcher | BiiTts (Caio Fabrício) |
| CVE / Advisory | CVE-2026-49468 |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.8 (CVSS 3.1, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, per NVD as stated in source); 9.5 (CVSS 4.0 per GitHub advisory, as stated in source) |
| Status | PoC |
| Tags | litellm, llm-proxy, auth-bypass, host-header, route-confusion, cwe-290, fastapi, starlette, python |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | LiteLLM (BerriAI) proxy |
| Versions Affected | < 1.84.0 (verified by the source repo on v1.83.14-stable); fixed in 1.84.0 |
| Language / Platform | Python (FastAPI / Starlette ASGI application) |
| Authentication Required | No (pre-auth; bypasses authentication entirely) |
| Network Access Required | Yes |
Summary
exploit.py demonstrates a pre-authentication bypass in the LiteLLM proxy caused by a single crafted Host header (Host: evil/?). LiteLLM’s get_request_route() derives the route used for auth decisions from request.url.path, which Starlette reconstructs from the client-controlled Host header rather than from the real ASGI routing path. Injecting /? into the Host header pushes the real request path into the URL’s query component, making get_request_route() return / — a public, unauthenticated health-check route — while FastAPI still dispatches the request to the real, normally-protected handler. This lets an unauthenticated attacker mint valid API keys, create users, run inference, and read configuration/spend data on any endpoint whose only protection is this route-based check.
Vulnerability Details
Root Cause
litellm/proxy/auth/auth_utils.py::get_request_route() computes the route used by both the authentication builder and the authorization wrapper in user_api_key_auth.py from request.url.path. Starlette’s URL object rebuilds this string as f"{scheme}://{host_header}{path}" using the raw, attacker-supplied Host header, then re-parses it with urlsplit(...).path. FastAPI’s actual routing, however, dispatches on request.scope["path"] (the true ASGI path). A Host header of evil/? causes the reconstructed URL to become http://evil/?/<real-path>, whose urlsplit().path collapses to /. Since "/" is in LiteLLMRoutes.public_routes, both the authn check (user_api_key_auth.py ~L688) and the authz check (~L1642) short-circuit as if the request were hitting a public route, while the real handler for the real path still executes.
Attack Vector
- Attacker sends any HTTP request to the LiteLLM proxy for a protected endpoint (e.g.
POST /key/generate), but sets theHostheader to the literal stringevil/?instead of the real proxy host. - Starlette reconstructs
request.urlusing that Host header, causingget_request_route()to return/regardless of the real request path. - LiteLLM’s auth builder sees route
/inpublic_routesand returnsUserAPIKeyAuth(user_role=INTERNAL_USER_VIEW_ONLY)without requiring any API key. - LiteLLM’s authorization wrapper also sees route
/inpublic_routesand returns early, skippingcommon_checksand admin-route enforcement. - FastAPI still routes and executes the real handler for the real path (
/key/generate,/user/new,/chat/completions,/spend/logs, etc.), serving the request fully unauthenticated. - Endpoints that additionally perform an inline
PROXY_ADMINrole check (e.g./config/update,/model/new,/user/list, MCP direct-create) remain blocked, since the bypass only grantsINTERNAL_USER_VIEW_ONLY, notPROXY_ADMIN.
Impact
Complete unauthenticated compromise of any LiteLLM proxy endpoint gated solely by the route-based auth check: minting persistent, fully-functional virtual API keys (/key/generate), creating users (/user/new), unauthenticated LLM inference causing provider cost abuse (/chat/completions), and disclosure of configuration/spend/telemetry data (/spend/logs, /settings, /get/config/callbacks). Per the author’s analysis, this does not chain directly to remote code execution or full proxy-admin takeover on the tested version, since admin-gated endpoints (including MCP server creation and role elevation) still enforce an inline PROXY_ADMIN check that this bypass does not satisfy.
Environment / Lab Setup
Target: LiteLLM proxy v1.83.14-stable (vulnerable) and v1.84.0 (patched), both with LITELLM_MASTER_KEY auth enabled, run side-by-side via lab/docker-compose.yml on ports 4000 (vulnerable) and 4001 (patched), backed by PostgreSQL
Attacker: Python 3 stdlib (http.client) — exploit.py sets the Host header verbatim at the wire level via skip_host=True
Proof of Concept
PoC Script
See
exploit.pyin this folder (lab config/compose inlab/).
| |
exploit.py sends a GET /user/list both with and without the spoofed Host: evil/? header to confirm the bypass (401 vs. non-401), then offers mint-key, user, chat, dump, and raw subcommands that all use the same Host-header trick to reach protected endpoints without any API key.
Detection & Indicators of Compromise
alert http any any -> any any (msg:"CVE-2026-49468 LiteLLM Host route-confusion bypass";
flow:to_server,established; http.host; pcre:"/[\/?#]/";
classtype:web-application-attack; sid:2026049468; rev:1;)
Signs of compromise:
- Any request reaching the LiteLLM proxy whose
Hostheader contains/,?, or# - New virtual API keys created (
/key/generate) or users created (/user/new) with no corresponding authenticated admin session in logs - Inference/spend logs showing activity attributed to
INTERNAL_USER_VIEW_ONLYwith no legitimate API key presented
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to LiteLLM 1.84.0 or later, where get_request_route() reads request.scope["path"]/scope["root_path"] directly instead of reconstructing the path from the Host header |
| Interim mitigation | Place the proxy behind a reverse proxy that enforces strict Host header validation (reject any Host value containing /, ?, or #), and ensure a master_key is always configured |
References
Notes
Mirrored from https://github.com/BiiTts/CVE-2026-49468-LiteLLM-Auth-Bypass on 2026-07-05.
| |