FlawOpen

flawopen.com/AI Security/berriai-litellm-ghsa-7hp6-salt-key-session-token-privilege-escalation

● FlawOpen mapping: CWE-345 · CVSS 9.9 · Critical
FlawOpen Security Research

LiteLLM Salt-Key Privilege Escalation: GHSA-7hp6-4w63-5g45

LiteLLM reused its secret-encryption path for UI and CLI session tokens. A low-privilege internal_user could make attacker-controlled identity data pass through that shared path and reach proxy_admin; the role could then access MCP stdio command execution. The maintainer fixed token typing with a dedicated prefix and AES-GCM authenticated context. No CVE or confirmed in-the-wild exploitation is reported.[1], [2]

💡 Plain English Explainer (ELI5)

A building uses the same stamp to seal a storage envelope and issue an administrator's access badge. A visitor can influence what goes into the envelope, and a guard later mistakes that sealed envelope for a genuine badge. LiteLLM's fix gives session badges a distinct label and cryptographically binds that label to the seal, so a stored-secret envelope cannot pass as a login token.[1], [2]

Core Concepts & Subsystem Terms

Salt key
LiteLLM's configured key material used by its encryption helpers. This issue involved using the same underlying key path for stored values and bearer session tokens without binding the ciphertext to its intended purpose.[1], [2]
Authenticated additional data (AAD)
Data such as a token-type prefix that AES-GCM authenticates alongside ciphertext. AAD is not encrypted, but changing or omitting it makes authentication fail.[2]
internal_user and proxy_admin
LiteLLM roles. The advisory says an authenticated internal_user could escalate to proxy_admin, the administrative role.[1]
MCP stdio
A Model Context Protocol transport that starts a local process and exchanges messages through standard input and output. LiteLLM says the resulting proxy_admin access could reach command execution through this endpoint.[1]
CWE-345
Insufficient verification of data authenticity. This is FlawOpen's single primary editorial mapping for accepting ciphertext from one purpose as an authenticated token for another; the advisory also lists CWE-269 and CWE-441.[1], [5], [6], [7]

Framework mappings

  • FlawOpen’s conceptual mapping to MITRE CWE; no vendor or CVE record assigned this weakness ID to this research demonstration. CWE-345

Step-by-Step Attack Flow

Step 1

A low-privilege account reaches key management

The advisory's starting condition is an authenticated LiteLLM internal_user. Unauthenticated access is not required, but the actor must already hold that role.[1]

Step 2

Metadata ciphertext is created under the shared value-encryption path

The advisory says crafted identity data in API-key metadata could be encrypted and returned through key generation. The issue is the later acceptance of that ciphertext as another token type; do not treat arbitrary metadata as an independent login.[1], [2]

Step 3

The session parser accepts the wrong ciphertext purpose

Before the fix, value encryption and session-token verification shared the encryption helper and key path without an authenticated token-purpose boundary. The resulting identity could be interpreted as proxy_admin.[1], [2]

Step 4

Administrative access reaches MCP stdio

With proxy_admin privileges, the advisory says the actor could execute commands through the MCP stdio endpoint. This is host-side process capability, not a command run by the language model itself.[1]

Step 5

Token-purpose binding rejects cross-use

The fix uses a litellm_login_ token prefix as AES-GCM AAD and checks tokens through a dedicated decryptor. Upgrade to a fixed release; disabling EXPERIMENTAL_UI_LOGIN is only a temporary workaround with login-functionality impact.[1], [2], [3], [4]

Root Cause Analysis

BerriAI's advisory GHSA-7hp6-4w63-5g45 describes a cross-purpose authentication failure. LiteLLM used its value-encryption helper for both stored secrets and UI/CLI bearer session tokens, while session authentication decrypted values using the same key path. An internal_user could request an API key whose metadata contained attacker-controlled serialized identity data; the management path encrypted and returned the metadata value. When that ciphertext was presented as a bearer token, the session path could decrypt and interpret the forged proxy_admin identity. That role could then reach MCP stdio command execution. The upstream fix at f7cd90f changes token creation and verification: tokens use the litellm_login_ prefix, AES-GCM authenticated additional data (AAD) bound to that prefix, and a token-specific decryptor. The salt key remains key material; the security boundary is the cryptographically authenticated token purpose and the distinct parser path. Stored-value ciphertext therefore does not validate as a session token. The advisory rates the issue CVSS 3.1 9.9 Critical, says versions after 1.91.0 are affected by default, and says 1.87.0–1.90.x are affected only when EXPERIMENTAL_UI_LOGIN=true. It lists fixed versions 1.100.4, 1.101.3, 1.102.2, 1.103.1, and 1.104.0rc2 on their respective branches. The temporary mitigation EXPERIMENTAL_UI_LOGIN=false disables the vulnerable login path but also breaks CLI SSO and Claude Code gateway login. The advisory says no CVE is known. It does not establish exploitation in the wild. The code pair below shows the authentication-domain change from the upstream patch; it is not an exploit or a full replacement for the surrounding authentication implementation.[1], [2], [3], [4]

Source Code: Flaw vs. Secure Implementation

The upstream patch replaces the generic value encryptor/decryptor at both session-token creation and authentication. encrypt_bearer_token authenticates the token prefix as AES-GCM AAD; decrypt_bearer_token requires that prefix and the same AAD, so a value encrypted for storage cannot be reinterpreted as a login token. This is a reduced excerpt from the cited commit, not standalone authentication code. The shared salt key is not itself proof of a unique key per purpose; the verified separation shown here is the authenticated token type and matching decoder.[2], [3]

Source excerpt: litellm/proxy/auth/auth_checks.py and litellm/proxy/common_utils/encrypt_decrypt_utils.py — reduced excerpts from upstream commit f7cd90f

UNPATCHED FLAW
# Before the patch: stored-value ciphertext was also minted as a session token.
return encrypt_value_helper(valid_token.model_dump_json(exclude_none=True))

# Session authentication decrypted through the generic value helper.
decrypted_token = decrypt_value_helper(hashed_token, key="ui_hash_key")
HARDENED SECURE PATCH
# After the patch: session tokens have a dedicated type and AAD.
LITELLM_SESSION_TOKEN_PREFIX = "litellm_login_"
return encrypt_bearer_token(
    valid_token.model_dump_json(exclude_none=True),
    prefix=LITELLM_SESSION_TOKEN_PREFIX,
)

# Authentication uses the matching purpose-specific decoder.
decrypted_token = decrypt_bearer_token(
    hashed_token, prefix=LITELLM_SESSION_TOKEN_PREFIX
)

Engineering & System Hardening Checklist

Sources

← AI Security Hub Directory →