LiteLLM's MCP authentication handler could turn a rejected credential into an anonymous session with access to configured tooling. The CVE record published July 8, 2026 rates CVE-2026-59822 High, with a CVSS 4.0 score of 8.8. The failure occurs after key validation: an exception handler catches the rejection and permits OAuth2 passthrough without checking whether the target server uses OAuth2.
The upstream advisory published June 30, 2026 credits yaaras as the reporter. This writeup traces the vulnerable source and its patch; it does not claim an independent exploit reproduction.
The validator rejects the credential. The fallback makes that rejection nonterminal.
The bug
One header enters both paths
lowAuthorization supplies the candidate LiteLLM key and also marks the request as
eligible for the OAuth2 branch.
A rejected key becomes an anonymous session
mediumWithout an explicit x-litellm-api-key, a 401 or 403 from user_api_key_auth()
can be replaced with an empty UserAPIKeyAuth().
The target does not constrain the fallback
highThe handler does not require the target to use auth_type=oauth2 before accepting
this anonymous passthrough context.
Shared server permissions can survive
highOutside toolset scope, the server-permission calculation includes servers with
allow_all_keys: true, even when the session has no key or team grants.
This chain follows the header parsing and authentication branches, then the server-permission calculation, at the parent of the April 30, 2026 fix.
Why the fallback needs its own authorization condition
An upstream OAuth2 token may be opaque to LiteLLM. Forwarding that token can be a legitimate request even when it fails LiteLLM's own key check. The missing condition is whether the selected upstream server is configured to accept that kind of credential.
In this handler, the authentication error selected the permissive path by itself. A failed key check did not establish anything about the target server. The patch restores that connection by consulting the operator's server configuration before allowing passthrough.
That distinction matters when reviewing an authentication exception handler: identify the fact that authorizes continuation after failure, then trace where that fact is checked. Here, the target's authentication mode was the required fact, and the vulnerable branch never consulted it.
Where
litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py, in MCPRequestHandler.process_mcp_request:
The following is the vulnerable HTTPException branch, with its source indentation normalized:
elif oauth2_headers:
# No x-litellm-api-key, but Authorization header present.
# Could be a LiteLLM key (backward compat) OR an OAuth2 token
# from an upstream MCP provider (e.g. Atlassian).
# Try LiteLLM auth first; on auth failure, treat as OAuth2 passthrough.
try:
validated_user_api_key_auth = await user_api_key_auth(
api_key=litellm_api_key, request=request
)
except HTTPException as e:
if e.status_code in (401, 403):
verbose_logger.debug(
"MCP OAuth2: Authorization header is not a valid LiteLLM key, "
"treating as OAuth2 token passthrough"
)
validated_user_api_key_auth = UserAPIKeyAuth()
else:
raise
The assignment to UserAPIKeyAuth() discards the failed validation. The adjacent ProxyException branch applies the same rule to 401 and 403 codes.
The second bypass in the same handler
The patch also changes public-route detection in the same handler. The advisory describes the OAuth2 fallback; this is a related change visible in the patch. Public-route detection read:
if ".well-known" in str(request.url): # public routes
validated_user_api_key_auth = UserAPIKeyAuth()
That is a substring test against the entire URL, not the path. The marker can be carried in a query string, in a hostname, or in a deeper path segment, and any MCP route becomes a public route. It now tests request.url.path.startswith("/.well-known/").
Both branches can produce an empty authentication context. They require separate guards: one on the public path, the other on the target server configuration.
Impact depends on the server-permission path
In the inspected manager implementation, get_allowed_mcp_servers() combines key, team, and end-user permissions. An empty session supplies no such grants. Outside toolset scope, the function also adds server IDs marked allow_all_keys: true. That union explains how an anonymous context can still produce a nonempty permission result.
This is a result from the inspected code path, not proof that every listed tool can be called in every deployment. Toolset scoping can omit the union; IP filtering and upstream authentication can restrict access further. With no allow-all entries and no other grants, this permission calculation yields an empty list. What connected data or actions become available depends on the subsequent checks and the configured tools.
Affected and patched
The June 30, 2026 advisory identifies versions below 1.84.0 as affected. Version 1.84.0 contains the fix.
The fix PR merged April 30, and the patched release shipped May 14, 2026. CISA's catalog entry records September 2, 2026 as the addition date and September 16 as its due date. The catalog establishes known exploitation; it does not establish when exploitation began.
The fix
The comparison below abbreviates the exception branches to show the added target check. The full patch, merged April 30, 2026, includes logging and target resolution.
-except HTTPException as e:- if e.status_code in (401, 403):+except (HTTPException, ProxyException) as e:+ status = e.status_code if isinstance(e, HTTPException) else int(e.code)+ if status in (401, 403) and MCPRequestHandler._target_servers_use_oauth2(...): validated_user_api_key_auth = UserAPIKeyAuth() else: raiseThe fallback now runs only when every server the request targets is operator-configured for auth_type=oauth2. For anything else the original authentication error propagates, as it did before the passthrough feature existed. Target resolution prefers the x-mcp-servers header, including the explicitly-empty case, and falls back to parsing the standard transport URL patterns. Routes matching neither form fail closed.
The vendor's June 30, 2026 guidance is to upgrade to 1.84.0 or later. If you cannot upgrade immediately, disable MCP routes or block /mcp/ and related MCP endpoints at the reverse proxy or API gateway. When assessing your exposure, trace which configured servers the anonymous permission path could select and which checks remain before a tool call reaches them.
