# The LiteLLM MCP auth bypass, and the one request that shows it

> Source: <https://dev.to/mcpscore/the-litellm-mcp-auth-bypass-and-the-one-request-that-shows-it-42bo>
> Published: 2026-10-28 21:59:01+00:00

*Originally published on [mcpscore.dev](https://mcpscore.dev/blog/litellm-mcp-auth-bypass-invalid-token).*

On 2 September 2026, CISA added CVE-2026-59822 to its [Known Exploited Vulnerabilities](https://www.cisa.gov/known-exploited-vulnerabilities-catalog?search_api_fulltext=CVE-2026-59822) catalog. LiteLLM’s MCP endpoint let anyone in who sent a bearer token, and any token would do. The flaw is in LiteLLM’s code, but the symptom is one any MCP server can have, and one request from outside shows it. mcpscore 1.25.0 now sends that request.

LiteLLM is a gateway: it sits between applications and model providers, and between agents and MCP servers. Its MCP endpoint checks the `Authorization` header against LiteLLM’s own API keys. Some upstream MCP servers, Atlassian among them, expect their own OAuth token in that same header, so LiteLLM added a fallback: if the header is not a LiteLLM key, treat it as a token for the upstream server and pass it through.

The fallback ran for every server, not only the ones configured for OAuth. In outline, per the [advisory](https://github.com/BerriAI/litellm/security/advisories/GHSA-7488-6r32-c95q):

```
# The shape of the bug, simplified from the advisory
try:
    identity = await check_litellm_key(authorization)
except AuthError:
    # Maybe it is an OAuth token for an upstream server.
    identity = AnonymousUser()  # every server, not only OAuth ones
```

A failed key check turned into an anonymous session, so a made-up header could list and call every tool behind the gateway and reach the services connected through it. The [fix in 1.84.0](https://github.com/BerriAI/litellm/commit/73869f0faf7d11ee21adcb5f91b8c33a340b6c2c) runs the fallback only when every server the request targets is configured for OAuth, and fails closed when the target is unknown.

Two more LiteLLM CVEs made the security news in October, and the widely shared advice was to upgrade to at least 1.83.9. That advice is right for those two and leaves the MCP bypass open: CVE-2026-59822 affects every release before 1.84.0, so 1.83.9 through 1.83.14 are still exposed.

**Recent LiteLLM CVEs, the release that fixes each, and whether it involves MCP**

| CVE | Flaw | Fixed in | CISA KEV | MCP | 
|---|---|---|---|---|
| [CVE-2026-59822](https://github.com/BerriAI/litellm/security/advisories/GHSA-7488-6r32-c95q) | MCP endpoint accepts any bearer token | 1.84.0 | Yes | Yes | 
| [CVE-2026-59823](https://github.com/BerriAI/litellm/security/advisories/GHSA-hx8v-g79f-8w5f) | Server-side request forgery via user_config | 1.83.9 | No | No | 
| [CVE-2026-35029](https://github.com/BerriAI/litellm/security/advisories/GHSA-53mr-6c8q-9789) | Privilege escalation via the config endpoint | 1.83.0 | No | No | 

The two newer CVEs are not about MCP, and no outside audit can see them. The third is the one CISA has evidence of attackers using, and its symptom is visible from outside.

The MCP authorization spec has said the same thing since 2025-06-18, in [Token Handling](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#token-handling), and the 2026-07-28 release keeps it word for word:

Invalid or expired tokens MUST receive a HTTP 401 response.

A server that serves a request carrying a token it never issued has not just answered with the wrong status code. Its auth check did not run, or ran and was ignored. Either way, the cause is inside the server, but the result shows on the wire.

The new rule, `auth_invalid_token_rejected`, runs on any HTTP server that refuses a request without a token. mcpscore sends the same request again, a `tools/list`, with `Authorization: Bearer mcpscore-invalid-token-` followed by a random UUID. No real server issued that token, so the only correct answer is a refusal.

`WWW-Authenticate` header carries `error="invalid_token"` (
The second pass condition is there because of a real server. GitHub Copilot’s MCP server answers a malformed token with HTTP 400 and `error="invalid_token"`, and a token shaped like a real GitHub token with 401. Both are refusals, and failing the first at HIGH would have been a false positive.

The rule only reads. It sends no `tools/call`, follows no redirect with the token, and if the server opens a session anyway, closes it with a `DELETE`. If the first answer is an unexplained 4xx, as some older servers give to a request without a session, it repeats the check with an `initialize`, and counts that only when the same `initialize` without a token is refused.

Our acceptance suite has a test server with the bypass built in. It answers a request without a token with a proper 401 challenge and protected resource metadata, and serves any non-empty bearer token. This is the audit of that server, with no token passed:

```
uvx mcpscore==1.25.0 'http://127.0.0.1:8094/mcp'
Server requires authentication — running a partial audit of the observable surface.
...
✅ The observed HTTP 401 response carries a non-empty WWW-Authenticate challenge.
...
❌ A fabricated bearer token was accepted: tools/list answered HTTP 200, where the same request without a token gets HTTP 401. Observed HTTP status: 200.
  Fix: Validate every bearer token (signature or introspection, expiry, audience) before handling the request, and answer an invalid one with HTTP 401. A failed check must never fall back to a default identity.
...
Audit finished. PARTIAL score: 13/27 from 10 of 92 checks — not comparable to a full audit.
```

Without credentials, mcpscore runs a [partial audit](https://docs.mcpscore.dev/authenticated-servers?utm_source=devto&utm_medium=referral&utm_campaign=blog) of what a server behind auth shows to anyone. The new check belongs there: it needs no token, because the point is what happens to a bad one. Switch the test server to accept only its real token and the check passes, with the three points back:

```
# After: only the server's own token is served
✅ A fabricated bearer token was refused with HTTP 401.
...
Audit finished. PARTIAL score: 16/27 from 10 of 92 checks — not comparable to a full audit.
```

Before release we ran the rule against the auth-gated servers in our live acceptance set, among them Asana, Linear, Notion, Sentry, Stripe and GitHub Copilot. All of them refused the token.

A refused made-up token is necessary, not sufficient. Behind the front door, a server can still accept a real token issued for a different service, forward the client’s token to an upstream API, or grant more scope than the token carries. The spec forbids all three, and none of them is visible from outside. Code review and the gateway in front of the server are where those get caught.

The check also needs a refusal to compare against. If your server answers a request without a token with a 500, mcpscore cannot tell it is behind auth, and the rule does not run. Fix that first: a server behind auth should answer a missing token with 401 and a `WWW-Authenticate` challenge, which mcpscore checks too.

A gateway holds model-provider keys, upstream tokens and access policy for every application behind it, which is why it is worth putting there. It is also why a single fallback in its auth path exposed every connected MCP server at once. Treat LLM and MCP gateways like the API gateways and identity systems they have become: patch them on the advisory, keep them off the public internet unless they need to be there, and check the auth on the gateway and on each server behind it, since a request that gets past one should still be refused by the other.

The rule runs on every HTTP audit in mcpscore 1.25.0, on the CLI and on [mcpscore.dev](https://mcpscore.dev/?utm_source=devto&utm_medium=referral&utm_campaign=blog). Point it at the URL you give clients, without a token:

```
uvx mcpscore==1.25.0 'https://your-server.example/mcp'
```

Add `--sarif` to put the finding in [GitHub code scanning](https://mcpscore.dev/blog/sarif-explained-mcpscore-1-14-0?utm_source=devto&utm_medium=referral&utm_campaign=blog). The token check covers part of MCP07, insufficient authentication, in the [OWASP MCP Top 10](https://mcpscore.dev/blog/owasp-mcp-top-10-catalog-security?utm_source=devto&utm_medium=referral&utm_campaign=blog); the [Origin check](https://mcpscore.dev/blog/mcp-origin-validation-dns-rebinding?utm_source=devto&utm_medium=referral&utm_campaign=blog) covers another. Every rule and its citation is in the [rules reference](https://docs.mcpscore.dev/rules?utm_source=devto&utm_medium=referral&utm_campaign=blog#security), and the [release notes](https://github.com/mcp-box/mcpscore/releases/tag/v1.25.0) list what changed.

**Does your server refuse a token it never issued?**

One command, no credentials: mcpscore sends a made-up bearer token and reports whether your server lets it in.
