Saltar al contenido

Seguridad

Ataque a la cadena de suministro de LiteLLM 1.107.0: qué revisar, qué rotar y los CVE de 2026

El 21 de octubre, una cadena de publicación secuestrada subió un ladrón de credenciales como LiteLLM 1.107.0 a PyPI y Docker. Cómo saber si lo ejecutaste, qué rotar, los IOC y todos los CVE de LiteLLM de 2026 en una tabla.

Por el equipo de seguridad de OpenWAF

14 min de lectura

Compartir

En resumen. El 21 de octubre de 2026 un atacante secuestró la cadena de publicación de LiteLLM y subió litellm 1.107.0 a PyPI y Docker con un ladrón de credenciales dentro. Estuvo disponible unas dos horas. Si la instalaste, o descargaste en ese intervalo las imágenes latest, main-latest o main-stable, trata la máquina como comprometida.

  • No empieces por pip: borra primero los ficheros de arranque y después reconstruye desde cero con una versión fijada (la 1.106.2 o una 1.106.x posterior).
  • Si la máquina tenía salida a internet, da por hecho que los datos salieron. Rota todas las claves de proveedores de la base de datos de LiteLLM, la clave maestra y las virtuales, la contraseña de la base de datos y el rol de nube.
  • Aparte, LiteLLM acumuló en 2026 una serie de CVE graves, desde una inyección SQL sin autenticación hasta tomas de control de administrador. La tabla de abajo los recoge, y cualquier versión 1.106.x es posterior a todas las versiones afectadas.
  • Un WAF no ve un malware que corre dentro de tu máquina. Delante de una pasarela de IA sí bloquea el salto por la cabecera Host (CVE-2026-49468) y limita lo que acepta un endpoint expuesto.

Qué pasó con LiteLLM 1.107.0

LiteLLM es una pasarela de código abierto muy extendida que pone una única API al estilo de OpenAI delante de muchos proveedores de modelos. Suele guardar todas las claves de proveedor de una organización. Eso hace que una versión con puerta trasera valga mucho para un atacante.

La versión maliciosa no salió del código de LiteLLM. Según el análisis de causa raíz de LiteLLM, el atacante usó tokens robados de mantenedores para apuntar la cadena de publicación legítima a un fork que imitaba el nombre del original. La etiqueta oficial v1.107.0 en GitHub está limpia, y la carga maliciosa solo existe en los paquetes y las imágenes compilados. Las horas son UTC del 21 de octubre de 2026.

Hora (UTC)Suceso
10:08Se anula la protección de rama del repositorio de publicación con un token robado de propietario.
11:11Se aprueba el paso a producción con un segundo token robado de mantenedor. La compilación sale del fork Berri-AI/litellm.
11:39–11:40Aparecen en PyPI siete paquetes maliciosos litellm==1.107.0.
11:46Se suben a Docker Hub y GHCR imágenes con las etiquetas v1.107.0, latest, main-latest y main-stable.
11:53–12:24El atacante borra las ejecuciones de los flujos de trabajo y reescribe el historial con un force-push.
12:06Un miembro de la comunidad avisa en GitHub de la versión maliciosa, menos de media hora después de publicarse.
antes de las 13:23PyPI pone en cuarentena la 1.107.0.
antes de las 13:51Las etiquetas de GHCR dejan de estar disponibles.
14:20Las etiquetas de Docker Hub se retiran o vuelven a apuntar a imágenes limpias.

LiteLLM dice que los clientes de su registro privado para empresas no se vieron afectados. Ninguna fuente de las que hemos leído da el número de descargas de la 1.107.0. Los 92 millones de descargas mensuales que cita la prensa son del proyecto en conjunto.

Cómo entró el atacante

La causa viene de meses atrás. El 27 de mayo de 2026 un fichero de configuración subido a un repositorio público de LiteLLM filtró la clave maestra de una plataforma interna de agentes, y esa clave nunca se revocó. El 10 de octubre el atacante la usó para obtener el token de la CLI de GitHub de un propietario de la organización. Después, con flujos de CI de usar y tirar, envió los tokens de otros dos mantenedores a un webhook externo. LiteLLM había relajado además hacía poco la aprobación de versiones para que bastaran dos ingenieros cualesquiera. Con los tres tokens robados, el atacante anuló la protección de rama, aprobó la versión y borró el rastro. El primer artículo de LiteLLM culpaba a otra vía, y su análisis posterior lo corrigió, así que fíate del artículo sobre la cadena de ataque.

