Se detecta malware con temática Shai-Hulud en la biblioteca de entrenamiento de IA PyTorch Lightning | Semgrep
(semgrep.dev)- Las versiones 2.6.2 y 2.6.3 de
lightningen PyPI, publicadas el 30 de abril de 2026, fueron utilizadas en un ataque a la cadena de suministro, y con solo ejecutarpip install lightningpuede activarse un directorio oculto_runtimejunto con una carga útil de JavaScript ofuscada - La carga maliciosa se ejecuta automáticamente al importar el módulo y roba credenciales, tokens de autenticación, variables de entorno y secretos de la nube, además de intentar contaminar repositorios de GitHub
- Aunque el punto de entrada de este ataque es PyPI, la propagación tipo gusano ocurre a través de npm: si encuentra credenciales de publicación de npm, inyecta el dropper
setup.mjsyrouter_runtime.jsen los paquetes que pueda publicar y vuelve a publicarlos con un incremento de parche - La exfiltración de datos usa 4 canales paralelos: POST por HTTPS, dead-drop mediante búsqueda de commits en GitHub, repositorios públicos de GitHub controlados por el atacante y push directo a repositorios de las víctimas; deja como indicadores el prefijo de commit
EveryBoiWeBuildIsAWormyBoiy la descripción de repositorio"A Mini Shai-Hulud has Appeared" - El malware instala un hook
SessionStarten.claude/settings.jsonde Claude Code y una tarearunOn: folderOpenen.vscode/tasks.jsonde VS Code para ejecutar el dropper cada vez que se abre el repositorio; cualquier máquina que haya importado el paquete malicioso durante el periodo afectado debe considerarse totalmente comprometida
Paquetes afectados y procedimiento de verificación
lightninges un framework de deep learning que suele aparecer en el árbol de dependencias de equipos que construyen clasificadores de imágenes, hacen fine-tuning de LLM, ejecutan modelos de difusión o desarrollan pronosticadores de series temporales-
Paquetes afectados
lightningversión2.6.2lightningversión2.6.3
-
Procedimiento de verificación para clientes de Semgrep
- Si no se ha ejecutado un escaneo reciente del proyecto, se debe lanzar uno nuevo
- En la página de advisories se puede verificar si el proyecto instaló recientemente esas versiones del paquete
- En el dependency filter se pueden revisar coincidencias; si aparece “No matching dependencies”, significa que el proyecto no está usando activamente la dependencia maliciosa
- Si hay coincidencias, se deben auditar en el repositorio archivos inesperados dentro de los directorios
.claude/y.vscode/indicados abajo en los indicadores de compromiso - Deben rotarse los tokens de GitHub, credenciales de nube y claves de API que pudieron estar presentes en los entornos afectados
- Consejos generales sobre respuesta ante ataques a la cadena de suministro y periodos de espera se tratan en $foo compromised in $packagemanager y Attackers are Still Coming for Security Companies
Cómo se propaga de PyPI a npm
- A diferencia de mini Shai-Hulud, que apuntaba directamente a npm, en este ataque el punto de entrada es PyPI
- La carga maliciosa sigue siendo JavaScript, y la propagación tipo gusano ocurre por npm
- Si el malware en ejecución encuentra credenciales de publicación de npm, inyecta el dropper
setup.mjsyrouter_runtime.jsen todos los paquetes que ese token pueda publicar - Luego configura
scripts.preinstallpara ejecutar el dropper, incrementa la versión de parche y vuelve a publicar - Los desarrolladores aguas abajo que instalen esos paquetes terminan ejecutando el malware completo en sus máquinas, lo que lleva al robo de tokens y a nuevas infecciones del gusano en paquetes
Métodos de exfiltración de datos
- La funcionalidad de robo comparte mecanismos y diseño con la campaña previa de Mini Shai-Hulud, y usa 4 canales paralelos para asegurar la salida de datos incluso si alguna ruta individual es bloqueada
- El ataque incluye una temática Shai-Hulud, como la creación del repositorio público
EveryBoiWeBuildIsaWormBoi - La estructura de los indicadores del ataque coincide con la campaña previa de mini Shai-Hulud, y los mensajes de commit maliciosos usan el prefijo
EveryBoiWeBuildIsAWormyBoipara diferenciarse del ataque original de Mini Shai-Hulud -
Transmisión C2 vía HTTPS POST
- Los datos robados se envían de inmediato por POST a un servidor controlado por el atacante a través del puerto 443
- El dominio y la ruta están almacenados como cadenas cifradas dentro de la carga, lo que dificulta el análisis estático
-
Dead-drop mediante búsqueda de commits en GitHub
- El malware consulta en bucle la API de búsqueda de commits de GitHub para encontrar mensajes con el prefijo
EveryBoiWeBuildIsAWormyBoi - Los mensajes de commit transportan un token con doble codificación Base64 en el formato
EveryBoiWeBuildIsAWormyBoi:<base64(base64(token))> - El token decodificado se usa luego para autenticar un cliente de Octokit para operaciones posteriores
- El malware consulta en bucle la API de búsqueda de commits de GitHub para encontrar mensajes con el prefijo
-
Repositorio público de GitHub controlado por el atacante
- Se crea un nuevo repositorio público con un nombre elegido aleatoriamente a partir de palabras de Dune y la descripción
"A Mini Shai-Hulud has Appeared" - Esa descripción puede buscarse directamente en GitHub
- Las credenciales robadas se confirman en
results/results-<timestamp>-<n>.json; mediante la API van codificadas en Base64, pero internamente son JSON plano - Los archivos de más de 30 MB se dividen en fragmentos numerados
- Los mensajes de commit usan
chore: update dependenciescomo camuflaje
- Se crea un nuevo repositorio público con un nombre elegido aleatoriamente a partir de palabras de Dune y la descripción
-
Push directo al repositorio de la víctima
- Si el malware obtiene un token de servidor de GitHub
ghs_, hace push directo de los datos robados a todas las ramas delGITHUB_REPOSITORYde la víctima
- Si el malware obtiene un token de servidor de GitHub
Objetivos del robo
- El malware apunta a credenciales en archivos locales, entorno, pipelines de CI/CD y proveedores de nube
-
Sistema de archivos
- Escanea más de 80 rutas de archivos de credenciales para buscar tokens
ghp_,gho_ynpm_ - Procesa hasta 5 MB por archivo
- Escanea más de 80 rutas de archivos de credenciales para buscar tokens
-
Shell y variables de entorno
- Ejecuta
gh auth token - Vuelca todas las variables de entorno de
process.env
- Ejecuta
-
GitHub Actions
- En runners Linux, vuelca la memoria del proceso
Runner.Workerusando Python incorporado - Extrae todos los secretos marcados como
"isSecret":true, además deGITHUB_REPOSITORYyGITHUB_WORKFLOW
- En runners Linux, vuelca la memoria del proceso
-
Organizaciones de GitHub
- Verifica los alcances del token
repoyworkflow - Recorre los secretos de organización de GitHub Actions
- Verifica los alcances del token
-
AWS
- Intenta usar variables de entorno, perfiles
~/.aws/credentials, IMDSv2169.254.169.254y ECS169.254.170.2para invocarsts:GetCallerIdentity - Enumera y obtiene todos los valores de Secrets Manager y parámetros de SSM
- Intenta usar variables de entorno, perfiles
-
Azure
- Usa
DefaultAzureCredentialpara enumerar suscripciones y acceder a secretos de Key Vault
- Usa
-
GCP
- Se autentica con
GoogleAuth - Enumera y obtiene todos los secretos de Secret Manager
- El alcance objetivo incluye entornos locales de desarrollo, runners de CI y los 3 principales proveedores de nube
- Toda máquina que haya importado el paquete malicioso durante el periodo afectado debe considerarse totalmente comprometida
- Se autentica con
Persistencia a través de herramientas de desarrollo
- Una vez dentro del repositorio, el malware apunta a herramientas de desarrollo de uso común como Claude Code y VS Code para instalar hooks de persistencia
-
Claude Code
- El malware escribe un hook
SessionStartconmatcher: "*"en.claude/settings.json, el archivo de configuración de Claude Code del repositorio - El hook apunta a
node .vscode/setup.mjs - Se ejecuta cada vez que un desarrollador abre Claude Code en el repositorio infectado, sin requerir uso de herramientas ni acciones del usuario más allá de iniciar la sesión
- El malware escribe un hook
-
VS Code
- Para usuarios de VS Code, instala una tarea
runOn: folderOpenen.vscode/tasks.json - Cada vez que se abre la carpeta del proyecto, se ejecuta
node .claude/setup.mjs
- Para usuarios de VS Code, instala una tarea
-
Dropper
setup.mjs- Ambos hooks invocan
setup.mjs, un bootstrapper autocontenido del runtime de Bun - Si Bun no está instalado, descarga silenciosamente
bun-v1.3.13desde GitHub releases - Soporta Linux x64, Linux arm64, Linux musl, macOS x64, macOS arm64, Windows x64 y Windows arm64
- Después ejecuta la carga completa de 14.8 MB
.claude/router_runtime.jsy limpia/tmp
- Ambos hooks invocan
-
Workflow malicioso de GitHub Actions
- Si el malware posee un token de GitHub con permisos de escritura, hace push a un workflow llamado
Formatteren el repositorio de la víctima - En cada push, vuelca todos los secretos del repositorio mediante
${{ toJSON(secrets) }} - Sube el resultado como un artefacto descargable de Actions llamado
format-results - Las Actions se fijan a un commit SHA específico para aparentar legitimidad
- Los repositorios que hayan recibido el paquete
lightninginfectado en CI y cuenten con un token con permisos de escritura deben auditar esos archivos
- Si el malware posee un token de GitHub con permisos de escritura, hace push a un workflow llamado
Indicadores de compromiso
-
Indicadores buscables
- Los mensajes de commit con el prefijo
EveryBoiWeBuildIsAWormyBoise usan como portadores de tokens del dead-drop y pueden encontrarse con la búsqueda de commits de GitHub - Los repositorios de GitHub con la descripción
"A Mini Shai-Hulud has Appeared"son repositorios de exfiltración del atacante y pueden buscarse directamente
- Los mensajes de commit con el prefijo
-
Paquetes
lightning@2.6.2lightning@2.6.3
-
Archivos y artefactos del sistema
_runtime/start.py: cargador en Python que inicializa la carga al importar_runtime/router_runtime.js: carga útil de JavaScript ofuscada y runtime de Bun de 14.8 MB_runtime/: directorio añadido en las versiones maliciosas del paquete.claude/router_runtime.js: copia del malware inyectada en el repositorio de la víctima.claude/settings.json: configuración del hook de Claude Code inyectada en el repositorio de la víctima.claude/setup.mjs: dropper inyectado en el repositorio de la víctima.vscode/tasks.json: tarea de autoejecución de VS Code inyectada en el repositorio de la víctima.vscode/setup.mjs: dropper inyectado en el repositorio de la víctima
1 comentarios
Comentarios de Hacker News
Puede que sea simplemente una ilusión de frecuencia, pero últimamente se ven bastantes ataques a la cadena de suministro muy sonados en paquetes importantes
Incluso en las primeras páginas de HN ahora mismo hay varias publicaciones sobre casos distintos
Al mirar atrás a
left-padhace 10 años, da la impresión de que hoy hay más ataques exitosos que antes, y probablemente sí sea asíTambién debe haber aumentado claramente el valor de que un ataque tenga éxito, pero me pregunto si, como comunidad en general, realmente estamos mejorando en la capacidad de detectarlos antes del lanzamiento de paquetes
Las empresas de software comercial deberían hacerlo mejor, pero todavía parece faltar una herramienta universal y sencilla para los casos en que algo empieza como código amateur o de hobby y termina siendo una dependencia de muchísimos proyectos
Ya puse el mismo comentario en el hilo sobre el ataque a la cadena de suministro de SAP: https://news.ycombinator.com/item?id=47964003
Antes era más común ejecutar
npm installmanualmente, y seguramente solo se hacía cuando se rompía el build o muy de vez en cuandoLos ataques a la cadena de suministro dependen de que la gente, o más exactamente los pipelines, actualicen paquetes automáticamente en cuanto sale una nueva versión sin pensarlo mucho
No sé si eso sea un buen modelo de negocio; probablemente no
left-padno fue un ataque, fue un bug de NPMNo debería haber sido posible borrar una versión de un paquete del que ya dependían otros paquetes públicos, y en cambio sí debería haber sido posible borrar una versión nueva específica de un paquete de la que nadie dependía
Cuando el autor de
left-padintentó borrar todos sus datos con la intención de abandonar el servicio, NPM debió haber devuelto un código de errorSegún Wikipedia, cuando Koçulu se decepcionó de la decisión de npm, Inc. y dijo que no quería seguir siendo parte de la plataforma, Schlueter, creador de NPM, le proporcionó el comando para borrar los 273 módulos que había registrado
Lo raro es que se abrieron 4 issues de seguridad y en todos el bot
pl-ghostcomentó automáticamente y los cerró [1][2][3][4]Al final, solo [4] se atendió correctamente y todos los comentarios del bot fueron eliminados
En otro reporte [5] se puede ver el comentario del bot, y da más información que la publicación original
[1] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[2] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[3] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[4] https://github.com/Lightning-AI/pytorch-lightning/issues/216...
[5] https://socket.dev/blog/lightning-pypi-package-compromised
El atacante creó un nuevo workflow de Actions con esa cuenta y, desde ese workflow ejecutado, extrajo y se llevó los secretos de PyPI
Después de publicar el paquete, usó esa misma cuenta para comentar y burlarse un poco de nosotros
Ojalá llegue pronto el día en que no haya dependencias en absoluto
Como ejemplo extremo, ahora cuando hago apps educativas interactivas para mi hija le pido a Opus que use solo JavaScript y HTML puros
Desde péndulos dobles hasta simulaciones de fluidos, todo funciona de una sola vez, y antes eso implicaba cientos de dependencias
Si el código tiene licencia MIT, puedo pedirle a Opus que extraiga exactamente las partes necesarias, las adapte a mi caso de uso y las incluya
En proyectos de hobby me ha funcionado bien hasta ahora, y ojalá en el futuro también el software de producción pueda quedarse sin dependencias
Si Chrome cambia la forma de alguna API, tú mismo tendrás que encontrarlo y corregirlo; y si Marruecos cambia la fecha de inicio del horario de verano, también tendrás que actualizar por tu cuenta el código de fechas y horas
Esas son cosas que las librerías nos resolvían y que dábamos por sentadas
No es gran cosa para un simulador de péndulo doble del que tu hija perderá interés la próxima semana, pero sí es un problema para una empresa que construye algo que debe seguir funcionando indefinidamente
Debería publicar algún código de acceso remoto con licencia MIT para que entre en los datos de entrenamiento de Opus
Cuando tomé el curso de deep learning de Fast.AI, me sorprendió la cantidad de dependencias de Python que arrastra un proyecto de machine learning
Siempre pensé que los proyectos de frontend web tenían muchas dependencias de terceros, pero a mí el ecosistema de machine learning me parece mucho más enredado
Además, el desarrollo web siempre se ha considerado sensible a la seguridad, así que ha acumulado mucha sabiduría y prácticas al respecto, mientras que el desarrollo de ML parece mucho más improvisado y como que ni siquiera aplica muchas prácticas generales de ingeniería de software
Por ejemplo, en ese momento una de las formas de desplegar modelos de ML era con Python pickle, que es básicamente un objeto ejecutable sin restricciones por defecto
Un modelo en ese formato podía hacer cualquier cosa en la computadora que lo importara, y ese ecosistema tipo lejano oeste de los inicios puede facilitar mucho más las intrusiones de seguridad y los ataques a la cadena de suministro
Algunos aprendieron algo de programación sobre la marcha, algunos son matemáticos, y otros son como desarrolladores intoxicados con la IA
También existe la mentalidad de “el código ya no importa, mientras funcione”
Para mucha gente, una gestión de dependencias adecuada no es más que trabajo tedioso del que no quiere ocuparse
En muchos proyectos de machine learning se combinan todos esos factores, cuando en realidad los proyectos de ML deberían ser de los que más se enfocan en la reproducibilidad
Busqué en repositorios y me salieron 2.2 mil repositorios creados en el último día que contienen el texto
"A Mini Shai-Hulud has Appeared": https://github.com/search?q=A%20Mini%20Shai-Hulud%20has%20Ap...Eso parece indicar que se usaron para crear repositorios después de comprometer cuentas, probablemente tokens de autenticación de GitHub/Actions
Ya había pasado algo parecido antes, así que pensé que habrían aprendido la lección
Este malware no se esforzó mucho, y Microsoft tampoco parece estar esforzándose mucho
Para ser claro, nunca he usado pytorch y tampoco sé mucho de prácticas de seguridad de software
Pero no se me ocurren muchos escenarios en los que pytorch necesite acceso a red
Parece incorrecto que desde cualquier parte del código base se pueda importar cualquier módulo y usar esa API
Da la impresión de que hacen falta restricciones adicionales de importación o análisis estático
El lenguaje no parece tener la abstracción adecuada para tratar estos problemas
En comparación, me gusta que en Rust puedas ver mutabilidad y lifetimes solo con la firma de una función, sin entender el código interno
Siento que hace falta algo parecido para las dependencias
Los desarrolladores deberían poder auditar fácilmente todas las dependencias sin meterse al código inferior y ver “ah, esta dependencia usa
eval()” o “tiene acceso a red”Las apps móviles obligan a pedir permisos; uno pensaría que los desarrolladores también deberían poder poner en lista blanca solo ciertas capacidades, en lugar de tragarse bloques enormes de funcionalidad
No me gusta generalizar, pero parece que la comunidad de desarrollo de IA en particular prioriza la comodidad por encima de cualquier otra consideración
Por ejemplo, ya casi es estándar que un proyecto descargue automáticamente un modelo grande en la primera ejecución
Normalmente se puede desactivar, pero encontrar el parámetro correcto es un dolor terrible por las capas profundas de clases y código repartidas entre varias librerías
Está bien que sea muy fácil empezar con cosas complejas, por lo general casi juguetes, pero este ambiente permisivo es bastante incómodo
Siento que el primer paso para resolver cualquier problema siempre es “
pip install …”, y algunos entornos, como MacOS, ni siquiera virtualizan bien el acceso a GPUEsta semana me estaba preguntando si era buena idea usar uv para gestionar versiones de Python
El sitio web [1] dice: “Como Python no ofrece binarios oficiales para distribución, uv usa distribuciones del proyecto Astral python-build-standalone”
Apunta a este repositorio de GitHub https://github.com/astral-sh/python-build-standalone, y ahí luego menciona https://gregoryszorc.com/docs/python-build-standalone/main/r...
Si lo entendí bien, parece que no toman directamente de python.org el código fuente para compilar Python, y no estoy seguro de qué tan seguro sea eso
Tengo la misma preocupación con asdf [2], aunque asdf usa pyenv [3] y eso me suena más cercano a lo oficial
¿Alguien puede explicar cuál herramienta es mejor y más segura entre uv y asdf para instalar Python?
[1] https://docs.astral.sh/uv/guides/install-python/
[2] https://github.com/asdf-community/asdf-python
[3] https://github.com/pyenv/pyenv/tree/master/plugins/python-bu...
De hecho, ¿de dónde más lo sacaría?
[1]: https://github.com/astral-sh/python-build-standalone/blob/a2...
uvycpythonno me preocupan demasiado. El proceso es sólido, responden rápido y ahora además tienen bastante financiamientoLo que sí me preocupa es, por ejemplo, un formateador como
mdformat, muy usado pero mantenido principalmente por una sola persona en su tiempo libre, o una dependencia súper específica que lleva años sin actualizarse y está tres niveles abajo en el árbol de dependenciasNo quiero fijar y aprobar manualmente todas las actualizaciones en una app desarrollada activamente, pero para una app seria eso ya empieza a parecer obligatorio
Mientras tanto voy a sacar las claves API de archivos
.envsin cifrarSi te pasa en una gran webapp de consumo da vergüenza, pero se entiende; perder cientos o miles de dólares por una dependencia indirecta de un repositorio demo de juguete que casualmente está en el mismo host y sistema duele muchísimo
¿Alguien sabe si OAI o Anthropic reembolsan cuando te roban claves de esta manera, o se considera error del usuario?
No sé cuánto cambia el riesgo si ellos compilan los binarios de Python o si los compila alguien más
Últimamente la mayoría de mis
pip installvienen de sugerencias de Claude Code, y yo solo aprieto EnterEl modelo fue entrenado con datos de hace unos meses, así que no tiene forma de saber qué se comprometió esta semana
Básicamente armé el peor filtro posible para decidir si “este paquete es seguro en este momento”
Dijiste que dejas que Claude Code recomiende software para instalar desde internet, y luego lo instalas tal cual
Nunca he oído a nadie sugerir que Claude Code, ni ningún LLM, sea un filtro para decidir “si este paquete es seguro en este momento”, y por las razones que mencionaste parece una heurística pésima
setup.pyen tu máquinaEso es porque no hay una inspección real del paquete antes de ejecutarlo
Lo que hace falta es una herramienta que obtenga metadatos antes de ejecutar nada y verifique qué hooks existen
Claude Code se actualiza casi todos los días, a veces varias veces por día
Si algún día comprometen Anthropic, a todos nos va a ir muy mal
Pero hoy en día todo es YOLO
Vi este mensaje en GitHub del 20 de abril y me dejó algo confundido
"deependujha hi @thebaptiste, thanks for inquiring. Release of 2.6.2 is blocked due to some internal reasons. Will notify once release is made."Si sabían del problema desde entonces y no advirtieron hasta ahora, eso sí me parecería muy mal
Ojalá alguien que sepa más pueda aclararlo bien
https://github.com/Lightning-AI/pytorch-lightning/issues/216...
Antes de eso no había distribuciones afectadas y no sabíamos de la filtración
La release original en GitHub no tenía problema, pero la bajamos para evitar confusiones