Anatomía del envenenamiento de LiteLLM 1.107.0: Cómo un asistente de IA escribió las claves maestras en los logs

Anatomía del envenenamiento de LiteLLM 1.107.0: Cómo un asistente de IA escribió las claves maestras en los logs

Seguridad de la Cadena de SuministroLiteLLMPyPIInfraestructura de IAFuga de Credenciales

Fuentes:HN + web research

[tool.maturin]
data = "cook"

El 21 de octubre de 2026 a las 11:39:51 UTC, la pasarela de modelos de lenguaje de código abierto LiteLLM publicó su versión oficial 1.107.0 en PyPI. En el archivo pyproject.toml del repositorio solo se había añadido esa única línea de configuración. Sin embargo, en las dos horas siguientes, convirtió todos los entornos desplegados sin bloqueo estricto de versión en recolectores masivos de credenciales. La frontera de confianza en la infraestructura de IA se ha desplazado: ya no basta con auditar el código fuente de las dependencias, ahora es crítico proteger las credenciales del proceso de compilación. Y la herramienta que entregó las llaves maestras a los atacantes fue, irónicamente, el asistente de programación con IA de uso cotidiano.

Un hook de 12 bytes toma el control del intérprete sin necesidad de importar

litellm==1.107.0 permaneció disponible en PyPI hasta las 13:23 UTC, una ventana de ataque de aproximadamente dos horas. Los siete paquetes wheel distribuidos para Linux, macOS y Windows contenían la carga maliciosa. Al mismo tiempo, se sobreescribieron cuatro etiquetas clave en Docker Hub y GHCR: v1.107.0, latest, main-latest y main-stable. Entre las 11:46:56 y las 14:20 UTC, estas etiquetas apuntaron unánimemente a la imagen infectada sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e. Cualquier flujo de CI/CD automatizado sin versiones ancladas descargó la puerta trasera directamente en producción.

El código malicioso se introdujo en el directorio purelib/ del paquete mediante la directiva data = "cook" de Maturin. Al instalarse, el paquete desplegaba tres archivos en site-packages: un archivo cook.pth de 12 bytes, cook.py de 22.252 bytes y sitecustomize.py de 25.509 bytes. En el repositorio público BerriAI/litellm nunca existió un directorio cook/, ni el paquete de distribución fuente contenía tales ficheros. Las auditorías de seguridad que se limitan a revisar confirmaciones públicas en GitHub resultaron completamente inútiles frente a una inyección en fase de compilación.

El archivo cook.pth consta de una sola línea: import cook. Según el mecanismo estándar de archivos .pth de Python, el intérprete ejecuta al arrancar cualquier línea que empiece por import. En ese mismo directorio, sitecustomize.py actúa como otro gancho global evaluado automáticamente al inicio, formando un doble mecanismo de activación junto con la carga comprimida de cook.py. La carga de ataque elude completamente el flujo habitual de importación: aunque un servidor nunca ejecute import litellm, el simple hecho de invocar pip o abrir un editor con servidor de lenguaje activa de inmediato la puerta trasera.

Inyección de archivos del paquete malicioso LiteLLM 1.107.0 y cadena de ejecución Figura: Inyección de archivos y flujo de ejecución de la doble carga útil en el paquete LiteLLM 1.107.0. Fuente: SafeDep (Informe de seguridad de la cadena de suministro)

16 módulos de recolección apuntan a herramientas de IA y bases de datos

