Noticias

LiteLLM 1.107.0, comprometido en PyPI y Docker: robaba credenciales y dejaba una puerta trasera

7 min de lectura

LiteLLM 1.107.0, la versión que se publicó el miércoles en PyPI y en Docker, llevaba un programa que roba credenciales y una puerta trasera. La subió un atacante que se había hecho con los tokens de GitHub de tres desarrolladores del proyecto. Estuvo disponible casi dos horas en PyPI y unas dos horas y media en Docker Hub. Quien la instalara en ese intervalo debe dar por robadas todas las claves que había en esa máquina.

LiteLLM es una de las piezas más usadas para trabajar con modelos de IA en Python: una biblioteca y un servidor proxy que dan una sola API para hablar con más de cien modelos de OpenAI, Anthropic, Google y otros, y que guardan las claves de API de todos esos proveedores. Por eso es un objetivo muy valioso: un servidor de LiteLLM concentra justo las credenciales que un atacante quiere. Ya en marzo dos versiones del paquete se publicaron con malware, y tras aquel incidente el equipo rehízo su proceso de publicación.

Esta vez el ataque no pasó por el código del proyecto. El atacante cambió el flujo de publicación para que compilara desde un repositorio imitador, Berri-AI/litellm, en lugar del oficial, BerriAI/litellm, con un guion de más. El propio sistema oficial de LiteLLM firmó y publicó el resultado. Lo detectó un usuario, LidorMachluf, que abrió una incidencia en GitHub a las 12:06 UTC con el análisis del código malicioso; PyPI retiró la versión a las 13:23.

Claves del ataque a LiteLLM 1.107.0

  • Qué versiones están afectadas: litellm==1.107.0 en PyPI, sus siete wheels, de las 11:39 a las 13:23 UTC del 21 de octubre. En Docker Hub, las etiquetas v1.107.0, latest, main-latest y main-stable, de las 11:46 a las 14:20 UTC; en GitHub Container Registry, las mismas hasta las 13:51. Las versiones anteriores están limpias, y los clientes de la versión enterprise, que usan un registro privado, no se vieron afectados.
  • Cómo se escondía: el código fuente etiquetado en GitHub no tenía nada raro. Al compilar, una línea añadida al pyproject.toml metía en el paquete una carpeta con tres archivos que pip coloca en la raíz de site-packages: cook.pth, cook.py y sitecustomize.py. Python ejecuta los dos primeros al arrancar, así que el malware corría en cualquier programa de Python de ese entorno, incluido el propio pip, aunque nunca se importara LiteLLM.
  • Qué roba: claves SSH; credenciales de AWS, Azure, Google Cloud, Kubernetes, Docker y GitHub CLI; archivos .env; contraseñas y cookies de los navegadores; historiales de la consola; secretos de CI como los de GitLab, Jenkins, Vault o Terraform; los tokens de herramientas de IA como Claude Code, Codex, Gemini, Cursor o Copilot; y las variables de entorno de todos los procesos que puede leer. Si encuentra la base de datos del proxy, vuelca las tablas con las claves de todos los proveedores de IA. En la nube, pide las credenciales del rol de la máquina y comprueba que funcionan.
  • Adónde lo manda: todo se cifra con AES-256 y RSA-4096 y se envía a www.litellm.dev, un dominio registrado el 13 de octubre que no es del proyecto (el oficial es litellm.ai). Desde la red no se puede saber qué se llevó.
  • La puerta trasera: sitecustomize.py deja un proceso en segundo plano que cada hora lee una dirección en un archivo de una cuenta de GitHub creada también el 13 de octubre, vuelve a robar y descarga y ejecuta el código que encuentre allí. Ese archivo estaba vacío, así que esta parte no llegó a activarse, pero bastaba un cambio del atacante para hacerlo. En Windows esta parte falla; el robo de credenciales, no.
  • Cómo entró el atacante: según LiteLLM, tenía las credenciales de GitHub CLI de tres desarrolladores. Con la de uno de los dos propietarios de la organización desactivó la protección de la rama y cambió el flujo de publicación; con la de un segundo superó la aprobación de producción; con la de un tercero borró los registros de esas ejecuciones. El lunes, además, el equipo había ampliado temporalmente los permisos de publicación para que dos ingenieros sacaran parches diarios a clientes.
  • De dónde salieron los tokens: LiteLLM cree que el atacante entró en un despliegue interno de LiteLLM expuesto a internet, donde encontró una cuenta de usuario que no debía estar, y sacó los tokens de sus registros. La empresa dice haber visto que, en situaciones concretas, Claude Code incluye el token OAuth de GitHub CLI en el cuerpo de una respuesta, y si el proxy guarda las respuestas en sus registros, el token queda allí. Todavía no ha confirmado cómo entró el atacante en ese despliegue; sospecha de una vulnerabilidad parcheada hace poco.

