LiteLLM 1.107.0 PyPI 套件遭植入憑證竊取程式及後門
LiteLLM 1.107.0 en PyPI instaló un robacredenciales y backdoor
LiteLLM 1.107.0 於 2026 年 10 月 21 日上載至 PyPI 的套件遭植入憑證竊取程式及遠端執行後門。安裝後新增的三個檔案會在任何 Python 程序啟動時執行,無須匯入 LiteLLM;SafeDep 認為攻擊很可能來自 BerriAI 的私有建置流程。PyPI 已撤下該版本,1.106.2、1.107.0rc1 和 1.107.0rc2 則確認未受影響。
PyPI 發佈檔案與公開 GitHub 版本不一致,令 LiteLLM 1.107.0 個案凸顯只審查原始碼的不足,並將焦點帶到建置產物的來源驗證。
LiteLLM 1.107.0 llegó a PyPI el 21 de octubre de 2026 con algo que ningún mantenedor aprobó: un robacredenciales y una puerta trasera de ejecución remota escondidos en tres archivos nuevos. Las siete ruedas de instalación (wheels), para Linux, macOS y Windows, los copiaban directo a site-packages, y ambos arrancaban con cualquier proceso de Python en la máquina, incluso uno que nunca importara litellm.
El repositorio público de BerriAI, la empresa detrás del proyecto, nunca tuvo ese código. Según la investigación de SafeDep, el ataque salió del propio pipeline de build privado de la compañía, no de un token de PyPI robado y usado desde otra máquina.
TL;DR
- LiteLLM 1.107.0 (PyPI, 21 oct 2026) instaló un robacredenciales y backdoor en los 7 paquetes.- Tres archivos nuevos en site-packages corrían con cualquier proceso de Python, sin importar litellm.- El repositorio en GitHub nunca tuvo ese código: la build de PyPI no coincidía con ningún commit.- SafeDep halla con confianza moderada a alta que el ataque salió del pipeline privado de BerriAI.- PyPI retiró la versión; 1.106.2, 1.107.0rc1 y 1.107.0rc2 quedan confirmadas limpias.
Qué es LiteLLM
LiteLLM es una librería de código abierto en Python, mantenida por BerriAI, que expone una única API compatible con OpenAI para llamar a más de 100 proveedores de modelos de lenguaje. LiteLLM 1.107.0, publicada el 21 de octubre de 2026, es la versión que resultó comprometida en PyPI.
La librería LiteLLM funciona como proxy: un equipo apunta su aplicación a ella y cambia de GPT a Claude o a un modelo abierto moviendo una línea de configuración, sin tocar el código de negocio. BerriAI también vende una capa enterprise con panel de administración y claves de proveedores guardadas en una base de datos propia, el mismo tipo de dato que terminó en la mira del robacredenciales.
Qué pasó
La cadena empieza en el instalador. Al correr pip install litellm==1.107.0, pip agrega tres archivos nuevos a site-packages: cook.pth, cook.py y sitecustomize.py. Python ejecuta automáticamente los archivos .pth y sitecustomize.py en cada arranque del intérprete, así que ambos corren apenas se abre cualquier script, sin que nadie importe el paquete comprometido de PyPI.
cook.pth son doce bytes con una sola línea: import cook. Ese import dispara cook.py, de 22.252 bytes, el robacredenciales. Según SafeDep, recolecta claves SSH, credenciales de nube, secretos de CI/CD, tokens de herramientas de IA, contraseñas guardadas en el navegador y las claves de proveedores de LLM que guarda la base de datos del proxy de LiteLLM 1.107.0. Cifra todo y lo manda a www.litellm[.]dev, un dominio que imita al proyecto real pero no le pertenece.
Las siete ruedas de la versión comprometida cubrían Linux, macOS y Windows por igual.
sitecustomize.py, de 25.509 bytes, hace dos cosas. Primero trae una copia del robacredenciales por si cook.py falla. Segundo levanta un demonio en segundo plano que, cada hora, lee la dirección de un servidor desde un archivo alojado en GitHub, vuelve a correr el robo de credenciales, y descarga y ejecuta lo que ese servidor le mande. Es, en los hechos, una puerta trasera de ejecución remota persistente, no un robo puntual.
El repositorio público de BerriAI en GitHub nunca tuvo estos tres archivos ni la línea de configuración que los genera. PyPI retiró el paquete apenas se conoció el hallazgo. Las versiones 1.106.2, 1.107.0rc1 y 1.107.0rc2 están limpias, igual que los paquetes complementarios que la versión maliciosa fija como dependencia: litellm-proxy-extras==0.4.110 y litellm-enterprise==0.1.78.
Contexto e historia
BerriAI construyó LiteLLM como una capa fina sobre decenas de APIs de modelos, y el proyecto se volvió una pieza común en stacks de IA que necesitan cambiar de proveedor sin reescribir integraciones. Esa posición lo convierte en un blanco atractivo: comprometer una sola versión alcanza para llegar a las claves de proveedores de LLM de cualquier equipo que la instale, justo el tipo de secreto que hoy mueve dinero real en facturación de IA.
Los ataques a la cadena de suministro de paquetes abiertos no son nuevos: npm y PyPI acumulan casos de typosquatting, cuentas de mantenedor robadas y dependencias troyanizadas. Lo distinto de este caso es que el código malicioso no aparece en ningún commit público ni en el código fuente que cualquiera puede auditar en GitHub; solo vive en el artefacto binario que PyPI distribuye. Auditar el repositorio no habría servido de nada.
Cómo LiteLLM 1.107.0 terminó con un backdoor en su pipeline
El cambio que habilita todo el ataque es una sola línea en el pyproject.toml del sdist de la versión 1.107.0, dentro de la sección de build de maturin, la herramienta que BerriAI usa para compilar las ruedas:
-version = "1.108.0"
- "litellm-proxy-extras==0.4.111",
- "litellm-enterprise==0.1.79",
+version = "1.107.0"
+ "litellm-proxy-extras==0.4.110", # mismos pines que 1.107.0rc2
+ "litellm-enterprise==0.1.78",
+data = "cook" # la linea inyectada
data = "cook" le dice a maturin que copie una carpeta cook/ dentro de litellm-1.107.0.data/purelib/ al armar la rueda. pip instala ese contenido directo en site-packages, por eso los tres archivos del implante terminan ahí sin pasar por ningún paso de instalación visible para el usuario. El sdist no incluye la carpeta cook/, así que esa línea es el único cambio visible en el código fuente distribuido.
El archivo RECORD de la rueda, que lista cada archivo instalado, confirma los tres agregados: cook.pth (12 bytes), cook.py (22.252 bytes) y sitecustomize.py (25.509 bytes). Quien quiera confirmar si una instalación quedó infectada por el implante cook.py puede revisar manualmente esos tres nombres en el site-packages local: su sola presencia confirma el compromiso, porque ningún release legítimo de litellm los incluye.
VersiónEstadoNotas1.106.2LimpiaEl sdist coincide con el commit del tag salvo los cambios de readme en Cargo.toml1.107.0rc1LimpiaSin los tres archivos del implante1.107.0rc2LimpiaMismos pines de litellm-proxy-extras y litellm-enterprise que usa la build maliciosa1.107.0ComprometidaIncluye cook.pth, cook.py y sitecustomize.py en las siete ruedas; retirada de PyPI
El tag v1.107.0 en GitHub apunta a un commit cuyo pyproject.toml dice version = "1.108.0", no 1.107.0. SafeDep comparó cada archivo del sdist contra los árboles de git por hash de blob y encontró que coincide con un commit posterior de main, más cinco archivos editados y la carpeta cook/ que no está en ningún commit público. Cuatro de esos cinco archivos son Cargo.toml que ganan una línea readme = "README.md", el mismo cambio que tienen las releases legítimas 1.106.2 y 1.107.0rc2. El quinto es el pyproject.toml con la línea data = "cook" inyectada.
Las ruedas maliciosas no parecen artesanales: coinciden con la salida normal del pipeline de BerriAI. El archivo WHEEL nombra maturin 1.9.4, la misma versión que usan las releases legítimas. Los timestamps del zip muestran builds nativos separados (macOS a las 11:20 UTC, Linux a las 11:21 UTC y Windows a las 11:37 UTC), y en la rueda de Windows los 2.804 archivos .py tienen terminación de línea CRLF, incluidos los tres del implante, mientras que la rueda de Linux no tiene ninguno: el patrón típico de un checkout de git hecho en Windows con conversión de fin de línea activada.
SafeDep revisó 539 ejecuciones de GitHub Actions entre las 09:30 y las 12:30 UTC de ese día.
SafeDep revisó esas 539 ejecuciones de GitHub Actions y no encontró ninguna que publicara la release. A las 11:50 UTC, unos diez minutos después de la subida a PyPI, el bot litellm-dispatcher publicó el release de GitHub v1.107.0, el mismo retraso que tuvo 1.106.2. PyPI no tiene atestaciones de procedencia para 1.107.0, 1.106.2 ni rc2.
Con esa evidencia, SafeDep concluye con confianza moderada a alta que el pipeline privado de BerriAI construyó las ruedas a partir de una fuente de git modificada por el atacante. Pudo ser una credencial de mantenedor o de bot comprometida, una rama o mirror privado, o la referencia que el pipeline usa para construir. Un token de PyPI robado y usado desde la máquina del atacante explica peor la evidencia, porque no produciría builds que coinciden en versión de maturin, timestamps y formato de línea con el pipeline real.
⚠️ Ojo: si instalaste litellm 1.107.0 entre el 21 de octubre de 2026 y el retiro del paquete, rotá las claves SSH, los tokens de CI/CD y las claves de proveedores de LLM que haya en esa máquina, aunque tu código nunca haya importado litellm.
flowchart TD
A["Atacante modifica el pyproject.toml en una fuente privada"] --> B["Pipeline de build de BerriAI genera las ruedas desde esa fuente"]
B --> C["PyPI recibe litellm 1.107.0 con tres archivos extra"]
C --> D["pip install copia cook.pth, cook.py y sitecustomize.py a site-packages"]
D --> E["Cualquier proceso de Python ejecuta el robacredenciales"]
D --> F["El demonio de sitecustomize.py descarga y corre codigo cada hora"]
Impacto y análisis
El daño no depende de usar la librería. Cualquier proceso de Python que arrancara en una máquina con litellm 1.107.0 instalado disparaba el robo de credenciales, incluido un script que nada tenía que ver con IA. Eso convierte a cualquier entorno de CI, contenedor base o máquina de desarrollo que instalara esa versión, aunque fuera como dependencia transitiva, en un objetivo comprometido.
El blanco también es específico del momento: el implante apunta directo a secretos de IA (tokens de proveedores de LLM, credenciales de herramientas de IA) además de los clásicos SSH y CI/CD. Un atacante con esas llaves puede facturar inferencia a nombre de la víctima, leer prompts con datos sensibles que pasan por el proxy, o usarlas como puente hacia otros sistemas.
Lo más grave para la confianza en el ecosistema es que el código nunca estuvo en el repositorio auditable. Pinnear por hash de commit no habría ayudado si el pipeline construye desde una fuente distinta a la que el mantenedor cree que está construyendo. La mitigación real pasa por verificar el artefacto publicado, no el código fuente en GitHub.
Queda una pregunta sin responder: SafeDep no tiene acceso a los logs internos de BerriAI, así que no puede confirmar si el atacante entró con una credencial de mantenedor, un bot comprometido o un mirror privado. Solo esos registros, que BerriAI no ha hecho públicos, pueden cerrar esa parte de la historia.
Qué sigue
BerriAI todavía no publicó un post-mortem con la causa raíz del acceso a su pipeline. Hasta que eso pase, cualquier conclusión sobre cómo entró el atacante queda en el terreno de la hipótesis razonada que plantea SafeDep. La ausencia de atestaciones de procedencia en PyPI para las últimas releases de litellm es, por ahora, el síntoma más visible de que falta un control de ese tipo en el pipeline.
El caso también suma presión a un reclamo que viene creciendo en la comunidad de Python: que PyPI exija o al menos muestre atestaciones de procedencia (build provenance) por defecto para proyectos con este nivel de adopción, de forma que una rueda sin ellas se note antes de instalar, no después de un robo de credenciales.
Probá esto hoy mismo: revisá si cook.pth o cook.py existen en algún site-packages de tu infraestructura, y si instalaste 1.107.0 en algún momento, rotá cada secreto que haya pasado por esa máquina antes de seguir usándola.
📖 Resumen en Telegram: Ver resumen
Preguntas frecuentes
¿Qué hace exactamente LiteLLM 1.107.0 al instalarse?
Agrega tres archivos a site-packages: cook.pth y sitecustomize.py, que Python ejecuta en cada arranque, y cook.py, el robacredenciales que ambos invocan. Roba claves SSH, credenciales de nube, secretos de CI/CD, tokens de IA, contraseñas del navegador y claves de proveedores de LLM, y además deja un demonio que corre de nuevo cada hora.
¿Corro riesgo si nunca importé litellm en mi código?
Sí. Los archivos .pth y sitecustomize.py corren con cualquier proceso de Python que arranque en esa máquina, sin que el script importe la librería ni sepa que existe.
¿Qué versiones de la librería LiteLLM son seguras?
1.106.2, 1.107.0rc1 y 1.107.0rc2 están confirmadas limpias, igual que litellm-proxy-extras==0.4.110 y litellm-enterprise==0.1.78, los paquetes complementarios que fija la versión maliciosa.
¿Cómo entró la puerta trasera de BerriAI al paquete si el repositorio en GitHub está limpio?
El sdist malicioso tiene una línea extra en pyproject.toml (data = "cook") que no existe en ningún commit público, y las ruedas se construyeron en el pipeline privado de la compañía a partir de una fuente de git que el atacante modificó, no desde el código visible en GitHub.
¿Qué datos robó el malware de cook.py?
Claves SSH, credenciales de nube, secretos de CI/CD, tokens de herramientas de IA, contraseñas guardadas en el navegador y las claves de proveedores de LLM almacenadas en la base de datos del proxy. Todo cifrado y enviado a www.litellm[.]dev.
¿PyPI o GitHub ya solucionaron el problema?
PyPI retiró la versión 1.107.0 del índice. El repositorio en GitHub nunca tuvo el código malicioso, así que no requirió limpieza; el riesgo está en cualquier máquina donde ya se haya instalado esa versión antes del retiro.
Referencias
- SafeDep: análisis técnico completo del paquete comprometido, con comparación de hashes y timestamps de build.- Repositorio de BerriAI en GitHub: código fuente público de LiteLLM, sin los archivos del implante.- Página del proyecto en PyPI: historial de versiones publicadas, incluida la 1.107.0 retirada.- Documentación oficial de LiteLLM: referencia de la API y el proxy que mantiene BerriAI.
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
來源:DEV Community · dev.to