FlawOpen

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

● Mapeo de FlawOpen: CWE-345 · CVSS 9.9 · Crítica
Investigación · FlawOpen

LiteLLM: escalada de privilegios por salt key (GHSA-7hp6)

LiteLLM reutilizaba el cifrado de secretos para los tokens de sesión de la interfaz y la CLI. Un internal_user autenticado podía hacer pasar datos de identidad bajo su control por esa ruta y obtener proxy_admin; ese rol podía alcanzar ejecución de comandos mediante MCP stdio. La corrección vincula los tokens a un prefijo y contexto autenticado AES-GCM. No consta un CVE ni explotación confirmada en el entorno real.[1], [2]

💡 Explicación en Lenguaje Sencillo (ELI5)

Un edificio usa el mismo sello para cerrar un sobre de almacenamiento y emitir una credencial de administrador. Un visitante puede influir en el contenido del sobre, y después un guardia lo confunde con una credencial auténtica. La corrección de LiteLLM da a las credenciales de sesión una etiqueta propia y la vincula criptográficamente al sello.[1], [2]

Conceptos Clave y Términos

Salt key
Material de clave configurado en LiteLLM y usado por sus funciones de cifrado. El fallo reutilizaba esa ruta sin vincular el ciphertext a su propósito.[1], [2]
Datos adicionales autenticados (AAD)
Datos, como un prefijo de tipo de token, que AES-GCM autentica junto con el ciphertext. Si cambian o faltan, la autenticación falla.[2]
internal_user y proxy_admin
Roles de LiteLLM. El aviso indica que un internal_user autenticado podía escalar a proxy_admin, el rol administrativo.[1]
MCP stdio
Transporte del Model Context Protocol que inicia un proceso local e intercambia mensajes por la entrada y salida estándar. El aviso indica que proxy_admin podía llegar a la ejecución de comandos por ese endpoint.[1]
CWE-345
Verificación insuficiente de la autenticidad de los datos. Es la clasificación editorial principal de FlawOpen; el aviso también enumera CWE-269 y CWE-441.[1], [5], [6], [7]

Mapeos de marcos

  • Mapeo conceptual de FlawOpen a MITRE CWE; ningún proveedor ni registro CVE asignó este identificador de debilidad a esta demostración de investigación. CWE-345

Flujo de Ataque Paso a Paso

Paso 1

Una cuenta con pocos privilegios llega a la gestión de claves

La condición inicial del aviso es un internal_user autenticado. No se requiere acceso anónimo, pero el actor ya debe tener ese rol.[1]

Paso 2

La metadata genera ciphertext por la ruta compartida

El aviso indica que datos de identidad controlados por el usuario en la metadata de una clave podían cifrarse y devolverse. El fallo es aceptar después ese ciphertext como otro tipo de token.[1], [2]

Paso 3

El analizador de sesión acepta ciphertext de otro propósito

Antes de la corrección, el cifrado de valores y la verificación de sesión compartían helper y ruta de clave sin vincular el propósito del token. La identidad podía interpretarse como proxy_admin.[1], [2]

Paso 4

El acceso administrativo alcanza MCP stdio

Con proxy_admin, el aviso indica que el actor podía ejecutar comandos mediante el endpoint MCP stdio. Es capacidad de proceso en el host, no una orden ejecutada por el modelo.[1]

Paso 5

La vinculación del propósito rechaza la reutilización

La corrección usa litellm_login_ como AAD AES-GCM y un decodificador específico. Actualiza a una versión corregida; desactivar EXPERIMENTAL_UI_LOGIN es una medida temporal con impacto funcional.[1], [2], [3], [4]

Análisis de Causa Raíz

El aviso GHSA-7hp6-4w63-5g45 de BerriAI describe un fallo de autenticación entre propósitos. LiteLLM usaba el helper de cifrado de valores tanto para secretos almacenados como para tokens de sesión de la interfaz y la CLI; la autenticación de sesión descifraba los valores mediante la misma ruta de clave. Un internal_user podía solicitar una clave cuya metadata contuviera datos de identidad serializados bajo su control; la ruta de gestión cifraba y devolvía ese valor. Al presentar el ciphertext como bearer token, la ruta de sesión podía descifrarlo e interpretar la identidad falsificada como proxy_admin, que podía acceder a la ejecución de comandos MCP stdio. La corrección upstream f7cd90f cambia la creación y validación del token: usa el prefijo litellm_login_, AAD AES-GCM ligado a ese prefijo y un decodificador específico. La salt key sigue siendo material criptográfico; la frontera demostrada es el propósito autenticado del token y su decodificador correspondiente. Por ello, el ciphertext de un valor almacenado no valida como token de sesión. El aviso asigna CVSS 3.1 9.9 Critical; las versiones posteriores a 1.91.0 están afectadas por defecto, y 1.87.0–1.90.x solo si EXPERIMENTAL_UI_LOGIN=true. Las versiones corregidas enumeradas son 1.100.4, 1.101.3, 1.102.2, 1.103.1 y 1.104.0rc2 en sus ramas respectivas. La mitigación temporal EXPERIMENTAL_UI_LOGIN=false desactiva la ruta vulnerable, pero también interrumpe CLI SSO y el inicio de sesión de Claude Code gateway. El aviso no asigna CVE ni confirma explotación real. El código muestra el cambio de dominio de autenticación upstream; no es un exploit ni una implementación completa.[1], [2], [3], [4]

Código Fuente: Vulnerable vs. Seguro

La corrección upstream sustituye el cifrador y descifrador genéricos en la creación y autenticación de sesiones. encrypt_bearer_token autentica el prefijo como AAD AES-GCM; decrypt_bearer_token exige el mismo prefijo y AAD. Así, el ciphertext de almacenamiento no puede reutilizarse como token de inicio de sesión. Es un fragmento reducido del commit citado, no código de autenticación independiente. La clave salt compartida no demuestra por sí sola que exista una clave única para cada propósito; la separación verificada aquí es el tipo de token autenticado y su descifrador correspondiente.[2], [3]

Fragmento de origen: litellm/proxy/auth/auth_checks.py y litellm/proxy/common_utils/encrypt_decrypt_utils.py — fragmentos reducidos del commit upstream f7cd90f

IMPLEMENTACIÓN VULNERABLE
# Antes de la corrección: el ciphertext de valores almacenados también se emitía como token de sesión.
return encrypt_value_helper(valid_token.model_dump_json(exclude_none=True))

# La autenticación de sesión descifraba mediante el helper genérico de valores.
decrypted_token = decrypt_value_helper(hashed_token, key="ui_hash_key")
PARCHE SEGURO Y ROBUSTO
# Tras la corrección: los tokens de sesión tienen un tipo y AAD propios.
LITELLM_SESSION_TOKEN_PREFIX = "litellm_login_"
return encrypt_bearer_token(
    valid_token.model_dump_json(exclude_none=True),
    prefix=LITELLM_SESSION_TOKEN_PREFIX,
)

# La autenticación usa el descifrador correspondiente a esa finalidad.
decrypted_token = decrypt_bearer_token(
    hashed_token, prefix=LITELLM_SESSION_TOKEN_PREFIX
)

Lista de Verificación de Seguridad para Ingeniería

Fuentes

← AI Security Directory →