LiteLLM paquete malicioso v1.107.0: guía de mitigación

En pocas palabras: El 21 de octubre de 2026 la versión 1.107.0 de LiteLLM, publicada en PyPI y Docker Hub tras un ataque a su pipeline de release, contenía malware que robaba credenciales y permitía ejecución remota de código mediante los archivos cook.pth y sitecustomize.py. La solución: reinstalar la versión 1.106.2 y rotar todas las credenciales.

El 21 de octubre de 2026, atacantes comprometieron el pipeline de release de LiteLLM y publicaron la versión 1.107.0 en PyPI y Docker Hub con malware que roba credenciales y ejecuta código remoto. El paquete estuvo online casi dos horas en PyPI y dos horas y media en Docker Hub antes de que el equipo lo retirara, según confirmó el comunicado oficial de LiteLLM.

LiteLLM paquete malicioso es el nombre con el que la comunidad bautizó este incidente de cadena de suministro: la versión 1.107.0 de la librería, que BerriAI usa como interfaz y proxy común para distintos proveedores de modelos de lenguaje, fue reemplazada por una build con código dañino. LiteLLM tiene cerca de 23 millones de descargas semanales según el análisis de Socket.dev, así que el radio de impacto potencial no es chico.

En 30 segundos

  • Fecha exacta: el 21 de octubre, entre las 11:39 y las 14:20 UTC, circuló la versión maliciosa.
  • Mecanismo: dos archivos, cook.pth y sitecustomize.py, ejecutan código en cada arranque de Python, sin necesidad de importar litellm.
  • Qué roba: credenciales de AWS, Azure, Google Cloud, GitHub, claves de Claude, Codex y Gemini, y datos de navegadores.
  • Versión segura: litellm==1.106.2, según la recomendación oficial del equipo.
  • Causa raíz: un repositorio lookalike, Berri-AI/litellm con guion, en vez del oficial BerriAI/litellm.

¿Qué pasó exactamente con LiteLLM 1.107.0 el 21 de octubre?

Un atacante comprometió el pipeline de release privado de LiteLLM y lo apuntó a un repositorio falso para firmar y publicar artefactos envenenados. El equipo de LiteLLM lo confirmó en su blog de seguridad: a las 10:08:29 UTC se rechazó un intento no autorizado de modificar el workflow de release privado, pero cinco segundos después, a las 10:08:34 UTC, una credencial asociada a una cuenta de desarrollador privilegiada se usó para forzar el push igual.

Lo llamativo es la sutileza del engaño. El repositorio usado fue Berri-AI/litellm, con un guion de más respecto al oficial BerriAI/litellm. Esa diferencia de un carácter quedó registrada en la procedencia de build del contenedor, según detalló la investigación de Socket.dev. A las 11:39:51 UTC, siete wheels maliciosos quedaron publicados en PyPI. Siete minutos después, el escáner de IA de Socket ya los había marcado.

Vale la pena pararse un momento en por qué este vector funciona tan bien: no depende de que alguien escriba mal el nombre del paquete al instalarlo (como en el típico typosquatting de PyPI), sino de que el propio pipeline oficial, ya comprometido, firme y publique con la credibilidad del proyecto legítimo. Para quien instala litellm desde el índice oficial, todo luce normal: el nombre es correcto, la fuente es la de siempre. El engaño está un nivel más abajo, en la cadena de build, donde el usuario final no tiene visibilidad.

¿Cómo se ejecuta el código malicioso en cada inicio de Python?

El código corre apenas arranca el intérprete de Python, antes de que cualquier script del usuario se ejecute, gracias a dos archivos legítimos de Python usados con fines maliciosos: cook.pth y sitecustomize.py.

Ponele que instalaste LiteLLM 1.107.0 sin saberlo. El archivo cook.pth se copia al directorio site-packages y, cuando Python arranca, modifica sys.path para agregar una carpeta nueva al camino de búsqueda de módulos. Esa carpeta contiene sitecustomize.py, un script que Python ejecuta automáticamente durante la inicialización si lo encuentra en el path. Resultado: código arbitrario corre en cada arranque, sin que hayas escrito import litellm en ningún lado.

Según el análisis técnico de Socket.dev, ambos archivos Python embeben código fuente comprimido con zlib y codificado en Base64 (unos 61 KB cada uno). Uno dispara un recolector de credenciales en un hilo daemon; el otro abre un lock en /tmp/remote_code_loader.lock y lanza un proceso separado que puede descargar y ejecutar Python arbitrario desde un dominio controlado por el atacante.

Ejemplo hipotético (ilustrativo, no un caso real reportado): imaginá un runner de CI que cada noche corre pip install litellm sin pin de versión, porque “siempre toma la última estable”. Esa misma madrugada del 21 de octubre, ese runner habría instalado 1.107.0, ejecutado un script de build que importa Python para correr tests, y disparado sitecustomize.py sin que nadie tocara LiteLLM directamente. El runner suele tener variables de entorno con tokens de despliegue y, en muchos setups, acceso a un rol de IAM con permisos amplios. Ese combo —CI sin pinning más credenciales de larga vida en el entorno— es exactamente el peor escenario que describen las fuentes, aunque no implica que haya ocurrido así en ningún caso documentado públicamente.

