LiteLLM 1.107.0 troyanizado mediante tokens robados de GitHub
Tokens robados de GitHub CLI permitieron subir LiteLLM 1.107.0 troyanizado a PyPI y Docker, la segunda brecha de su cadena de suministro en un año.

LiteLLM volvió a ser comprometido. El 21 de octubre, los atacantes subieron una versión troyanizada, la 1.107.0, a PyPI, Docker Hub y GHCR, incluyendo en uno de los gateways de IA más desplegados del planeta un robador de credenciales y un troyano de acceso remoto que emite balizas cada hora. El paquete malicioso de PyPI estuvo disponible unas dos horas antes de ponerse en cuarentena; las etiquetas Docker envenenadas (1.107.0, latest, main-latest, main-stable) siguieron activas un poco más. Si descargaste una compilación sin versión fijada en esa ventana, asume que tus secretos están comprometidos y actúa en consecuencia.
Es la segunda brecha en la cadena de suministro de LiteLLM en un solo año. Tras el incidente de marzo de 2026, que CloudSEK estimó podría haber expuesto a más de 2.500 organizaciones y 434.000 pipelines de CI/CD, el proyecto reforzó su pipeline de publicación: repositorio de releases privado, etiquetas Docker inmutables e imágenes firmadas. Nada de eso frenó este ataque, porque el atacante entró por la puerta principal con credenciales válidas. Ahí está la clave, y es más feo que cualquier typosquatting.
Qué está confirmado, qué se afirma y qué aún no sabemos
Separemos las pruebas, porque las lagunas importan para saber cómo responder.
- Confirmado (varios investigadores independientes): los archivos envenenados, el mecanismo de autoejecución, el endpoint de exfiltración y el abuso observado de credenciales robadas de AWS en cuestión de horas, documentados por Aikido, Wiz y Socket.
- Afirmado (por LiteLLM): la vía de intrusión: Claude Code incrustó tokens OAuth de GitHub CLI en cuerpos de respuesta que acabaron en los logs de LiteLLM, exfiltrados desde un despliegue interno expuesto a internet.
- Desconocido: cómo llegó ese despliegue interno a ser accesible públicamente, cómo entró el atacante, el número total de víctimas y si los atacantes de marzo han vuelto.
- Disputado: la atribución. La técnica .pth replica el ataque de marzo atribuido a TeamPCP, pero Socket afirma que las pruebas no los relacionan.
La cadena: de un token filtrado a una versión firmada y envenenada
Según The Hacker News, la secuencia empezó el 19 de octubre, cuando LiteLLM amplió temporalmente los permisos de publicación para que dos ingenieros internos pudieran subir backports diarios de correcciones para clientes. El atacante, que tenía tres juegos de credenciales de desarrollador de GitHub CLI (uno de ellos capaz de ejecutar workflows), usó un token perteneciente a un propietario de la organización de GitHub para desactivar las protecciones de ramas y forzar la subida de cambios maliciosos.
Luego vino la parte elegante: ejecutaron el workflow de publicación legítimo, pero lo apuntaron a un repositorio parecido, Berri-AI/litellm, en lugar de BerriAI/litellm. Un solo guion. Como el pipeline en sí era auténtico, los artefactos envenenados salieron firmados y se publicaron por los canales habituales. Esto debería preocuparte si ejecutas tu propio CI/CD: verificar la firma demuestra que el artefacto salió del pipeline, no que el pipeline apuntara al origen correcto.
«Tras una investigación exhaustiva, hemos observado situaciones específicas en las que Claude Code incrusta el token OAuth de GitHub CLI de un usuario en el cuerpo de una respuesta, y en situaciones donde los cuerpos de respuesta se registran, estos tokens pueden aparecer en los logs de LiteLLM», indicó LiteLLM en su aviso.
La hipótesis de trabajo de los mantenedores es que el atacante encontró una «cuenta de usuario inesperada y de nombre inocuo» en un despliegue interno de LiteLLM expuesto a internet, extrajo de sus logs esos tokens OAuth y los usó contra el workflow de publicación. Cómo llegó ese despliegue a ser accesible desde internet no está claro; LiteLLM plantea una vulnerabilidad recientemente parcheada. Considéralo no demostrado hasta que se pruebe.
Dentro del payload: cook.pth, cook.py y un bucle C2 horario
El malware es una cadena bien armada, y merece entenderse porque sus primitivas son generalizables. El investigador de Aikido Ilyas Makari lo analizó, según The Register y The Hacker News:
cook.pth: un archivo de ruta de Python. Cualquier línea que empiece porimporten un .pth se ejecuta en cada arranque del intérprete. Este importacook. No requiere acción del usuario ni importar LiteLLM; basta con ejecutar Python para que se active.cook.py: se desofusca y ejecuta una rutina de robo llamadaprivcollect, que recorre secretos de navegadores, herramientas de desarrollo, nube y herramientas de IA, y luego envía el botín por POST alitellm[.]dev/healthcheck.sitecustomize.py: se ejecuta automáticamente en Linux y macOS al arrancar el intérprete. Obtienedomain.txtde un dead drop de GitHub ya eliminado (github[.]com/letters9813) y, si la respuesta es válida, descarga una segunda fase desde<dominio>/codepy.
Makari describió el mecanismo de persistencia: el demonio toma un bloqueo en /tmp/remote_code_loader.lock para evitar instancias duplicadas y luego entra en un bucle infinito. Cada hora obtiene el dominio C2 actual de un archivo de GitHub en bruto y después descarga y ejecuta un payload en Python desde allí. Los investigadores de Wiz, Benjamin Read y Rami McCarthy, señalaron que el uso combinado de los dos .pth y de sitecustomize.py parece ser el primer caso documentado de ambas técnicas usadas juntas en la naturaleza. Wiz también indicó que el archivo de texto del dead drop contenía solo valores de prueba antes de que GitHub lo eliminara alrededor de las 13:30 UTC: un pequeño alivio, pero el diseño hace que el C2 sea trivial de redirigir.

