3 puntos por GN⁺ 2025-10-31 | 2 comentarios | Compartir por WhatsApp
  • Se confirmó que en el repositorio de NPM se subieron más de 100 paquetes maliciosos para robar credenciales sin ser detectados desde agosto, y que en conjunto fueron descargados más de 86 mil veces
  • La empresa de seguridad Koi informó que una campaña de ataque llamada PhantomRaven distribuyó 126 paquetes maliciosos aprovechando la función Remote Dynamic Dependencies (RDD) de NPM
  • RDD es una estructura que permite que un paquete descargue dinámicamente código de dependencias desde dominios no confiables, por lo que no es detectada por herramientas de análisis estático
  • Los atacantes aprovecharon esta función para descargar código malicioso mediante conexiones HTTP y, en los metadatos del paquete, aparecía como “0 Dependencies”, por lo que ni los desarrolladores ni los escáneres de seguridad lo detectaban
  • Esta vulnerabilidad estructural expone los límites de la gestión de seguridad del ecosistema de NPM y los riesgos del mecanismo de instalación automática

Expansión de paquetes maliciosos en el repositorio de NPM

  • Los atacantes aprovecharon una debilidad estructural del repositorio de código de NPM para subir, desde agosto, más de 100 paquetes para robar credenciales
    • La mayoría de los paquetes se distribuyó sin ser detectada, y el total acumulado de descargas superó las 86,000
  • La empresa de seguridad Koi nombró este ataque como la campaña PhantomRaven y analizó que se explotó una función específica de NPM
    • Según Koi, de los 126 paquetes maliciosos, alrededor de 80 seguían todavía en NPM al momento de redactarse el artículo

Estructura vulnerable de Remote Dynamic Dependencies (RDD)

  • RDD es una función que permite que un paquete descargue dinámicamente código de dependencias desde sitios web externos
    • Normalmente las dependencias se descargan desde la infraestructura confiable de NPM, pero RDD también permite descargas mediante conexiones no cifradas como HTTP
  • Los atacantes de PhantomRaven configuraron esta función para descargar código desde una URL maliciosa, por ejemplo http://packages.storeartifact.com/npm/unused-imports
    • Estas dependencias no son visibles para los desarrolladores ni para los escáneres de seguridad, y en la información del paquete aparecen como “0 Dependencies”
  • Debido a la función de instalación automática de NPM, este código de dependencia “invisible” se ejecuta automáticamente

Límites de detección de las herramientas de seguridad

  • Oren Yomtov, de Koi, señaló que “PhantomRaven es un caso que explota con precisión un punto ciego de las herramientas de seguridad existentes
    • RDD no es detectado por herramientas de análisis estático
  • Por esto, los atacantes pudieron evadir las verificaciones de seguridad y distribuir código malicioso

Factores adicionales de vulnerabilidad

  • Koi explicó que las dependencias descargadas mediante RDD se vuelven a descargar desde el servidor del atacante en cada instalación
    • Como no hay caché ni control de versiones, incluso el mismo paquete podría inyectar código malicioso distinto según el momento de instalación
  • Esta estructura de descarga dinámica dificulta la verificación de integridad de los paquetes

Estructura y contexto de NPM

  • NPM es un administrador de paquetes para JavaScript gestionado por npm, Inc., una subsidiaria de GitHub
    • Es el administrador de paquetes predeterminado de Node.js y está compuesto por un cliente de línea de comandos y el npm registry
    • En el registry se almacenan paquetes públicos y privados de pago, y pueden buscarse a través del sitio web
  • Este incidente se señala como un caso que muestra cómo la estructura de gestión automática de dependencias de NPM puede ser explotada en ataques

Otras menciones

  • Al final del artículo se menciona la opinión de que debería bloquearse la ejecución innecesaria de JavaScript
    • Sin embargo, se señala que en este ataque incluso código JavaScript esencial fue utilizado de forma maliciosa

2 comentarios

 
developerjhp 2025-11-25

Creé un script de escaneo en tiempo real.

En la ruta del repositorio sospechoso,
npx sha1-hulud-scanner
solo tienes que ingresarlo.

