LiteLLM en PyPI: cómo TeamPCP usó la cadena de suministro para envenenar 95 millones de descargas mensuales
# LiteLLM en PyPI: cómo TeamPCP usó la cadena de suministro para envenenar 95 millones de descargas mensuales
El 24 de marzo de 2026, dos versiones maliciosas de LiteLLM — 1.82.7 y 1.82.8 — fueron publicadas al Python Package Index. La última versión limpia conocida es 1.82.6, publicada el 22 de marzo. En la ventana de aproximadamente un día entre la versión limpia y las versiones maliciosas, LiteLLM pasó de ser una de las librerías de infraestructura de IA más descargadas del ecosistema Python a ser un canal de exfiltración masiva de credenciales. Lo que hace este incidente especialmente grave es que LiteLLM maneja claves de API de modelos de lenguaje por diseño — toda instalación legítima del paquete tiene acceso a secretos que un atacante quisiera robar. La escala potencial de compromiso es, por tanto, proporcional a la escala de adopción: 95 millones de descargas mensuales, presente según Wiz en aproximadamente el 36% de los entornos cloud escaneados en el momento del incidente, con usuarios que incluyen Stripe, Netflix y el propio Google ADK.
Quién es TeamPCP y por qué importa
TeamPCP es un actor de amenazas que se identificó a sí mismo con ese nombre — las iniciales que aparecen en artefactos como `tpcp.tar.gz` son la pista más directa — y que orquestó entre el 19 y el 24 de marzo de 2026 una campaña de cuatro oleadas contra la cadena de suministro de herramientas de seguridad y desarrollo. La particularidad que distingue a TeamPCP de otros actores de供应链 es que empezó por comprometer herramientas de seguridad: primero Trivy de Aqua Security, después las GitHub Actions de Checkmarx KICS y AST. Herramientas que cualquier equipo de DevSecOps razonable tendría en su pipeline, ejecutándose con acceso amplio a credenciales y al código fuente. La cadena de compromiso fue secuencial: Trivy comprometido robó tokens de publishing de PyPI que LiteLLM usaba en su CI/CD; esos tokens se usaron para publicar las versiones maliciosas en PyPI; las versiones maliciosas robaron credenciales de los entornos donde se instalaron, que se usaron en la siguiente oleada. Es el patrón de ataque a cadena de suministro en su forma más escalofriante: cada ola produce las credenciales que alimentan la siguiente.
CVE-2026-33634, con un CVSS de 9.8, cubre el compromiso de los binarios y GitHub Actions de Trivy. PYSEC-2026-2 cubre los paquetes maliciosos de LiteLLM en PyPI. El compromiso de las GitHub Actions de Checkmarx KICS y AST no tiene CVE asignado al momento de escribir este artículo, lo cual es un recordatorio de que no toda la actividad de TeamPCP está completamente cubierta por el sistema formal de identificación de vulnerabilidades.
Anatomía del payload malicioso
El código malicioso en LiteLLM 1.82.7 y 1.82.8 opera en tres etapas. La primera es la recolección de credenciales. El payload busca más de 50 rutas conocidas donde se almacenan secretos en entornos Python típicos: archivos de credenciales de AWS, contenido del AWS Secrets Manager, parámetros de SSM, variables de entorno que contienen tokens de proveedores de IA como OpenAI, Anthropic o Google, configuraciones de Kubernetes en `~/.kube/config`, y tokens de GitHub almacenados localmente. La exfiltración se dirige a un dominio controlado por el atacante: `models.litellm.cloud`, un nombre elegido con cuidado para parecer legítimo a observadores casuales.
La segunda etapa es el movimiento lateral. En entornos Kubernetes, el payload intenta desplegar pods privilegiados en cada nodo del cluster usando las credenciales de Kubernetes que haya podido recolectar. Los pods desplegados tienen acceso suficiente para persistir incluso después de que el contenedor original sea destruido. En entornos que no son Kubernetes, el movimiento lateral toma otras formas — conexiones SSH a máquinas conocidas, lectura de metadatos de instancias cloud, intentos de pivotaje a través de servicios internos que las credenciales recolectadas permiten alcanzar.
La tercera etapa es la persistencia. El payload instala un script de Python en `~/.config/sysmon/sysmon.py` y una unidad de systemd en `~/.config/systemd/user/sysmon.service`, con el nombre display "System Telemetry Service" para disfrazarse de un servicio legítimo del sistema. La unidad de systemd arranca al inicio y mantiene comunicación con el servidor del atacante, esperando payloads adicionales o instrucciones de actualización. La persistencia está diseñada para sobrevivir reinicios y, en muchos casos, actualizaciones del propio LiteLLM.
La variante del archivo .pth y por qué es más peligrosa
LiteLLM 1.82.7 contenía el stealer embebido dentro del archivo `litellm/proxy/proxy_server.py`, que se ejecuta cuando alguien importa LiteLLM en su código Python. Eso limita la activación a entornos donde LiteLLM se usa realmente — un servidor de desarrollo, una pipeline, una aplicación — pero deja fuera entornos donde LiteLLM está instalado pero no se importa.
LiteLLM 1.82.8 añadió una segunda vía de activación: un archivo `.pth` en el paquete, llamado `litellm_init.pth`. Los archivos `.pth` en Python se procesan automáticamente por el intérprete cada vez que arranca, sin necesidad de importar el paquete. Eso significa que cualquier proceso Python que arranque en una máquina donde LiteLLM 1.82.8 está instalado ejecuta el código del archivo `.pth`, independientemente de que LiteLLM se importe o no. Un script de CI/CD que ejecute `python algo.py` donde LiteLLM está en el entorno ejecuta el payload. Un Jupyter notebook que arranca ejecuta el payload. Una herramienta de testing que arranca Python para validar código ejecuta el payload. El alcance efectivo del compromiso se multiplica.
Hay un detalle que revela el nivel de improvisación del actor: el fork bomb involuntario. El archivo `.pth` usa `subprocess.Popen` para lanzar un proceso hijo de Python, pero ese proceso hijo también procesa los archivos `.pth`, incluido el mismo `litellm_init.pth`, lo que crea una recursión exponencial de forks que agota los recursos del sistema y lo bloquea. Es un bug en el malware — el `litellm` malicioso se autodestruye parcialmente — pero también es un ejemplo de cómo incluso los atacantes cometen errores cuando iteran rápido.
Cómo se comprometieron los tokens de publishing de PyPI
LiteLLM publicaba a PyPI desde un pipeline de CI/CD que usaba Trivy de Aqua Security para escaneo de vulnerabilidades. Cuando Trivy fue comprometido en la primera oleada de TeamPCP, el malware dentro de Trivy pudo acceder a la memoria del runner de CI/CD donde se ejecutaba. Los tokens de publishing de PyPI que LiteLLM tenía configurados como secretos del repositorio estaban en esa memoria o eran accesibles desde el contexto del runner. TeamPCP los exfiltra y los usa dos días después para publicar las versiones maliciosas, sin pasar por el proceso legítimo de release de LiteLLM — de hecho, las versiones 1.82.7 y 1.82.8 no tienen tag ni release correspondiente en el repositorio de GitHub de LiteLLM; aparecieron directamente en PyPI.
Este detalle es crucial para entender la cadena. El atacante no necesitaba comprometer directamente el repositorio de LiteLLM ni a sus maintainers. Necesitaba comprometer una herramienta upstream en el pipeline, y los tokens caían como efecto secundario. Es exactamente el patrón de ataque que la conversación sobre供应链 ha estado describiendo durante años, hecho realidad de la forma más incómoda posible: atacando las herramientas de seguridad que la industria presumía defenderían contra ese mismo tipo de ataque.
Versiones afectadas y línea limpia confirmada
Las versiones afectadas son LiteLLM 1.82.7 y LiteLLM 1.82.8, ambas publicadas el 24 de marzo de 2026 — 1.82.7 a las 10:39 UTC, 1.82.8 a las 10:52 UTC. La diferencia de 13 minutos entre ambas y la adición del vector `.pth` en 1.82.8 indica que el atacante estaba iterando durante la ventana de ataque. La última versión limpia confirmada es LiteLLM 1.82.6, publicada el 22 de marzo de 2026. PyPI puso en cuarentena todo el proyecto LiteLLM alrededor de las 13:30 UTC del 24 de marzo; la cuarentena se levantó posteriormente y los maintainers gestionaron la recuperación.
Para cualquier organización, la acción inmediata es: auditar todos los entornos donde LiteLLM está instalado y confirmar la versión exacta. Si aparece 1.82.7 o 1.82.8 en cualquier sitio — producción, CI/CD, workstations de desarrolladores, contenedores efímeros — se debe tratar como entorno comprometido. La rotación de todas las credenciales que hayan estado accesibles desde ese entorno es obligatoria, no opcional.
Qué hacer en un entorno comprometido
La respuesta a un compromiso de LiteLLM 1.82.7 o 1.82.8 no es solo desinstalar el paquete y reinstalar la versión limpia. El payload ya se ejecutó, y los indicadores de compromiso probablemente estén en el sistema. La respuesta razonable tiene seis pasos.
Primero, aislar el entorno. Si la máquina es un servidor de producción, sacarla de la red inmediatamente para prevenir movimiento lateral activo. Si es una workstation de desarrollador, pedirle al desarrollador que deje de trabajar en ella hasta que se complete la investigación.
Segundo, preservar evidencia. Capturar imagen de disco o, al menos, snapshot de los archivos clave: `~/.config/sysmon/` si existe, el contenido de `~/.aws/credentials`, `~/.kube/config`, cualquier variable de entorno que contenga tokens de proveedores de IA, los logs del runner de CI/CD si el compromiso fue en pipeline.
Tercero, rotar todas las credenciales que hayan estado en ese entorno. Esto incluye: claves de acceso de AWS, tokens de Kubernetes, tokens de GitHub, claves de API de proveedores de IA, secrets de cualquier servicio que la máquina haya podido alcanzar. La rotación debe ser completa; no sirve rotar solo "las que parecen comprometidas" porque el payload busca más de 50 rutas y no siempre sabemos cuáles alcanzó.
Cuarto, buscar indicadores de persistencia. El script `~/.config/sysmon/sysmon.py` y la unidad de systemd son los más conocidos, pero el atacante pudo haber instalado otros mecanismos. Buscar trabajos cron anómalos, claves SSH desconocidas en `~/.ssh/authorized_keys`, entradas sospechosas en `/etc/passwd` y `/etc/shadow`, servicios systemd activos que no se corresponden con despliegues conocidos.
Quinto, reconstruir desde cero. Una máquina comprometida por este tipo de payload no se debe "limpiar"; se debe reconstruir. La persistencia del payload está diseñada para ser difícil de detectar completamente, y el coste de un compromiso residual supera con creces el coste de reconstruir.
Sexto, revisar la cadena de suministro propia. Si tu organización tenía LiteLLM comprometido, también pudo haber tenido comprometido cualquier otra herramienta del pipeline que comparte runners de CI/CD. Revisar el resto del pipeline, no solo LiteLLM.
Lo que cambia después de TeamPCP
Este incidente acelera varias conversaciones que la industria de供应链 ha estado teniendo durante años. La primera es sobre la separación de responsabilidades: cuando una herramienta de seguridad es el vector de compromiso, ¿quién responde? Aqua Security distribuyó Trivy comprometido; Progress distribuyó LiteLLM; Checkmarx distribuyó KICS comprometido. Las tres son empresas con equipos de seguridad formales. La realidad es que la cadena de suministro es tan fuerte como su eslabón más débil, y un eslabón comprometido puede envenenar el resto aunque cada eslabón individual tenga prácticas de seguridad razonables.
La segunda conversación es sobre la verificación de出版. PyPI no tiene un mecanismo de attestation robusto que permita a un consumidor verificar que una versión publicada corresponde realmente al código fuente del repositorio. Las herramientas in-toto y sigstore existen, pero su adopción es voluntaria y fragmentada. Mientras eso siga así, cualquier persona con tokens de publishing comprometidos puede publicar versiones maliciosas sin que el consumidor pueda detectarlo por medios criptográficos automáticos. Es un problema de infraestructura de Internet, no de LiteLLM específicamente, y necesita atención a nivel de estándar.
La tercera conversación es sobre la监视 de供应链. Las herramientas tradicionales de SCA (software composition analysis) escanean dependencias en busca de vulnerabilidades conocidas, pero no escanean en busca de comportamiento malicioso. Después de TeamPCP, esa limitación queda más en evidencia que nunca: las versiones maliciosas de LiteLLM eran versiones nuevas, sin vulnerabilidades conocidas en bases de datos de advisories, con comportamiento que solo se hace evidente en ejecución. La监视 de供应链 tiene que evolucionar hacia análisis de comportamiento de paquetes en sandboxes, no solo comparación contra bases de datos de vulnerabilidades.
Cierre
TeamPCP usó la cadena de suministro como vector, no como objetivo. El objetivo eran las credenciales que cualquier instalación legítima de LiteLLM tiene acceso. La cadena — Trivy, luego LiteLLM, y las credenciales que vendrán — es un recordatorio incómodo de que las herramientas de seguridad no son inmunes a ser comprometidas, y que el coste de un solo eslabón débil se multiplica aguas abajo. Para las organizaciones, la lección operativa es directa: auditar versiones, rotar credenciales, reconstruir desde cero los entornos afectados, y empezar a tomar en serio la监视 de供应链 más allá del escaneo tradicional. Para la industria, la lección es estructural: la供应链 moderna necesita attestation criptográfica robusta por defecto,监视 de comportamiento en sandbox por defecto, y separación efectiva de responsabilidades entre publishers, registries y consumidores. Sin esos cambios, TeamPCP — o el próximo actor que itere sobre el mismo patrón — volverá a tener éxito.