La primera fase del exploit, encapsulada en cook.py, integraba 16 módulos de exfiltración diseñados para rastrear el sistema por completo. Primero leía claves privadas en ~/.ssh/* e historiales de terminal, descifrando contraseñas guardadas en Chromium y Firefox mediante DPAPI o claves maestras del sistema. Luego realizaba un barrido exhaustivo de credenciales de AWS, Azure, GCP, ~/.kube/config, Docker, gh, Vault y Terraform. Además, consultaba la interfaz de metadatos IMDS de AWS (probando IMDSv2 antes de recurrir a IMDSv1) y recorría /proc/<pid>/environ de los procesos activos para sustraer variables de entorno. Todo el código estaba milimétricamente ajustado al entorno habitual de un ingeniero nativo en la nube.

Lo que reveló con mayor nitidez las intenciones de los atacantes fueron dos módulos especializados: ai_devtools y db_litellm. El módulo ai_devtools buscaba credenciales de 17 herramientas de desarrollo basadas en IA, incluyendo Claude Code, Claude Desktop, Cursor, Windsurf, Copilot, el archivo mcp.json de VS Code, Gemini CLI, Codex, Continue, Aider, Zed y Hugging Face. Por su parte, db_litellm leía DATABASE_URL para exportar íntegramente las tablas LiteLLM_CredentialsTable y LiteLLM_ProxyModelTable de la base de datos de la pasarela. Una pasarela de IA opera como el concentrador neurálgico de credenciales de una organización. Comprometer una sola instancia proxy de LiteLLM entrega todas las claves API de los modelos de lenguaje conectados.

La información robada se cifraba con una clave aleatoria AES-256-CBC, protegida luego mediante RSA-4096 OAEP con una clave pública embebida, y se empaquetaba en un archivo tar.gz enviado a www.litellm.dev/healthcheck con la cabecera X-Token: XWiA8gZNa7NcX2XRoAEb. La carga de segunda fase, sitecustomize.py, bloqueaba /tmp/remote_code_loader.lock y se convertía en demonio persistente mediante fork() y setsid(). Cada 3.600 segundos leía el archivo domain.txt del repositorio de GitHub letters9813/pythonclient-test para repetir el rastreo y descargar código remoto desde /codepy para ejecutarlo directamente con exec(). GitHub dio de baja la cuenta letters9813 alrededor de las 13:40 UTC, pero los equipos conectados a la red probablemente ya habían ejecutado la segunda fase. Gracias al dominio señuelo litellm.dev, los sistemas habituales de monitorización no detectaron la anomalía en dos horas.

Tres credenciales legítimas eluden las revisiones y firman el paquete infectado

Tras un incidente de seguridad en marzo, el equipo de LiteLLM había reforzado minuciosamente su infraestructura de despliegue. Trasladaron los flujos de compilación a repositorios privados, habilitaron Trusted GitHub Publishers, bloquearon etiquetas de Docker inmutables y exigieron firmas criptográficas en las imágenes. Teóricamente, nadie externo podía alterar los scripts de publicación ni falsificar una firma oficial.

Sin embargo, los registros del 21 de octubre reflejan un asalto ejecutado con llaves maestras legítimas. A las 10:08:29 UTC, las reglas de protección de ramas bloquearon un intento de modificar el flujo de trabajo sin revisión previa de PR. Solo cinco segundos después, a las 10:08:34 UTC, el atacante empleó un token de GitHub CLI con privilegios de propietario de la organización para desactivar la protección y subir el commit 9cf97e935aeaf791b9c8db224a2bb3ee156134ce. A las 10:46:39 UTC, el flujo alterado descargó el commit 20535d8fa de un repositorio impostor Berri-AI/litellm (con un guion extra respecto al legítimo BerriAI) y superó las pruebas de pre-lanzamiento. A las 11:11:18 UTC, una segunda credencial de desarrollador autorizó el paso a producción. A las 11:53 UTC, tras distribuirse los paquetes envenenados, una tercera credencial eliminó el historial de ejecuciones. Cuando el atacante dispone de tres credenciales con máximos privilegios, las firmas automatizadas y los filtros de aprobación no detienen el ataque: estampan un sello oficial de autenticidad sobre el código malicioso.

Comunicado oficial de LiteLLM sobre el compromiso del pipeline el 21 de octubre Figura: Aviso oficial de LiteLLM que reconstruye la intrusión en el pipeline mediante credenciales privilegiadas. Fuente: LiteLLM (Informe oficial de seguridad)

Claude Code filtró tokens en respuestas y convirtió los logs en trampolín

¿Cómo se perdieron las credenciales de GitHub de los tres desarrolladores principales? El informe oficial de LiteLLM reveló un detalle determinante: bajo ciertas condiciones, Claude Code incorporaba el token OAuth de GitHub CLI del usuario en el cuerpo de respuesta de la API. Si la pasarela tenía habilitado el registro de respuestas HTTP completas, esos tokens privilegiados quedaban grabados en texto claro en los logs de LiteLLM. El equipo localizó posteriormente en su red interna una instancia olvidada de LiteLLM expuesta a internet. Los atacantes aprovecharon una vulnerabilidad recientemente parcheada para acceder a ella y extraer los tokens de GitHub CLI archivados en los registros.

Esta cadena de fallos abrió un intenso debate en la comunidad de ciberseguridad. Quienes señalan a los asistentes de IA argumentan que los agentes locales gozan de permisos de acceso excesivos sobre el sistema anfitrión y que propagar tokens en las respuestas incumple los principios de mínimo privilegio y anonimización de datos. Quienes enfocan el problema en la infraestructura recuerdan que exponer a internet una pasarela que registra respuestas completas constituye una negligencia grave de configuración. Ambas posturas convergen en una misma realidad: los desarrolladores usan asistentes de IA para programar la pasarela, y el asistente vierte las claves maestras en los logs de dicha pasarela. El bucle cerrado de las herramientas de IA amplifica drásticamente el alcance destructivo de cualquier fisura.

Paso cero de remediación: No ejecutar pip y anclar cada dependencia

Para las organizaciones afectadas por la versión 1.107.0, la guía de mitigación oficial comienza con una orden contraintuitiva: no ejecute pip install para solucionar el problema. Al ser pip un proceso de Python, invocarlo dispara de nuevo cook.pth y sitecustomize.py. El procedimiento adecuado exige borrar manualmente en site-packages los archivos cook.pth, cook.py, sitecustomize.py y los directorios __pycache__, además de terminar cualquier proceso demonio vinculado a /tmp/remote_code_loader.lock. Solo tras esta limpieza manual es seguro reinstalar 1.106.2 o reconstruir por completo contenedores y entornos virtuales.

Eliminar los archivos maliciosos solo detiene la hemorragia; la prioridad absoluta es la rotación total de credenciales. La advertencia es tajante: «Si 1.107.0 se ejecutó en una máquina con acceso a internet, debe asumirse que la exfiltración fue exitosa». Reiniciar contenedores o vaciar /tmp borra los archivos de bloqueo, pero la ausencia del candado no garantiza la seguridad. Es imprescindible rotar roles IAM de las máquinas, variables de entorno, contraseñas de bases de datos y cada clave API almacenada en la pasarela, auditando llamadas sospechosas a GetCallerIdentity en AWS CloudTrail en torno al incidente. Las versiones 1.106.2, 1.107.0rc1, 1.107.0rc2 y las instalaciones con versión fija de litellm-proxy-extras==0.4.110 son completamente limpias: únicamente cayeron víctimas quienes no fijaron sus versiones.

El frente de batalla en la seguridad del software ha cambiado. Los atacantes ya no dedican meses a redactar PRs maliciosas encubiertas; recogen tokens de logs expuestos y dejan que los pipelines oficiales firmen su código. Dado que los asistentes de desarrollo con IA procesan a diario credenciales confidenciales, aislar los registros de las pasarelas como material ultrasecreto y anclar mediante hashes cada dependencia son las únicas defensas reales para la infraestructura de IA.

Enlaces de referencia:

  • Informe de SafeDep sobre seguridad en la cadena de suministro: Análisis del backdoor y robo de credenciales en LiteLLM PyPI
  • Comunicado oficial de LiteLLM: Análisis postmortem del incidente de seguridad del 21 de octubre de 2026
  • Debate en la comunidad de Hacker News sobre el ataque a LiteLLM 1.107.0