Código fuente: https://github.com/developerjhp/sha1-hulud-scanner

 
GN⁺ 2025-10-31
Opiniones en Hacker News
  • Últimamente tengo un alias para ejecutar el comando npm dentro de un contenedor Docker
    Así no expone mis variables de entorno, no accede a archivos fuera del directorio actual y tampoco puede acceder a archivos de configuración como .bashrc
    Referencia: Run tools inside Docker

    • Eso parece un sandboxing excesivo. Al final, npm descarga código arbitrario que se va a ejecutar de inmediato
      En su lugar recomiendo pnpm. Por defecto no ejecuta scripts de lifecycle y permite definir en una lista blanca cuáles scripts se autorizan
    • Demonizar los scripts post-install solo da una falsa sensación de seguridad
      Si de verdad quieres protección, no solo la instalación sino toda la ejecución debería ir dentro de un sandbox
      Bloquear solo el post-install, como ahora, no es más que una medida a medias. Los ataques a la cadena de suministro son cada vez más peligrosos
    • Hay demasiados vectores de ataque. Si alguien tiene intenciones maliciosas, puede hacer typosquatting del nombre de un plugin o LSP popular para que el editor ejecute código automáticamente al iniciarse
      Si neovim o vscode quedan comprometidos, ya pueden hacer cosas bastante peligrosas con los permisos del usuario
    • Yo uso sandbox-run
      Un alias simple sirve para node/npm, pero es difícil aplicarlo a otros programas, porque hay que montar en el contenedor los recursos que necesitan
    • Pero al final igual podrías descargar un paquete malicioso, ¿no? La dependencia en sí podría estar comprometida
  • Siempre me he preguntado por qué la gente ejecuta npm en su sistema con tanta naturalidad
    Desde la perspectiva de alguien acostumbrado a builds reproducibles como make, me impactó que npm descargue cosas distintas cada vez y produzca resultados distintos
    Incluso me parecía raro amarrar la generación de CSS a dependencias de npm. Por eso intenté congelar todo el entorno de npm dentro de Docker, pero al final parece una batalla perdida

    • Hoy en día todos los package managers funcionan así. maven, nuget, pip, npm, todos son iguales
      Si siguiéramos dependiendo de los package managers de las distribuciones como antes, sería imposible tener un ecosistema tan rápido como el actual
      Aun así, están apareciendo nuevos package managers con más seguridad. No está bien criticar el medio sin entender la razón de fondo
    • El desarrollo frontend se siente como una especie de Viejo Oeste de “confía en mí, bro”. Por la evolución del navegador, da la impresión de que todo está pegado con cinta adhesiva
    • Si congelaste npm con Docker, me pregunto si validabas ese entorno cada vez que actualizabas dependencias
      En realidad npm y pnpm ya fijan las dependencias por defecto con archivos lock
    • Es un problema causado porque “npm install thing” es demasiado fácil y barato
      Mucho open source termina lleno de código para el currículum más que por calidad, y al final se usa para hacer cosas como rastreadores publicitarios de grandes empresas o apps de billetera
  • npm install no solo descarga paquetes, también ejecuta código
    De hecho corre los hooks preinstall, install y postinstall del package.json
    ¿Cuál sería una razón legítima para necesitar ejecutar comandos arbitrarios durante la instalación?
    Informe relacionado: PhantomRaven npm malware
    Otro caso: Blog de Socket.dev

    • En realidad, esta estructura ya existía en package managers antiguos como DEB y RPM
      Por ejemplo, los paquetes del kernel de Linux ejecutan scripts post-install para regenerar initramfs y actualizar GRUB
      La mayoría de los paquetes DEB/RPM incluyen scripts así. O sea, es un problema de diseño en sí
    • El problema es que en npm cualquiera puede subir un paquete
      Las distribuciones Linux tienen un sistema confiable de maintainers y hasta construyen directamente una raíz de confianza basada en PGP
      En cambio, npm, pip, rubygems, cargo y otros son básicamente una versión refinada de “curl | bash
    • Por ejemplo, el proyecto Mediasoup es una librería de streaming escrita en C++ y compila el código fuente durante la instalación
      Ese tipo de build post-install fue necesario para reducir la carga de mantenimiento
    • Swift Package Manager también ejecuta realmente el archivo Package.swift
      Pero tengo entendido que está fuertemente sandboxeado, así que es difícil abusar de eso
      Referencia: Documentación de SwiftPM, PackageDescription
    • Por cierto, pnpm v10 desactiva por defecto todos los scripts de lifecycle y el usuario tiene que permitirlos manualmente
      Discusión relacionada
  • Viendo los ataques recientes a npm, ya me pregunto si desarrollar con npm sigue siendo seguro
    Cada vez que empiezo un proyecto en React se instalan cientos de paquetes y ni sé qué hacen
    En backend se instalan explícitamente solo los paquetes necesarios, pero el frontend parece una caja de Pandora de vulnerabilidades

    • En realidad todos los ecosistemas de lenguajes son parecidos. Solo que npm es el más grande y por eso sale más en las noticias
    • Instalé jj de Rust y metió 470 paquetes; wan2gp de Python, 211. Al final todos están más o menos igual
    • El ecosistema de JavaScript es estructuralmente vulnerable a ataques
      Como en el caso de xz, cada dependencia queda en manos de personas aleatorias, y hay que confiar en que no caerán en ataques de ingeniería social
    • Cuantas menos dependencias, mejor. Cero es perfecto. Esa sí es la verdadera victoria
    • Por cierto, PyPI tampoco es seguro. Incluso hubo casos en que se inyectó código malicioso en paquetes legítimos mediante el hackeo de GitHub Actions
  • Cada vez que desarrollo con frameworks como Angular o Vue me da ansiedad
    Ver miles de dependencias dentro de node_modules se siente como el anuncio de un desastre
    Si hacen phishing a un solo desarrollador de open source, todo podría infectarse de inmediato
    El ecosistema de JavaScript está roto de raíz. Un simple typo basta para quedar expuesto a un ataque a la cadena de suministro
    Con NuGet o Maven también puede pasar, pero allá la biblioteca estándar es más grande, hay menos dependencias y se siente que tienes más control

    • Go usa URLs de repositorio en lugar de nombres de paquetes, lo que reduce el typosquatting
      No es perfecto, pero al menos es un paso adelante
    • Deno resuelve este problema. Es un problema estructural de Node.js / npm
  • De esas 86,000 descargas, es muy probable que la mayoría no hayan sido usuarios reales sino scanners automatizados o bots
    Cuando subes una versión nueva, a veces se descarga cientos de veces en uno o dos días, pero puede que no sea gente real
    O sea, quizás casi no hubo usuarios infectados

    • A mí también me pasó: cuando publiqué una librería, al principio tuvo unas 300 descargas por semana y luego unas 100
      También hay muchos ataques dirigidos a nombres de paquetes inventados por alucinaciones de chatbots de IA. Es más que una simple estadística
    • O quizá sea el CI zombi de alguien descargándolo una y otra vez
    • Pero si el ataque apuntaba a nombres falsos de paquetes generados por LLM, entonces sí podría haber muchos desarrolladores infectados de verdad
  • Para una explicación más detallada del ataque, ver el artículo de BleepingComputer

  • Me pregunto si habrá alguna forma de detectar o filtrar durante npm install los paquetes que usan URLs HTTP como dependencia
    Como pueden enviar payloads distintos según quién haga la solicitud, los scanners comunes lo tienen difícil para detectarlos

  • Como desarrollador aficionado, me pregunto cómo debería prepararme ante este tipo de ataques a la cadena de suministro
    Cuando sigues tutoriales conocidos e instalas dependencias, sin darte cuenta te vuelves descuidado con la seguridad
    También corro varios servicios en mi homelab, y me preocupa que algún bot llegue a infiltrarse. ¿Por dónde debería empezar?

    • Si separas los servicios en contenedores o VMs, puedes aislar el daño
      No es una garantía perfecta, pero es mucho mejor que dejar que comprometan todo el servidor
    • Hay que ver todas las dependencias como un riesgo potencial de seguridad y usarlas solo cuando de verdad hagan falta
      Copiar y usar directamente solo el código necesario también puede ser una buena forma de aprender y una opción más segura
    • Es más seguro usar releases populares y con más de un año. Si hubiera habido problemas, es más probable que ya se hubieran detectado
    • También existen sistemas como FreeBSD que usan package managers del sistema
      Con una estructura así, no hace falta que millones de usuarios verifiquen todo por su cuenta: la confiabilidad se asegura a nivel de distribución
    • Prefiero paquetes con más de 1 millón de descargas semanales y sin dependencias
      Ejemplos: Hono, Zod
      Últimamente me cambié a Bun, que ya trae integrados cosas como drivers de DB o clientes S3, así que hay menos descargas adicionales
  • La estructura de traer dependencias desde hooks de lifecycle puede convertirse en un punto de giro del ataque en cualquier momento
    Aunque ahora sea legítimo, más adelante el dueño puede ser hackeado o cambiar de idea y convertirlo en código malicioso
    Este tipo de hooks de instalación son, al final, un diseño insostenible