El alcance del robador es lo que convierte esto de un incidente de paquete en un incidente de nube. Según The Register, Wiz afirma que el infostealer ejecuta 16 módulos que cubren navegadores (credenciales y cookies de Chrome, Firefox, Edge y Safari), material de AWS de ~/.aws/, variables de entorno y metadatos de instancias EC2/ECS. Aikido añade tokens de cuenta de servicio de Kubernetes y secretos de CI/CD de GitLab y Jenkins, según CSO Online.
Lo que sigue: LLMjacking, casi en tiempo real
Wiz observó el abuso de credenciales robadas unas 4,5 horas después de la exfiltración. Entre las 16:20 y las 17:01 UTC, el atacante validó credenciales de roles de instancia de AWS extraídas de runners de CI y nodos de Kubernetes comprometidos desde dos IP: 168.100.8[.]57 y 165.227.201[.]8.
«Cada credencial se comprobó con sts:GetCallerIdentity, seguido de bedrock:ListFoundationModels y una llamada a ListModels, lo que es coherente con LLMjacking», afirmó Wiz. «En un pequeño número de entornos, el atacante también enumeró IAM (ListRoles, ListUsers, ListAccessKeys) y realizó un descubrimiento general (ListAccounts, DescribeTrails, etc.)».
Es un ciclo de monetización rápido y deliberado: robar claves, validarlas y comprobar qué modelos de Bedrock pueden consumir. Si has seguido casos de abuso de API keys como aquel en que un modelo de OpenAI gastó el ETH de prueba de un desconocido, sabes lo barato que se ha vuelto ejecutar el mal uso autónomo de claves.
¿Mismo atacante que en marzo? Las pruebas dicen «no demostrado»
Por tentador que sea atribuirlo a TeamPCP (el cargador .pth replica la táctica de marzo de 2026 que se les atribuye), Socket se negó explícitamente a unir los puntos. Le dijo a The Hacker News que las dos campañas usan claves públicas RSA incrustadas distintas, implementaciones de exfiltración distintas y comportamientos de backdoor diferentes. No está verificado si las credenciales robadas en marzo tuvieron algún papel. Que el mismo operador use herramientas nuevas sigue siendo posible; simplemente no está establecido.
Entonces, ¿sigue valiendo la pena confiar en un gateway de IA?
Aquí está el cálculo incómodo. LiteLLM registra unos 23 millones de descargas semanales y, por diseño, se sitúa junto a cada clave de API de modelos, identidad de nube y cadena de conexión a base de datos de tu organización. Sakshi Grover, de IDC, le dijo a CSO Online que «un gateway de IA es infraestructura empresarial privilegiada»: centralizar el acceso a modelos mejora la gobernanza, pero «concentra la confianza». Una encuesta de IDC de julio encontró que el 95 % de las empresas ya ejecuta al menos un flujo de trabajo con agentes financiado por la compañía en producción. Es mucho radio de impacto colgando de un solo proyecto de PyPI.
El investigador de JFrog Guy Korolevski hizo un punto relacionado en The Hacker News: basta una sola instalación envenenada para que un atacante empuje código malicioso a sistemas posteriores, y un repositorio con buena apariencia dice poco sobre el artefacto que realmente instalas. Por eso hay que verificar el wheel exacto y revisar las versiones antes de que lleguen a una compilación. Mi opinión, como alguien que ha pasado demasiadas noches de CTF abusando exactamente de este tipo de confianza implícita: fijar versiones por sí solo no te habría salvado (las propias etiquetas estaban envenenadas), pero fijarlas, verificar hashes y aplicar un periodo de cuarentena a las versiones recién publicadas sí lo habría hecho. Hacer vendoring o autoproxy del tráfico hacia LLM ya no es paranoia: es un control razonable para una infraestructura tan privilegiada. El coste de ingeniería es real; también lo es reconstruir cada runner de CI de tu organización porque se disparó un archivo .pth.
Detección y respuesta: qué hacer hoy
Si instalaste la 1.107.0 vía pip entre las 11:39 y las 13:23 UTC, o descargaste las etiquetas Docker/GHCR afectadas entre las 11:46 y las 14:20 UTC, estás en el alcance. Este es el triaje rápido de Makari, vía The Register:
# Exfil already fired if this exists:
ls /tmp/.pc_once_*
# C2 daemon ran (or is still running) if this exists:
ls /tmp/remote_code_loader.lock
# Malicious artifacts:
find / -name 'cook.pth' -o -name 'cook.py' 2>/dev/null
find / -name 'sitecustomize.py' -path '*litellm*' 2>/dev/null
- Vuelve a la v1.106.2, la versión segura más reciente según The Register, y aísla cualquier host que haya ejecutado la 1.107.0, incluidos los contenedores proxy y los runners de CI (consejo de Socket).
- Bloquea
litellm[.]dev/healthchecky el dead dropraw.githubusercontent.com/letters9813en la salida de red; alerta sobre cualquier proceso que descargue/codepy. - Rota todas las credenciales accesibles desde los hosts afectados: claves de nube, secretos guardados en navegadores, tokens de cuenta de servicio de K8s, secretos de GitLab/Jenkins y claves de proveedores de modelos. El consejo de LiteLLM: trata el entorno como comprometido. Eliminar el paquete no basta, porque el RAT podría seguir residente.
- Audita CloudTrail en busca de
sts:GetCallerIdentityseguido debedrock:ListFoundationModelsdesde principales inesperados, y cruza las dos IP de Wiz con tus logs. - A partir de ahora: fija versiones exactas, verifica los hashes de los wheels contra artefactos conocidos como buenos y da a las versiones recién publicadas un periodo de espera antes de que lleguen a las compilaciones.
LiteLLM afirma que el incidente está contenido y que los clientes con registros privados empresariales no se vieron afectados. El cofundador Krrish reconoció públicamente que el ataque era novedoso y pidió comentarios. Justo. Pero dos veces en un año, por dos puertas distintas, la lección se mantiene: cuando un solo paquete custodia todas las llaves de tu ecosistema de IA, su pipeline de publicación es tu superficie de ataque, te hayas apuntado a ello o no.
Fuentes
Este artículo se basa en informaciones y avisos de:


