Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

La versión maliciosa LiteLLM 1.107.0 roba credenciales e instala puertas traseras

Un actor de amenazas secuestró el pipeline de publicación de LiteLLM con una clave maestra filtrada que nunca se revocó y publicó una versión maliciosa 1.107.0 en PyPI y Docker el 21 de octubre de 2026. El wheel instala archivos de inicio de Python que roban credenciales y abren una puerta trasera cada hora, aunque nunca se importe LiteLLM. PyPI puso la versión en cuarentena tras 1 hora y 44 minutos; quienes la instalaron deben reconstruir el entorno y rotar todos los secretos accesibles.

Resumen

  • LiteLLM 1.107.0, publicado en PyPI a las 11:39 UTC del 21 de octubre de 2026 y en Docker Hub y GHCR a las 11:46 UTC, fue construido por un atacante, no por el proyecto. PyPI lo puso en cuarentena a las 13:23 UTC y las etiquetas Docker se eliminaron o redirigieron a las 14:20 UTC, después de que Upwind comunicara el ataque a las 12:06 UTC.
  • El wheel malicioso escribe archivos de inicio de Python fuera del directorio del paquete LiteLLM, por lo que el payload se ejecuta cada vez que Python arranca en ese entorno, aunque nunca se importe LiteLLM. Eliminar solo el paquete deja los componentes de inicio, como documentaron los investigadores de seguridad de Microsoft.
  • El payload roba credenciales: recopila credenciales SSH y Git, variables de entorno, tokens de registros y gestores de paquetes, claves de nube e IA y datos de navegadores, y los envía a litellm[.]dev/healthcheck. Además, incluye una puerta trasera Unix que obtiene y ejecuta código del atacante cada hora.
  • Si algún sistema de su infraestructura instaló o descargó 1.107.0 durante esa ventana, considere comprometidos el host y sus secretos: reconstruya el entorno con una versión limpia verificada y rote todas las credenciales accesibles para el host, la compilación y el CI circundante. La última versión limpia de PyPI es 1.106.3.

¿Estoy afectado?

Está afectado si se cumplen todas estas condiciones:

  • Algún sistema instaló litellm 1.107.0 desde PyPI entre las 11:39 UTC y la cuarentena de las 13:23 UTC del 21 de octubre de 2026, o descargó una imagen de LiteLLM de Docker o GHCR publicada desde las 11:46 UTC hasta que las etiquetas v1.107.0, latest, main-latest y main-stable se eliminaron o redirigieron a las 14:20 UTC.
  • El entorno se inició al menos una vez después de instalarlo. La ejecución ocurre mediante archivos de inicio de Python, por lo que no es necesario importar LiteLLM y no haberlo llamado nunca no protege al host.
  • Desde entonces no ha reconstruido el entorno ni rotado sus secretos.

Compruebe lo que está instalado y fijado en hosts, ejecutores de CI e imágenes:

pip show litellm | grep -E '^(Name|Version|Location)'
grep -rn 'litellm' requirements.txt pyproject.toml 2>/dev/null
grep -n -A2 'name = "litellm"' uv.lock poetry.lock 2>/dev/null   # shows the pinned version
docker images | grep -i litellm                                  # local caches

En CI y equipos de desarrollo, busque también archivos de inicio de Python que no deberían existir: archivos .pth, sitecustomize.py o usercustomize.py inesperados en los directorios site-packages o user-site del entorno. Ese es el mecanismo de persistencia utilizado por el wheel. A fecha del 25 de octubre, PyPI no enumera ninguna versión final 1.107.0 (solo compilaciones preliminares dev y rc), y 1.106.3 es la versión final más reciente, lo que confirma rápidamente que nunca se publicó legítimamente una 1.107.0 final.

Para dimensionar el alcance: litellm registró 21.343.835 descargas de PyPI en la última semana (pypistats, consultado el 25 de octubre de 2026). Por tanto, una ventana de exposición de 1 hora y 44 minutos sigue alcanzando a un gran número de instalaciones automáticas, cachés de CI y compilaciones nocturnas.

¿Qué hace el código malicioso?

La versión maliciosa convierte LiteLLM de biblioteca en implante a nivel de host, al instalar código en la ruta de inicio de Python, fuera del paquete que elimina una desinstalación normal.

Los análisis de Socket, del grupo OpenSourceMalware y de Microsoft Security Research coinciden en dos rutas de ejecución dentro del wheel 1.107.0:

  1. Un recolector de credenciales que se ejecuta al iniciar Python. Recopila información del sistema, archivos SSH, credenciales y tokens Git y GitLab, variables de entorno, material de autenticación de herramientas de desarrollo, aplicaciones de IA, entornos de nube y navegadores web, y tokens de npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems y Cargo. Los datos se comprimen y envían mediante HTTP POST a litellm[.]dev/healthcheck, un dominio registrado el 13 de octubre de 2026, ocho días antes de la publicación.
  2. Una puerta trasera orientada a Unix que se inicia como proceso en segundo plano, contacta con el atacante y obtiene y ejecuta código arbitrario cada hora. Proporciona persistencia que sobrevive a corregir una sesión robada o cambiar una contraseña.

El detalle decisivo es dónde reside el código. El wheel incluía tres archivos, cook.pth, cook.py y sitecustomize.py, bajo su directorio de datos litellm-1.107.0.data/purelib/, que pip copia a la raíz de site-packages durante la instalación, fuera del paquete litellm. Los archivos aparecen con hashes válidos en el manifiesto RECORD del wheel, por lo que se incorporaron a la versión al construirla, no después. Python ejecuta los archivos .pth y sitecustomize.py de site-packages cada vez que arranca el intérprete. Microsoft Security Research afirma que los archivos de inicio maliciosos residían fuera del directorio de la aplicación LiteLLM, de modo que «bastaba iniciar Python en un entorno afectado para activar el código malicioso, aunque nunca se importara LiteLLM» y «eliminar solo los archivos de la aplicación podía dejar los componentes de inicio maliciosos». Por tanto, desinstalar o actualizar el paquete no limpia el entorno; reconstruirlo sí.

¿Cómo ocurrió el compromiso?

La divulgación de LiteLLM reconstruye toda la cadena y la cronología de Socket añade los detalles de los flujos de trabajo:

  1. El 27 de mayo de 2026, un archivo de configuración incorporado al repositorio público LiteLLM-Labs/lite-harness contenía la clave maestra de un despliegue de LiteLLM Agent Platform. Se detectó el error, se sobrescribió el commit mediante un push forzado y se marcó la clave para revocación, pero nunca se revocó.
  2. El 10 de octubre, un atacante que operaba desde 69.48.229[.]143 utilizó esa clave maestra sin revocar para entrar en Agent Platform y extraer un token OAuth de GitHub CLI configurado allí. El token pertenecía a la cuenta propietaria de la organización GitHub y no tenía un alcance limitado, por lo que daba acceso de propietario a todos los repositorios de la organización.
  3. Entre las 22:11 y las 22:12 UTC de ese día, el atacante publicó commits en ramas efímeras llamadas ci-tmp-a351cc y ci-tmp-4d8517 de repositorios internos (BerriAI/litellm y BerriAI/litellm-auto). Cada uno añadía un flujo en .github/workflows/cache.yml con el asunto inocuo «ci: bump cache config», activado en cada push. Los flujos recolectaron tokens GitHub y claves de API del espacio Linear de la organización, del despliegue LiteLLM y de Together AI, además de un identificador de GitHub App y su clave de firma, y los enviaron a dos endpoints webhook[.]site controlados por el atacante. Las ejecuciones y ramas se eliminaron minutos después. Una segunda ronda el 12 de octubre amplió la recolección a claves Anthropic, OpenAI y AWS Bedrock, una clave Tavily, una clave de inquilino de escaneo de secretos de GitGuardian, estado de almacenamiento de Playwright y credenciales PostgreSQL y Redis. La clave de inquilino GitGuardian importa para la defensa: es una credencial de la propia plataforma de escaneo de secretos, con visibilidad y posiblemente control sobre las alertas que habrían revelado la actividad.
  4. El 21 de octubre, entre las 10:08 y las 11:08 UTC, el atacante anuló la protección de ramas y cambió el origen del pipeline de publicación de BerriAI/litellm a un repositorio imitador, «Berri-AI/litellm», que inserta un guion entre «Berri» y «AI». Normalmente, las publicaciones requieren un ingeniero autorizado y la aprobación de una cuenta especial vinculada a un dispositivo físico en la oficina; esa restricción se había relajado a principios de la semana para permitir dos ingenieros cualesquiera. El atacante utilizó un token robado de mantenedor para activar la publicación y otro para aprobarla.
  5. PyPI recibió 1.107.0 a las 11:39 UTC, las imágenes de Docker Hub y GHCR siguieron a las 11:46, y los registros de ejecución de producción y preproducción se eliminaron a las 11:53. El investigador de Upwind Lidor Machluf comunicó el ataque a la cadena de suministro a las 12:06, PyPI puso la versión en cuarentena a las 13:23 y las etiquetas Docker se eliminaron o redirigieron a imágenes limpias verificadas a las 14:20.

LiteLLM descartó su hipótesis inicial de que los tokens GitHub se habían filtrado mediante registros de despliegues internos y ahora está revocando todos los tokens asociados con cada sistema interno y plataforma de proveedores.

Flujo del ataque

flowchart LR A["Clave maestra publicada en un
repositorio, nunca revocada (27 de mayo)"] --> B["El atacante entra en Agent
Platform con la clave (10 de octubre)"] B --> C["Extracción del token GitHub CLI
del propietario de la organización"] C --> D["Ramas ci-tmp-* ejecutan flujos
de recolección de tokens"] D --> E["Pipeline dirigido al repositorio
imitador Berri-AI (21 de octubre)"] E --> F["Se publica la versión maliciosa 1.107.0
en PyPI, Docker Hub y GHCR"] F --> G["Hooks de inicio roban credenciales a
litellm[.]dev; puerta trasera cada hora"] style E fill:#f8d7da

¿Qué puede hacer un atacante?

En cualquier host donde se haya ejecutado el wheel malicioso:

  • Leer y exfiltrar todas las credenciales accesibles para el propietario del proceso: claves SSH, configuración y tokens Git, claves de proveedores de nube e IA, credenciales de bases de datos y cachés, variables de entorno y tokens de registros de paquetes de npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems y Cargo.
  • Volver a ejecutar código proporcionado por el atacante cada hora mediante la puerta trasera, conservando el acceso después de rotar las credenciales robadas si no se reconstruye el entorno.
  • Moverse a otros sistemas mediante credenciales recolectadas de CI y plataformas: tokens GitHub de mantenedor, una clave de firma de GitHub App, una clave de inquilino de escaneo GitGuardian y la misma clase de tokens de API de proveedores para los que LiteLLM actúa como proxy.

Para los responsables del proyecto, el impacto fue más allá de una versión: el atacante tuvo acceso de propietario a la organización GitHub durante once días, suficiente para leer y modificar todos los repositorios, y recolectó secretos de dos ingenieros con privilegios de mantenedor.

¿Qué debo hacer si lo instalé?

No hay una versión a la que actualizar: no existe una 1.107.0 final limpia en PyPI. Trate una instalación de 1.107.0 como un compromiso del host, en este orden:

  1. Aísle los hosts, contenedores y ejecutores de CI afectados. Deténgalos en lugar de iniciar sesión para una inspección informal, porque iniciar Python vuelve a ejecutar el payload.
  2. Reconstruya los entornos desde cero (nuevas imágenes de contenedor, entornos virtuales nuevos y nuevos ejecutores de CI) con una versión limpia verificada. Fije litellm==1.106.3, la última versión limpia, o una posterior verificada cuando el proyecto la publique. No se limite a pip uninstall litellm: los archivos de inicio están fuera del directorio del paquete y sobreviven a la desinstalación.
  3. Rote todos los secretos accesibles para el entorno, su compilación y su CI: variables de entorno, claves SSH, tokens Git y GitLab, claves de proveedores de nube e IA, credenciales de bases de datos y Redis, credenciales almacenadas en navegadores y tokens de registros de paquetes de todos los ecosistemas atacados por el ladrón (npm, PyPI, pip, Yarn, Maven, Gradle, Composer, RubyGems, Cargo). Suponga que el atacante los posee, no que simplemente los vio.
  4. Compruebe y limpie el origen de la instalación: cachés de CI, imágenes base y digests Docker descargados entre las 11:39 y las 14:20 UTC del 21 de octubre, y archivos de bloqueo que quizá sigan fijando 1.107.0 para la próxima compilación.
  5. Si tiene relación con la organización LiteLLM o los flujos de sus ingenieros, revise el registro de auditoría de GitHub en busca de ramas ci-tmp-*, ejecuciones de flujos eliminadas y cambios de protección de ramas, y rote la clave de inquilino GitGuardian.

¿Cómo compruebo si he sufrido una intrusión?

Indicadores de los análisis públicos, todos publicados:

  • Presencia del paquete: pip show litellm informa de la versión 1.107.0, o archivos de bloqueo y digests de imágenes la fijan. La ventana de exposición de PyPI es de 11:39 a 13:23 UTC del 21 de octubre de 2026; la de Docker Hub y GHCR es de 11:46 a 14:20 UTC.
  • Red: HTTP POST a litellm[.]dev/healthcheck (dominio registrado el 13 de octubre de 2026) y cualquier conexión a los dos endpoints de exfiltración webhook[.]site utilizados por los flujos recolectores, 65a42464-3cfa-4b02-a18c-17a6d7b86f37 y 4da03473-a3e7-4dc2-a087-9c564b4208e1, que también se aplican al CI del proyecto.
  • Host: los archivos de inicio cook.pth, cook.py y sitecustomize.py en el directorio site-packages de entornos que instalaron el wheel, o cualquier otro .pth, sitecustomize.py o usercustomize.py inesperado; los marcadores de ejecución /tmp/remote_code_loader.lock y /tmp/.pc_once_*; un proceso en segundo plano sin explicación que contacta con un host remoto cada hora.
  • Organización GitHub (mantenedores): ramas llamadas ci-tmp- seguido de hexadecimal, commits que añaden .github/workflows/cache.yml con el asunto «ci: bump cache config», ejecuciones de flujos eliminadas minutos después de su creación el 10 y el 12 de octubre, y cambios de protección de ramas o del pipeline el 21 de octubre.

Cronología

  • 2026-05-27: la clave maestra de Agent Platform se incorpora al repositorio público LiteLLM-Labs/lite-harness; el commit se sobrescribe mediante push forzado, pero la clave nunca se revoca.
  • 2026-10-10: el atacante utiliza la clave, extrae un token GitHub del propietario de la organización y ejecuta el primer flujo de recolección de tokens desde una rama ci-tmp-* a las 22:11 UTC.
  • 2026-10-12: un segundo flujo amplía la recolección a credenciales de IA, nube, bases de datos y escaneo de secretos.
  • 2026-10-13: se registra el dominio de exfiltración litellm[.]dev.
  • 2026-10-21: a las 10:08 UTC se redirige el pipeline de publicación al repositorio imitador; 1.107.0 llega a PyPI a las 11:39 y a Docker a las 11:46; los registros de ejecución se eliminan a las 11:53; Upwind comunica el ataque a las 12:06; PyPI pone la versión en cuarentena a las 13:23; las etiquetas Docker se limpian a las 14:20.
  • 2026-10-23: Microsoft Security Research publica el análisis de los hooks de inicio de Python.
  • 2026-10-24: LiteLLM publica la divulgación de la causa raíz; Socket publica la cronología de los flujos.
  • 2026-10-25: Este artículo.

Preguntas frecuentes

¿Se comprometió el propio PyPI?

No. El atacante publicó mediante el pipeline de LiteLLM utilizando credenciales GitHub de mantenedor, de modo que, desde la perspectiva de PyPI, la carga parecía legítima hasta que los investigadores la comunicaron. PyPI puso la versión en cuarentena una hora y cuarenta y cuatro minutos después de publicarse, lo que limitó la ventana sin eliminarla.

¿Estoy protegido si instalé 1.107.0 pero nunca importé LiteLLM?

No. El código malicioso se instala como archivos de inicio de Python fuera del directorio del paquete LiteLLM, por lo que se ejecuta al arrancar el intérprete, antes de cualquier importación. Los entornos que simplemente tenían instalado el paquete y ejecutaron código Python ajeno están tan expuestos como las aplicaciones que utilizan LiteLLM como puerta de enlace de LLM.

¿Puedo simplemente desinstalar el paquete o actualizar más allá de 1.107.0?

No. Desinstalar elimina el paquete, no los archivos de inicio, y no hay una 1.107.0 limpia a la que actualizar: PyPI muestra 1.106.3 como versión final más reciente. Reconstruya el entorno, fije la versión limpia y después rote las credenciales.

¿Son las imágenes Docker tan peligrosas como el wheel?

Sí. La misma compilación maliciosa se publicó en Docker Hub y GHCR a las 11:46 UTC, y las etiquetas afectadas no se eliminaron o redirigieron hasta las 14:20. Las imágenes descargadas en esa ventana, incluso mediante latest, contenían el mismo payload, y los digests en caché pueden seguir entregándolas a compilaciones que fijan por digest.

¿Por qué no lo detuvo la aprobación de publicación?

Normalmente la aprobación procede de una cuenta especial vinculada a un dispositivo físico, pero esa semana se había relajado la restricción para acelerar las publicaciones: dos ingenieros cualesquiera podían activarlas y aprobarlas. El atacante utilizó un token robado de mantenedor para iniciar la publicación y otro para aprobarla, cumpliendo la regla relajada.

¿Afecta también a las claves virtuales del proxy LiteLLM?

Todo lo que el proceso pudiera leer debe considerarse recolectado: el ladrón recoge variables de entorno y credenciales de proveedores de nube e IA, precisamente donde los despliegues LiteLLM guardan sus claves de proveedores y configuración de base de datos. Rótelas junto con todo lo demás en lugar de especular sobre cuáles podía alcanzar el código.

El ladrón también está diseñado para despliegues del proxy LiteLLM. Cuando DATABASE_URL está establecido, inicia un proceso hijo Python que utiliza Prisma para leer el campo credential_values de LiteLLM_CredentialsTable y el campo litellm_params de LiteLLM_ProxyModelTable, hasta 500 filas por consulta. Así obtiene las credenciales de modelos de proveedores almacenadas en la base de datos del proxy, además del entorno.

Escanee sus repositorios, imágenes de contenedor y configuración de CI con Ostorlab para identificar todos los entornos que fijan litellm 1.107.0 antes de reconstruirlos.

Referencias

  • Divulgación de la causa raíz de LiteLLM, «Incidente de seguridad del 21 de octubre de 2026: cadena de ataque», por Oliver Jensen y Mateo Wang, 24 de octubre de 2026: https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain (consultado el 25 de octubre de 2026)
  • Archivo de configuración filtrado en el repositorio público LiteLLM-Labs/lite-harness en el commit e10b14a: https://github.com/LiteLLM-Labs/lite-harness/blob/e10b14a/harnesses/opencode/opencode.json (consultado el 25 de octubre de 2026)
  • Corrección del mantenedor de LiteLLM sobre la hipótesis inicial de filtración de tokens: https://github.com/BerriAI/litellm/issues/47826#issuecomment-6374891275 (consultado el 25 de octubre de 2026)
  • Microsoft Security Research, «Dentro del compromiso de la cadena de suministro de LiteLLM 1.107.0: robo de credenciales mediante hooks de inicio de Python», 23 de octubre de 2026: https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/ (consultado el 25 de octubre de 2026)
  • Investigación de Socket de los commits de flujos maliciosos ci-tmp-* y sus endpoints de exfiltración: https://socket.dev/blog/litellm-attack-timeline (consultado el 25 de octubre de 2026)
  • Análisis de OpenSourceMalware de las dos rutas de ejecución del wheel: https://opensourcemalware.com/blog/litellm-1-107-0-pypi-wheel-contains-a-credential-stealer-and-persistent-python-backdoor (consultado el 25 de octubre de 2026)
  • The Hacker News sobre la clave maestra filtrada y el secuestro del pipeline, 24 de octubre de 2026, citando la divulgación de LiteLLM: https://thehackernews.com/2026/10/leaked-litellm-master-key-let-attackers.html (consultado el 25 de octubre de 2026)
  • The Hacker News sobre el informe inicial del paquete comprometido por SafeDep, Socket, StepSecurity y Upwind, 21 de octubre de 2026: https://thehackernews.com/2026/10/litellm-compromised-in-supply-chain.html (consultado el 25 de octubre de 2026)
  • Historial de versiones de litellm en PyPI, que no muestra una 1.107.0 final y señala 1.106.3 como versión final más reciente: https://pypi.org/pypi/litellm/json (consultado el 25 de octubre de 2026)
  • Estadísticas de descargas de litellm en PyPI, 21.343.835 descargas en la última semana: https://pypistats.org/packages/litellm (consultado el 25 de octubre de 2026)