Un pip install y se acabó todo.

Llaves SSH. Credenciales de AWS, GCP, Azure. Configs de Kubernetes. Wallets de crypto. Contraseñas de bases de datos. Variables de entorno. Historial de bash. Secretos de CI/CD.

Todo.

El 24 de marzo de 2026 a las 10:39 UTC, alguien publicó litellm 1.82.7 en PyPI. Trece minutos después, la versión 1.82.8. Ninguna de las dos existía en el repositorio de GitHub. Ninguna tenía tag. Ninguna fue compilada por el pipeline legítimo.

Fueron inyectadas directamente en PyPI con credenciales robadas.

Tres millones de descargas diarias. Dos horas de exposición.

Hagan la matemática.


Pero el malware no necesitaba que lo importaras.

Un archivo .pthlitellm_init.pthse ejecutaba automáticamente en cada proceso de Python que arrancara en la máquina donde estuviera instalado. No hacía falta un import litellm. No hacía falta correr nada que lo invocara. Solo existir en el ambiente era suficiente.

Cada script. Cada notebook. Cada cron job. Cada proceso hijo.

Todos disparaban el payload.


El ataque operaba en tres capas, documentadas por Sonatype:

Capa 1 — Cosecha. Un script de Python barría la máquina: llaves SSH, archivos .env, credenciales de nube, configs de Kubernetes, contraseñas de bases de datos, .gitconfig, historial de shell, wallets de crypto, y cualquier cosa que oliera a secreto.

Capa 2 — Exfiltración. Todo lo recolectado se cifraba con AES-256-CBC, una llave de sesión aleatoria, y esa llave se cifraba con una clave RSA de 4096 bits hardcodeada en el payload. El paquete resultante — tpcp.tar.gz — se enviaba por POST a models.litellm.cloud. Un dominio registrado el 23 de marzo. Un día antes del ataque.

Capa 3 — Movimiento lateral. Si había un token de service account de Kubernetes presente, el malware leía todos los secretos del cluster en todos los namespaces. Luego intentaba crear un pod privilegiado de alpine:latest en cada nodo de kube-system, montando el filesystem del host e instalando una puerta trasera persistente en /root/.config/sysmon/sysmon.py con un servicio de systemd.

No era un ladrón. Era una operación militar.


La cadena que nadie vio

Este ataque no empezó en PyPI.

Empezó con un escáner de vulnerabilidades.

19 de marzo. El grupo TeamPCP reescribe los tags de Git en aquasecurity/trivy-action — el escáner de vulnerabilidades de código abierto de Aqua Security. La versión v0.69.4 ahora apunta a un release malicioso. Miles de pipelines de CI/CD lo ejecutan sin darse cuenta. CVE-2026-33634, score CVSS: 9.4.

La herramienta que escanea tu código buscando vulnerabilidades ahora es la vulnerabilidad.

23 de marzo. Todos los tags de release en Checkmarx/kics-github-action están comprometidos. KICS — Keeping Infrastructure as Code Secure — el escáner IaC de Checkmarx. El branch master estaba limpio. Pero cada workflow que referenciaba la Action por tag de versión ejecutaba código del atacante. El dominio de exfiltración: checkmarx[.]zone. El mismo payload. El mismo tpcp.tar.gz. El mismo esquema de cifrado.

Checkmarx bajó el repo. Luego lo subió de nuevo. Luego dijo que "no tenía conocimiento de impacto en datos de clientes o ambientes de producción."

El optimismo corporativo es un fenómeno fascinante.

24 de marzo. El pipeline de CI/CD de LiteLLM corre Trivy como parte de su proceso de build. Sin versión pinneada. El Action comprometido exfiltra el token PYPI_PUBLISH del ambiente del runner. Con esa credencial, los atacantes publican las dos versiones envenenadas directamente en PyPI.

La cuenta comprometida pertenecía al co-fundador y CEO de LiteLLM, Krish Dholakia.


El bucle

Wiz lo llamó un "loop." Nosotros lo llamamos lo que es: un gusano de supply chain.

Trivy comprometido
  → credenciales de miles de CI/CD robadas
    → Checkmarx comprometido
      → más credenciales robadas
        → LiteLLM comprometido
          → llaves de API de cada proveedor de IA en tu organización
            → ???

Cada eslabón de la cadena alimenta el siguiente. Las credenciales robadas de un ataque desbloquean el próximo. Sysdig lo confirmó: payload idéntico, esquema de cifrado idéntico, convención de nombres tpcp.tar.gz idéntica. El mismo actor expandiendo su alcance.

Y hay un detalle que debería quitar el sueño.

LiteLLM existe para una sola cosa: ser la puerta de entrada unificada a todos los proveedores de IA. OpenAI, Anthropic, Google, Bedrock, VertexAI, y cientos más. Por diseño, el ambiente donde corre concentra todas las llaves de API de todos los proveedores de tu organización en un solo lugar.

El atacante no eligió cualquier paquete.

Eligió el que, por definición, tiene acceso a cada llave de cada proveedor.

Targeting quirúrgico.


El bug que los salvó

La razón por la que el mundo se enteró no fue una auditoría. No fue un escáner. No fue un equipo de seguridad haciendo su trabajo.

Fue un bug en el malware.

El archivo .pth lanza un proceso hijo de Python con subprocess.Popen. Pero como los archivos .pth se ejecutan en cada arranque del intérprete, el hijo re-dispara el mismo .pth. Y el hijo del hijo. Y así.

Una fork bomb exponencial. La máquina se colgaba.

Los desarrolladores notaron que sus máquinas morían al hacer pip install. Empezaron a preguntar. Alguien abrió un issue en GitHub. FutureSearch lo detectó porque lo jaló como dependencia transitiva un plugin de MCP corriendo dentro de Cursor.

El issue fue cerrado como "not planned" por el owner. Cientos de bots lo spamearon para diluir la discusión. El autor de litellm estaba muy probablemente completamente comprometido.

Sonatype lo detectó en segundos con tooling automatizado. PyPI puso el proyecto en cuarentena. Las versiones fueron yanked unas horas después.

Pero el código de persistencia del malware todavía referenciaba checkmarx[.]zone — el dominio de la operación anterior. No se molestaron en limpiarlo.

Vibe coding salva al mundo. Esta vez.


TeamPCP

También conocidos como PCPcat, Persy_PCP, ShellForce, DeadCatx3. Posiblemente relacionados con LAPSUS$, aunque la atribución sigue bajo investigación.

Su mensaje en Telegram:

"These companies were made to protect your supply chains and can't even protect their own."

Tiene razón.

Trivy es un escáner de seguridad. Checkmarx vende seguridad de supply chain. LiteLLM procesa todas tus llaves de IA. Las tres herramientas que existen específicamente para protegerte fueron el vector de ataque.

El zorro no entró al gallinero. Las gallinas le abrieron la puerta, le dieron las llaves, y se fueron a dormir.


Lo que no se dice

La ingeniería de software clásica enseña que las dependencias son buenas. Que estamos construyendo pirámides con ladrillos. Que cada pip install, cada npm install, cada GitHub Action referenciado por tag, es un ladrillo confiable que alguien más ya validó.

Cada paquete que instalas confía en cada dependencia de todo el árbol. Y cualquiera de ellas puede estar envenenada.

No es paranoia. Es lo que acaba de pasar.

Microsoft publicó guía de detección. CrowdStrike publicó IOCs. Sysdig publicó reglas de Falco. Snyk publicó análisis. Todo el aparato de seguridad de la industria se movilizó.

Después del hecho.

Las recomendaciones son las de siempre: pinnear Actions a SHA completo, no a tags. Monitorear conexiones salientes de runners de CI. Restringir IMDS. Rotar credenciales.

Cosas que todos saben. Cosas que casi nadie hace.


El próximo ataque no va a tener un bug que tumbe la máquina para avisarte.

El próximo tpcp.tar.gz va a salir silencioso, cifrado, y sin fork bomb. Va a vivir en tu cluster tres meses antes de que alguien haga la pregunta correcta.

Y cuando la hagan, las credenciales ya van a estar en otra cadena, abriendo otra puerta, publicando otro paquete.

El loop no se cerró.

Se expandió.