¿Qué sistemas están afectados por LiteLLM 1.107.0?

Estás afectado si instalaste LiteLLM sin fijar versión entre las 11:39 y las 13:23 UTC del 21 de octubre, o si tu infraestructura corrió imágenes Docker con los tags latest, main-latest, main-stable o v1.107.0 entre las 11:46 y las 14:20 UTC de ese mismo día. Para más detalles técnicos, mirá el ataque supply chain contra LiteLLM en PyPI.

ArtefactoTags/versión afectadaVentana de exposición (UTC)Estado actual
PyPIlitellm==1.107.0 (7 wheels)11:39 a 13:23Removido
Docker Hubv1.107.0, latest, main-latest, main-stable11:46 a 14:20Removido o repuntado a fuente limpia
GHCRv1.107.0, latest, main-latest, main-stable11:46 a 13:51No disponible
Versión seguralitellm==1.106.2No aplicaRecomendada por LiteLLM
litellm paquete malicioso diagrama explicativo

Los clientes empresariales que usan el registro privado de LiteLLM no estuvieron expuestos, según aclaró el propio equipo en su comunicado. Si corrés pipelines de CI propios en un VPS en vez de depender de runners administrados por terceros, la segmentación de red importa tanto como el pinning de versiones.

Criterios para decidir qué tan agresiva tiene que ser tu respuesta

No todos los entornos que tocaron 1.107.0 necesitan el mismo nivel de respuesta. Antes de lanzarte a rotar cien credenciales, conviene ubicar tu caso en alguna de estas tres situaciones, construidas a partir de lo que reportan LiteLLM y Socket.dev sobre el mecanismo del ataque:

  • Exposición baja: descargaste el wheel o la imagen dentro de la ventana afectada, pero nunca llegaste a ejecutar Python con ese entorno activo (por ejemplo, quedó en una caché de build sin usar). Acá alcanza con borrar el artefacto y confirmar que no corrió ningún intérprete contra ese site-packages.
  • Exposición media: Python corrió en el entorno comprometido, pero la máquina no tenía salida a internet (un contenedor aislado, una red air-gapped) o el proceso se cerró casi de inmediato. Como explican las fuentes, un intérprete de vida corta puede salir antes de que el hilo recolector termine de exfiltrar. Acá corresponde borrar los archivos maliciosos, reinstalar en limpio y rotar como mínimo las credenciales que estaban en el entorno, aunque el riesgo de exfiltración exitosa es menor.
  • Exposición alta: Python corrió en un entorno con salida a internet —el caso típico de un proxy LiteLLM en producción o un runner de CI con acceso a la nube—. Según la lógica que documenta LiteLLM, acá no hay forma de descartar la exfiltración solo por la ausencia del archivo marcador: hay que tratar todas las credenciales alcanzables como comprometidas y pasar por el ciclo completo de aislar, limpiar, reinstalar y rotar.

La variable que más pesa en esta clasificación no es si encontraste o no los archivos maliciosos, sino si el proceso tuvo salida a internet mientras corría. Eso es lo que determina si el riesgo es teórico o ya consumado.

¿Cómo saber si tu sistema está comprometido?

Buscá en el filesystem, dentro de cada site-packages, los archivos cook.pth, cook.py y sitecustomize.py, junto con litellm-1.107.0.dist-info. La presencia de cualquiera de ellos confirma el compromiso, según indicó el equipo de LiteLLM.

Ojo con esto: la ausencia de esos archivos no prueba nada. Un contenedor efímero o una limpieza de directorio temporal puede borrar la evidencia sin que el ataque haya fallado. El marcador /tmp/.pc_once_72129118c59f5636c0bb, que el malware crea tras exfiltrar datos con éxito, tiene la misma limitación: si está, confirma exfiltración; si no está, no confirma nada.

¿Cómo mitigar el ataque y volver a una versión segura?

Lo primero es aislar el sistema de la red para frenar la exfiltración en curso. Después, nada de correr pip directo sobre la máquina comprometida, porque cualquier arranque de Python en ese entorno vuelve a ejecutar cook.pth y sitecustomize.py, pip incluido. Más contexto en otro paquete open source que robó credenciales.

  • Matá el proceso daemon: buscá el que mantiene el lock en /tmp/remote_code_loader.lock y terminalo.
  • Borrá los archivos maliciosos: cook.pth, cook.py, sitecustomize.py y los .pyc compilados correspondientes en cada site-packages afectado.
  • Reconstruí el entorno: reinstalá litellm==1.106.2 en una máquina o imagen nueva, no sobre la comprometida.
  • Rotá todo: el rol IAM de la instancia, cada API key de proveedor guardada en la base del proxy LiteLLM, las claves master y virtuales, y la contraseña de DATABASE_URL.