Qué hacer si usas LiteLLM

Lo primero es saber si alguno de tus entornos llegó a instalar la 1.107.0. Si tienes LiteLLM sin versión fija en un requirements.txt, en una imagen de Docker con latest o en un servidor de integración continua que reconstruyó algo el miércoles entre las 11:39 y las 14:20 UTC (de 5:39 a 8:20 en la Ciudad de México), puede que sí.

  • Cómo comprobarlo: pip show litellm dice la versión instalada. Busca también en el disco litellm-1.107.0.dist-info, cook.pth y sitecustomize.py. Encontrar uno confirma que estuvo; no encontrarlos no demuestra lo contrario, porque un contenedor que ya se borró no deja rastro. En Docker, el índice de imagen malicioso es el sha256:1bbcb1443bc9….
  • No empieces con pip install: cualquier arranque de Python en ese entorno vuelve a ejecutar el malware, y eso incluye pip. Primero borra desde la consola cook.pth, cook.py, sitecustomize.py y sus .pyc de cada site-packages afectado y mata el proceso en segundo plano (es el que tiene abierto /tmp/remote_code_loader.lock). Después instala litellm==1.106.2, o mejor, reconstruye el entorno o la imagen desde cero.
  • Da por robado todo lo que había en esa máquina: si tenía salida a internet, LiteLLM y quien descubrió el ataque recomiendan suponer que los datos se enviaron. Hay que cambiar el rol de la nube de la máquina, todas las claves de proveedores guardadas en el proxy y sus claves maestra y virtuales, la contraseña de la base de datos y cualquier secreto que estuviera en el entorno de algún proceso.
  • Revisa los registros: en AWS, busca en CloudTrail llamadas a GetCallerIdentity del rol de la máquina hacia la hora de la instalación y, después, usos de esas credenciales desde fuera de tu infraestructura. En la red, conexiones a www.litellm.dev.
  • Fija las versiones: la lección de siempre. Una dependencia con versión fija y, mejor aún, con su hash en el archivo de bloqueo no habría instalado la 1.107.0 sola.

Lo que deja al descubierto un proxy de IA

Lo inquietante de este ataque es que LiteLLM había hecho los deberes después de marzo: publicaciones desde un repositorio privado, Trusted Publishing de PyPI, etiquetas de Docker inmutables, imágenes firmadas y una publicación que solo se podía lanzar desde una cuenta ligada a un portátil de su oficina. Nada de eso sirvió porque el atacante tenía las llaves de las personas que podían saltarse esos controles, y la firma acabó certificando un paquete construido con su código. Y esas llaves salieron, según la empresa, de los registros de su propio proxy de IA, el mismo tipo de servidor que vende: un sitio por el que pasa todo lo que hablan los desarrolladores con sus asistentes, tokens incluidos.

El equipo ha cerrado sus registros de paquetes a nuevas versiones y ha pausado las publicaciones normales mientras revoca y cambia todos los tokens de la organización. Además, tiene preparada una versión que corrige otra vulnerabilidad, que se hará pública el 27 de octubre y que de momento solo reciben sus clientes enterprise. Para quien use LiteLLM, la prioridad de hoy es comprobar qué versión instaló el miércoles y, ante la duda, cambiar las claves.

Más fuentes: incidencia #47826 en GitHub, con el análisis del código malicioso y las actualizaciones del equipo, y análisis de SafeDep. Portada: foto de Markus Spiske, CC0, Wikimedia Commons; logotipo de LiteLLM.

Fuente: LiteLLM, «Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised», 21 de octubre de 2026