Qué hace el malware

JFrog y Microsoft analizaron la carga:

  • Se ejecuta sin importarlo. El paquete deja cook.pth, que Python ejecuta en cada arranque, incluido el propio pip. Ese fichero carga cook.py, un ladrón que sus autores llaman «privcollect».
  • Roba casi todo lo que puede leer:
    • las variables de entorno del proceso y de cada /proc/*/environ legible;
    • credenciales y metadatos de instancia de nube (AWS, Azure, GCP) y la configuración de Kubernetes y Docker;
    • claves SSH, GPG y TLS, datos de navegador y tokens de Git, de registros de paquetes y de herramientas de IA;
    • las credenciales de proveedores de la propia base de datos de LiteLLM (LiteLLM_CredentialsTable y LiteLLM_ProxyModelTable), a las que llega con DATABASE_URL.
  • Envía los datos cifrados (AES-256 más RSA-4096) por HTTP sin TLS a un dominio que imita al de LiteLLM.
  • Instala un cargador. sitecustomize.py arranca en Linux y macOS un cargador que cada hora descarga código de una dirección publicada en un fichero de GitHub. Ese fichero estaba vacío cuando lo revisaron los investigadores, y no se ha confirmado ninguna segunda fase.

¿Me afecta?

Estás en riesgo si se cumple cualquiera de estas condiciones:

  • instalaste litellm==1.107.0 desde PyPI entre las 11:39 y las 13:23 UTC del 21 de octubre, a mano, en la CI o por un rango de dependencias que lo permitía;
  • descargaste o arrancaste una imagen de LiteLLM de Docker Hub o GHCR entre las 11:46 y las 14:20 UTC con v1.107.0, latest, main-latest o main-stable, incluidas las descargas automáticas de un orquestador, un autoescalado o un despliegue.

Compruébalo sin arrancar Python en el entorno sospechoso:

# ficheros que dejó el paquete en site-packages (no encontrar nada no prueba que esté limpio)
find / -path '*site-packages*' \( -name 'cook.pth' -o -name 'cook.py' -o -name 'sitecustomize.py' \
  -o -name 'litellm-1.107.0.dist-info' \) 2>/dev/null
ls -la /tmp/remote_code_loader.lock /tmp/.pc_once_72129118c59f5636c0bb 2>/dev/null

# el digest de la imagen que estás ejecutando
docker images --digests | grep -i litellm

Los ficheros marcadores solo prueban la infección cuando existen. Los contenedores y la limpieza de /tmp los borran.

Qué hacer ahora

  1. No ejecutes primero pip ni python en el entorno afectado. Los dos vuelven a arrancar el ladrón.
  2. Borra cook.pth, cook.py, sitecustomize.py, __pycache__/cook*.pyc y sitecustomize*.pyc de cada directorio site-packages. Después, mata el proceso de Python separado que tiene /tmp/remote_code_loader.lock.
  3. Reconstruye entornos virtuales e imágenes desde cero en lugar de limpiarlos. Fija una versión exacta (LiteLLM recomendó la 1.106.2; después han salido la 1.106.3 y la 1.106.4).
  4. Si la máquina tenía salida a internet, da por enviados los datos. Rota:
    • todas las claves de proveedores guardadas en la base de datos del proxy de LiteLLM;
    • la clave maestra de LiteLLM y todas las claves virtuales;
    • la contraseña de DATABASE_URL;
    • el rol IAM de la instancia o la tarea y cualquier clave de nube;
    • cualquier secreto en el entorno de cualquier proceso de esa máquina;
    • los tokens de SSH, GPG, Git y registros de paquetes.
  5. En AWS, busca en CloudTrail llamadas GetCallerIdentity del rol de la instancia alrededor de la hora de instalación y, después, usos de ese rol desde fuera de tu red. Haz lo mismo en los paneles de consumo de tus proveedores.
  6. Bloquea los indicadores de abajo en tu proxy de salida o tu resolutor DNS, y búscalos en los registros pasados.

Indicadores de compromiso

Vienen de LiteLLM, JFrog y Microsoft. Los dominios van desactivados.

TipoIndicador
Exfiltraciónhxxp://www.litellm[.]dev/healthcheck, cabecera X-Token: XWiA8gZNa7NcX2XRoAEb
Buzón del cargadorraw.githubusercontent[.]com/letters9813/pythonclient-test/main/domain.txt y luego hxxp://{dominio}/codepy
Ficheroscook.pth, cook.py, sitecustomize.py en site-packages; litellm-1.107.0.dist-info; /tmp/remote_code_loader.lock; /tmp/.pc_once_72129118c59f5636c0bb; /tmp/.pc_sq_*; /tmp/.pc_e_*
EntornoPRIVCOLLECT_ACTIVE
Índice de imagensha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e (berriai/litellm)
Infraestructura del atacantedirecciones IP 69.48.229.143 y 199.91.221.39, organización de GitHub Berri-AI que imita a la original, commit 20535d8f

Microsoft publica además en su análisis los hashes SHA-256 de los ficheros de arranque y los nombres de detección de Defender (Trojan:PyPI/LiteLlmSupplyChain).

Blinda la próxima actualización

  • Instala las dependencias de Python desde un fichero de bloqueo con hashes (pip install --require-hashes -r requirements.txt, o los ficheros de bloqueo de uv o Poetry). Así, una subida nueva con una versión antigua falla en lugar de instalarse.
  • Despliega las imágenes por digest (berriai/litellm@sha256:…), nunca por latest ni main-*. Que una persona o un bot abra la actualización, y espera un día antes de adoptar versiones nuevas de componentes críticos.
  • Da a la máquina de la pasarela lo mínimo: ningún rol de nube amplio, ninguna clave SSH, ningún secreto ajeno en su entorno, y tráfico de salida limitado a los proveedores de modelos que necesita.

Los CVE de LiteLLM de 2026

El ataque a la cadena de suministro llegó después de un año de fallos graves en el propio proxy de LiteLLM. Esta tabla los recoge de los avisos de GitHub y los registros CVE. El CVSS es la puntuación más alta del registro. Cualquier versión 1.106.x es posterior a todas las versiones afectadas de la lista.

CVEAfectadasCorregido enImpacto
CVE-2026-422081.81.16 a < 1.83.71.83.7Inyección SQL en la verificación de claves de API, sin autenticación. CVSS 9,8.
CVE-2026-422711.74.2 a < 1.83.71.83.7Ejecución de órdenes a través de los endpoints de prueba de MCP stdio, para un usuario autenticado. CVSS 8,8.
CVE-2026-47102< 1.83.101.83.10Un usuario puede cambiar su propio rol a proxy_admin con /user/update. CVSS 8,8.
CVE-2026-47101< 1.83.141.83.14Un usuario interno puede crear claves cuyas allowed_routes superan su rol y llegar a proxy_admin. CVSS 8,8.
CVE-2026-49468< 1.84.01.84.0Una cabecera Host manipulada hace que la comprobación de autenticación evalúe otra ruta y da acceso sin autenticar a rutas de gestión en ciertas condiciones. CVSS 9,8.
CVE-2026-59822< 1.84.01.84.0Una cabecera Authorization inventada activa el modo de paso de OAuth2 de MCP y da acceso a MCP sin autenticar. CVSS 4.0: 8,8.
CVE-2026-84377 (GHSA-3cv6-jpf6-8222)< 1.88.6 y las ramas 1.89 a 1.961.88.6, 1.89.7, 1.90.7, 1.91.5, 1.92.2, 1.93.2, 1.94.3, 1.95.1, 1.96.2Un usuario autenticado puede dirigir una petición a su propio api_base (SSRF) y recibir las credenciales de proveedor del operador. CVSS 6,5.
CVE-2026-93355hasta 1.102.1 según el registro CVEsin indicarEl inicio de sesión con JWT se fía de una reclamación email sin verificar, así que un token de un proveedor de identidad de confianza puede tomar la cuenta de otro usuario, también la de un administrador. CVSS 8,1 en el registro, 8,8 según OX Security.
CVE-2026-1146121.91.0 a 1.104.0rc1, por ramas1.100.4, 1.101.3, 1.102.2, 1.103.1, 1.104.0rc2Un usuario interno llega a proxy_admin reutilizando la clave de sal y, de ahí, a la ejecución remota de código por MCP stdio. CVSS 9,9.

Dos salvedades de las fuentes. CVE-2026-93355 no tiene aviso de LiteLLM ni versión corregida en ningún registro, y su CVSS difiere entre OX Security y el registro CVE. Los datos de GitHub para GHSA-3cv6-jpf6-8222 dan además rangos incoherentes, así que guíate por la lista de versiones corregidas. Ninguna de las fuentes que hemos leído informa de explotación activa de estos CVE.

Cómo ayuda un WAF y dónde no llega

Primero, lo claro: un WAF no puede parar el malware de 1.107.0. Se ejecutó dentro de tu propia máquina al arrancar Python y sacó los datos hacia fuera. Un proxy inverso delante de tu tráfico de entrada no lo ve nunca. La defensa es fijar versiones, verificar hashes y rotar.

Lo que OpenWAF sí hace por una pasarela LiteLLM (o cualquier API de IA) accesible desde internet:

  • Validación de la cabecera Host. El borde de OpenWAF enruta por Host y responde 421 Misdirected Request a cualquier host que no sea un nombre configurado, antes de cualquier filtro y sin contactar con el origen. Una cabecera Host manipulada no llega a LiteLLM a través de OpenWAF, que es justo el control previo que recomienda el aviso de LiteLLM para CVE-2026-49468.
  • Protección para endpoints de IA (Bots y agentes de IA). Para rutas como /v1/chat/completions puedes poner:
    • límites de peticiones por IP o por sesión;
    • un tope de tamaño del cuerpo;
    • comprobaciones fijas que rechazan peticiones desmedidas, como un max_tokens enorme, muchas respuestas en paralelo o miles de mensajes;
    • detección de inyección de prompts.
    Empieza en modo registro. Son límites de peticiones, no un contador de tokens.
  • Límites y puerta de acceso para la administración. Pon límites de peticiones más estrictos en las rutas de gestión de claves y usuarios, restríngelas por IP o país, o pon la interfaz de administración detrás de la puerta de acceso con códigos de un solo uso por correo.
  • Un origen que solo responde a OpenWAF. Con la autenticación del borde ante el origen (mTLS), el borde presenta un certificado de cliente y tu servidor puede rechazar cualquier conexión que no pase por OpenWAF, así que nadie se salta estos controles yendo directo al origen. La CA de cliente es común a toda la plataforma: prueba que la conexión viene del borde de OpenWAF, no que sea para tu zona, así que sigue comprobando también el Host.

Límites: los CVE con autenticación (42271, 47101, 47102, 84377, 114612) y la toma de cuentas por JWT (93355) usan credenciales válidas y llamadas a la API de aspecto normal, que ningún WAF distingue de un uso real. La inyección SQL de CVE-2026-42208 estaba en la verificación de claves de API, donde las reglas genéricas de un WAF pueden no mirar. La solución es actualizar LiteLLM.

Lista de comprobación

  1. Localiza todas las instalaciones de LiteLLM: servidores, contenedores, imágenes de CI, notebooks y portátiles del equipo.
  2. Comprueba si la 1.107.0 o las etiquetas de imagen afectadas se ejecutaron entre las 11:39 y las 14:20 UTC del 21 de octubre, con las órdenes de arriba y tus registros de despliegue y descarga.
  3. En las máquinas afectadas, borra los ficheros de arranque sin ejecutar Python y reconstruye con una versión fijada y con hashes verificados.
  4. Rota las claves de proveedores de la base de datos de LiteLLM, la clave maestra y las virtuales, DATABASE_URL, los roles de nube y todos los secretos que pudieran leer esas máquinas.
  5. Revisa el consumo en los proveedores, CloudTrail y los registros de auditoría de Git desde el 21 de octubre.
  6. Bloquea y busca los IOC en tu proxy de salida y tu resolutor DNS.
  7. Asegúrate de que no queda expuesto ningún LiteLLM anterior a 1.106.x. Los CVE de 2026 hacen peligrosas las versiones antiguas incluso sin el ataque a la cadena de suministro.
  8. Pon la pasarela detrás de un WAF que valide el Host, limite los endpoints de IA y proteja la interfaz de administración, y limita el origen a ese WAF.

Preguntas frecuentes

¿Me afectó el compromiso de LiteLLM 1.107.0?

Solo si instalaste litellm 1.107.0 desde PyPI entre las 11:39 y las 13:23 UTC del 21 de octubre de 2026, o descargaste las imágenes de Docker Hub o GHCR con las etiquetas v1.107.0, latest, main-latest o main-stable entre las 11:46 y las 14:20 UTC (GHCR estaba limpio a las 13:51). También cuentan las imágenes que bajara en ese intervalo un trabajo de CI o un autoescalado. LiteLLM dice que las versiones anteriores, la etiqueta de código en GitHub y el registro privado para empresas no se vieron afectados.

Solo instalé LiteLLM, nunca lo importé. ¿Sigo en riesgo?

Sí. El paquete malicioso dejaba un fichero .pth que se ejecuta en cada arranque de Python, incluido el propio pip, así que el ladrón se ejecutó en cuanto arrancó cualquier proceso de Python en ese entorno.

¿Qué versión de LiteLLM debo usar ahora?

El 21 de octubre LiteLLM pidió fijar la 1.106.2 u otra versión concreta publicada antes del incidente. Desde entonces han salido en PyPI la 1.106.3 y la 1.106.4 (24 y 25 de octubre). Todas son posteriores a todas las versiones afectadas de la tabla de CVE de 2026. Fija una versión exacta y verifica sus hashes en lugar de seguir latest.

¿Basta con desinstalar LiteLLM 1.107.0?

No. Borra cook.pth, cook.py, sitecustomize.py y sus ficheros compilados, para el proceso cargador y, mejor aún, reconstruye el entorno o la imagen desde cero. Si la máquina tenía salida a internet, LiteLLM pide dar por robados los datos y rotar todo lo que pudiera leer.

¿Qué secretos tengo que rotar?

Todas las claves de proveedores guardadas en la base de datos de LiteLLM, la clave maestra y las virtuales, la contraseña de DATABASE_URL, el rol de nube de la instancia o la tarea y cualquier secreto en el entorno de cualquier proceso de la máquina, además de los tokens de SSH, GPG, Git y registros de paquetes que hubiera en ella.

¿Se explotaron los CVE de LiteLLM de 2026?

Ninguno de los avisos ni informes que hemos usado lo dice. Varios son críticos y algunos no necesitan inicio de sesión, así que actualiza igualmente: cualquier versión 1.106.x es posterior a todas las versiones afectadas de la tabla.

¿Puede un WAF proteger LiteLLM?

No frente al malware de 1.107.0, que se ejecutó dentro de tu propia máquina y sacó los datos hacia fuera. Delante del proxy, un WAF que valida la cabecera Host bloquea CVE-2026-49468, como dice el propio aviso de LiteLLM. Los límites de peticiones, los topes de tamaño y una puerta de acceso también reducen lo que un atacante puede probar contra una pasarela expuesta. Las peticiones de un usuario con una clave válida parecen legítimas para cualquier WAF, así que actualizar sigue siendo imprescindible.

Fuentes

Temas

  • LiteLLM
  • LiteLLM 1.107.0
  • supply chain attack
  • PyPI
  • AI gateway
  • CVE-2026-49468
  • CVE-2026-42208
  • CVE-2026-93355
  • MCP

Autor

OpenWAF

Sigue leyendo

Ver todo

Trae las webs de tu agencia al acceso anticipado.

OpenWAF es gratis mientras está en acceso anticipado. Crea tu cuenta, añade los dominios de tus clientes y cambia sus servidores de nombres o añade un CNAME: el panel te guía en cada paso.