Si LiteLLM 1.107.0 corrió en un host con salida a internet, asumí que la exfiltración se completó. Revisá CloudTrail buscando llamadas a GetCallerIdentity desde el rol de la instancia cerca del horario de instalación, y después chequeá si ese mismo rol se usó desde fuera de tu infraestructura.

¿Cómo prevenir futuros ataques de cadena de suministro como este?

Fijar versiones exactas en requirements.txt es la medida más barata y más ignorada. Si no pineás versión, estás apostando la seguridad de tu sistema a que nadie comprometa el próximo release, y acá quedó claro que eso pasa.

LiteLLM ya había endurecido su pipeline tras un incidente previo en marzo, moviendo los releases a un repo privado con Trusted GitHub Publishers y tags de Docker inmutables. No alcanzó: el atacante tenía credenciales de tres cuentas de desarrollador distintas, una de ellas con permisos de owner de la organización en GitHub, y las usó para forzar un push saltándose las protecciones de rama. Lo que muestra este punto específico es que un control de branch protection, por sí solo, es tan fuerte como la cuenta con privilegios para anularlo: si esa cuenta cae, el control deja de existir. La lección es incómoda pero concreta: ningún hardening de CI reemplaza auditar de dónde vienen tus dependencias ni escanear repositorios con herramientas como las de Socket.dev. Lo explicamos a fondo en paquetes maliciosos que atacaron Strapi.

Qué está confirmado y qué no

Confirmado: LiteLLM reconoció oficialmente el compromiso, publicó ventanas horarias exactas y recomendó la versión 1.106.2. Socket.dev confirmó de forma independiente el payload y la discrepancia entre el repositorio oficial y el lookalike usado para firmar los artefactos.

Pendiente: LiteLLM todavía no confirmó cómo el atacante accedió por primera vez a su despliegue interno, aunque sospecha de una vulnerabilidad recientemente parcheada. Tampoco hay confirmación pública de qué organizaciones específicas sufrieron exfiltración exitosa; eso depende de cada auditoría forense individual.

Errores comunes al responder a este incidente

  • Correr pip para “arreglar” el problema: eso vuelve a disparar cook.pth y sitecustomize.py en el mismo arranque.
  • Confiar en la ausencia del archivo marcador: no encontrar /tmp/.pc_once_* no prueba que el sistema esté limpio.
  • Borrar solo los archivos sin rotar credenciales: si el host tuvo salida a internet, las claves ya pudieron viajar antes de la limpieza.
  • Actualizar a la tag “latest” pensando que es más segura: esa tag fue justamente una de las comprometidas entre las 11:46 y las 14:20 UTC.

Preguntas Frecuentes

¿Cómo saber si instalé la versión maliciosa de LiteLLM 1.107.0?

Corré pip show litellm y revisá si la versión instalada es 1.107.0, o buscá manualmente los archivos cook.pth y sitecustomize.py en tu directorio site-packages. Si instalaste sin pinning entre las 11:39 y las 13:23 UTC del 21 de octubre, tratá el entorno como comprometido aunque no encuentres los archivos. Sobre eso hablamos en imágenes Docker hardened para reducir riesgos.

¿Qué hace el archivo cook.pth en LiteLLM 1.107.0?

Cook.pth modifica sys.path durante el arranque de Python para agregar una carpeta que contiene sitecustomize.py, que a su vez ejecuta el código del malware. Es el primer eslabón de la cadena que permite ejecución de código sin que el usuario importe litellm.

¿Cómo elimino el malware de LiteLLM de mi sistema?

Aislá el sistema de la red, matá el proceso daemon que sostiene el lock en /tmp/remote_code_loader.lock, borrá cook.pth, cook.py y sitecustomize.py de cada site-packages afectado, y reinstalá litellm==1.106.2 en un entorno nuevo en vez de reparar el existente.

¿Qué versión de LiteLLM es segura para usar ahora?

Litellm==1.106.2 es la versión que el propio equipo de LiteLLM recomendó como segura mientras continúa la investigación. Cualquier versión anterior a la 1.107.0 no contiene los archivos maliciosos, pero conviene fijar explícitamente el número de versión en vez de instalar sin pin.

¿LiteLLM 1.107.0 robó mis API keys o credenciales de AWS?

El malware incluye un módulo que busca y exfiltra credenciales de AWS, Azure, Google Cloud, GitHub CLI y claves de herramientas de IA como Claude, Codex y Gemini, según el análisis de Socket.dev. Si tu sistema corrió la versión comprometida con salida a internet, asumí que esas credenciales viajaron y rotalas todas.

Conclusión

El incidente de LiteLLM 1.107.0 no es un bug de código, es una falla de proceso: un repositorio con un guion de diferencia bastó para firmar y distribuir malware durante dos horas y media. Si tu equipo usa LiteLLM, lo primero que tenés que hacer hoy es confirmar la versión instalada, fijarla a 1.106.2, y rotar cualquier credencial que haya estado en una máquina que corrió la build comprometida. No alcanza con borrar dos archivos; la pregunta que importa es si hubo salida a internet en esa ventana, y ahí la respuesta prudente es asumir que sí.

Fuentes

Te puede interesar...