Back
Adversary Intelligence
Table of Content

Executive Summary

CloudSEK TRIAD identified a sophisticated npm supply chain attack involving the typosquatted package crypto-javascri, which harvested npm and GitHub credentials and used compromised maintainer accounts to silently republish trojanized packages. The final payload was a weaponized Arti Tor client with credential theft, persistence, and Tor based C2, enabling resilient and difficult to disrupt attacker operations.

Introduction

The npm ecosystem processes billions of package installations every week. At that scale, even a marginal error rate, a mistyped package name, a copy pasted dependency translates into thousands of affected machines. Threat actors have long understood this, and the past several years have seen a steady stream of typosquatting and dependency confusion attacks targeting developers precisely because of it.

CloudSEK TRIAD recently found an undetected npm supply chain attack that delivers a final-stage implant with Tor-based command and control. Using Tor as C2 is an established tradecraft in targeted intrusions. Deploying it through a mass-scale npm typosquat is a meaningful escalation one that renders conventional network-layer defences largely ineffective. There is no domain to block, no IP to sinkhole, no certificate to fingerprint.

On 11 May 2026, a threat actor published crypto-javascri to the public npm registry one character removed from the widely used ##crypto-js## library. The package carried a Rust binary that harvested npm and GitHub credentials on installation, then used those credentials to inject itself into every package the victim maintained, propagating silently without any further attacker involvement. The final payload was a weaponised build of Arti the official Rust Tor client extended with credential theft, cryptomining, privilege escalation, and systemd persistence.

How the Package Got Into the Supply Chain

##crypto-js## is one of the most installed cryptography libraries on npm tens of millions of weekly downloads, used across frontend and backend JavaScript projects alike. A one-character transposition (##crypto-js → crypto-javascri##) is the kind of typo that slips past a casual review of a ##package.json## diff. The attacker registered the package under a throwaway account (##enge31##) with a matching Outlook address and set about making it look credible.

The decoy was well constructed. The ##lib/## directory contains a functional implementation of SHA-256, HMAC, PBKDF2, AES-CBC, and Base64, a near verbatim copy of the legitimate ##crypto-js## source. ##index.js## exports all of these correctly.

Between 14:39 and 21:21 UTC on 11 May 2026, the actor published 22 versions in a single session:

Time (UTC) Version Notes
14:39 1.0.0 Initial publish
14:41 1.0.1 2 minutes later
15:09 1.2.0 Jump implying fictitious history
16:11 1.2.1 Version analysed in this report
16:18–16:38 1.2.5–1.2.12 Rapid-fire, 1–11 min apart
16:41–16:57 1.3.5–1.4.1 Continued escalation
20:30–21:08 1.4.2–1.4.5 Late session
21:15 3.0.0 Major version jump
21:21 3.0.1 Final publish

The jump from ##1.4.5## to ##3.0.0## is deliberate. ##[email protected]## is the most widely installed legacy version of the legitimate library. Publishing 3.0.1 places crypto-javascri ahead of [email protected] in semver resolution for any consumer with "##crypto-js##": "^##3##" in their package.json who has a typo in their lockfile or ##.npmrc##. The rapid version cadence mimics an actively maintained project and is consistent with the attacker testing automated worm propagation across each iteration

Version Published Hit
1.0.0 - 1.4.1 2026-05-11 14:39 - 16:58Z clean (~7 KB starter / multi-MB filler)
1.4.2 2026-05-11 20:30Z HIT package/.claude/settings
1.4.3 2026-05-11 20:38Z HIT package/.claude/settings
1.4.4 2026-05-11 21:03Z HIT package/.claude/settings
1.4.5 2026-05-11 21:08Z HIT package/.claude/settings
3.0.0 2026-05-11 21:15Z clean (payload temporarily removed)
3.0.1 2026-05-11 21:21Z HIT package/.claude/settings

Three Ways In 

Opening the tarball, the package contains eleven files. Ten of them are JavaScript decoy. The eleventh is ##.claude/binary## a 6.4 MB compiled Rust ELF binary. Three separate mechanisms ensure it executes on the victim's machine:

1. npm ##preinstall## hook

This fires before any dependency resolution completes before the developer's own code runs, before any post-install step.

2. Claude Code ##SessionStart## hook

Claude Code evaluates ##.claude/settings.json## in the current working directory on every session start. If the developer opens Claude Code in a project that has this package installed, the binary executes again outside any npm install flow, silently.

3. VS Code ##folderOpen## task

VS Code's ##runOn: folderOpen## executes the task automatically whenever the workspace is opened. No user interaction is required here.

Sample Analysis

Once the binary executes, it works through a straightforward but effective sequence. The source module layout recovered from the binary's symbol table tells the story clearly:

Module Purpose
src/users.rs Locate and read credential files
src/npm.rs Validate tokens, enumerate packages, repack and publish
src/github.rs GitHub API interaction, repository enumeration
src/preinstall.rs Inject lifecycle hooks into victim packages
src/main.rs Orchestration and async executor

Step 1 - Credential Harvesting

The binary opens two files:

  • ##~/.npmrc## - reads the ##_authToken## field
  • ##~/.git-credentials##- reads the GitHub personal access token

Both paths are hardcoded. The binary enumerates home directories under /home first, then falls back to ##/root##. It validates each token against the live registry before proceeding:

  • https://registry.npmjs.org/-/whoami  ← npm token validation
  • https://api.github.com/user  ← GitHub token validation

It also checks specifically for ##bypass_2fa## capability on npm tokens, a flag that indicates a token can be published without two factor confirmation.

Step 2 - Maintainer Enumeration

With a validated npm token, the binary queries the registry for every package the victim maintains:

https://registry.npmjs.org/-/v1/search?text=maintainer:<victim>

In parallel, it uses the GitHub token to enumerate the victim's repositories and fetch ##package.json## files via the GitHub contents API giving it a secondary view of which packages the victim owns even if registry search returns incomplete results.

Step 3 - Self-Propagation

For each package the victim maintains, the binary:

  1. Downloads the current published tarball
  2. Unpacks it to ##/tmp/npm_tarball.tgz##
  3. Injects itself the binary, ##.claude/settings.json##, and ##.vscode/tasks.json##
  4. Adds ##"preinstall": "./.claude/binary"## to the package's ##scripts## field
  5. Bumps the version (e.g. ##1.4.3 → 1.4.4##)
  6. Repacks and publishes under the victim's own npm token

The victim's token signs the publish. The attacker's infrastructure is never involved. Downstream users who install the bumped version see a routine patch release from a maintainer they trust.

The binary also pads itself when injecting (##add_elf_padding## symbol recovered from the binary) varying its on disk size across generations to break naive hash based detection.

Inside the Malicious Package — Static Analysis of the Implant

The worm binary (##crypto-javascri's .claude/binary##) is the propagation engine. But the final-stage payload the settings binary dropped onto victim machines is where the campaign's full capability set becomes visible.

The binary is a modified build of Arti 1.1.x the official Rust implementation of the Tor protocol, developed by the Tor Project. The malicious implant is layered on top as additional Rust code that hooks into Arti's runtime. The .rodata section contains a large region (~2MB) of moderately high entropy; this is the legitimate Arti TLS certificates, Tor consensus data, and embedded documentation. However, embedded within this region at offset 0x1A1F7C (decimal 1,709,372) lies a specifically placed hex-encoded Python payload as well.

This choice is deliberate. A 6.6 MB binary that passes file as a standard ELF and contains legitimate Tor protocol code is considerably harder to dismiss on first inspection than a purpose-built malware stub. The Arti data and cache directories (~/.local/share/arti/, ~/.cache/arti/) that appear on disk after execution look like a developer tool, not an implant.

Cloud detection gate:

The malware checks for cloud provider metadata endpoints. If no cloud environment is detected, it exits cleanly. This is a common technique to avoid analysis in sandboxed environments and to target specific infrastructure (AWS EC2, Azure VMs, GCP Compute instances).

Credential Theft 

The credsgit format is a standardized output format for stolen credentials, it prefixes GitHub related thefts with this marker, NPM thefts with npm: , etc. The envinside ci env string detects if the malware is running inside a CI/CD environment (like GitHub Actions, Travis CI, or CircleCI)

Agent Startup and Heartbeat

The malware bootstraps a full Tor client, builds a circuit to a hidden service, and maintains a heartbeat. The make circuit to hidden service string specifically relates to Arti's HS client implementation.

Crypto and Mining Targets

The presence of struct MinerConfig confirms a cryptomining component. The field names ( XM_POOL ,XM_ADDRESS , XM_MAX_THREADS_HINT ) follow XMRig convention. Crypto wallet targets (exodus, atomic-wallet, ledger-live) suggest the attacker harvests cryptocurrency keys, not just tokens.

Stage 2 Payload

Searching for hex-encoded Python patterns in the binary revealed a continuous string of hexadecimal characters at binary offset 1,709,372 (0x1A1F7C). The string begins with 78daab77f57163626464... - the 78da magic byte is the zlib compression header.

Decoded and decompressed, it yields a 25-line Python 3 dropper:

The dropper uses ##AF_ALG## sockets, the Linux kernel's crypto subsystem interface to deliver the Stage 3 shellcode. This is not a conventional network socket. Traffic through ##AF_ALG## is invisible to standard network monitoring tools. The shellcode is sent in 4-byte chunks via ##sendmsg()## ancillary messages, further reducing the signal available to runtime detectors.

Stage 3 LPE Payload

Decompressing the zlib blob inside the Stage 2 dropper yields the Stage 3 payload a 218-byte position independent ELF:

The stage 3 payload is a minimal privilege escalation shellcode (218 bytes):

  • setuid(0): Calls sys_setuid(0) to drop effective UID to 0 (root). This assumes the binary is running with SUID bit set (targeting /usr/bin/su )
  • Command Execution: Receives the command to execute from the parent stage via the AF_ALG socket, then calls execve("/bin/sh", ["/bin/sh", "-c", cmd], NULL)
  • Graceful Exit: On any error, calls exit(0)

This is not a sophisticated kernel exploit it simply exploits the SUID bit on /usr/bin/su. If thesystem has a vulnerable su binary, this shellcode triggers it. On a patched system, the shellcode would fail and exit.

Persistence - The Systemd Implant

Following credential theft and payload staging, the binary establishes persistence through:

  • ~/.local/bin/systemd-broker ← copy of the main binary
  • ~/.config/systemd/user/systemd-broker.service

The service file:

##loginctl enable-linger## is called to ensure the service survives user logout. ##Restart=always## ensures it recovers from kills. The binary is timestomped to match the mtime of legitimate system binaries, reducing the signal available to filesystem based detectors.

Strace output

TOR C2

1cpur2zdsv762uzyoyzma6pvzz4a2xhv64zdouxpjlu3exyks7gh7leyd.onion:80

-> Using Tor as the command-and-control (C2) channel gives the attacker both anonymity and resilience. Instead of exposing a public IP or domain that defenders can trace or block, the malware connects to a Tor hidden service whose real hosting location remains concealed. Because it embeds the legitimate Arti Tor client, its traffic also resembles normal Tor activity rather than a custom malware C2 channel.

-> Tor also gives the attacker infrastructure flexibility. A hidden service’s .onion address is tied to its cryptographic identity, not the backend server’s IP. As long as the attacker retains the private key, they can move the C2 infrastructure to new hosts while keeping the same hardcoded onion address, making disruption significantly harder.

Attribution

Based on information obtained from underground sources, this campaign has been linked to the threat actor Sukob, reportedly a former member of the HellCat ransomware ecosystem. While the technical evidence in this sample does not independently confirm that attribution, the tradecraft aligns with the broader profile.

An interesting possibility is renewed collaboration between Sukob and operators associated with Team PCP. Team PCP has historically been linked to aggressive software supply chain abuse attacks like Shai Hulud worm, and underground reporting suggests overlap between some former HellCat-affiliated members and that ecosystem. The worm-like propagation model in this campaign stealing maintainer credentials and silently republishing trojanized packages under trusted identities fits that operational pattern. At present, however, this remains an intelligence led hypothesis rather than a technically proven attribution.

Impact & Mitigations

This campaign targets Linux developer systems and CI/CD environments where npm and GitHub credentials are present. Because the malware validates stolen tokens and automatically republishes trojanized packages under trusted maintainer accounts, a single compromised developer machine can quickly become a supply chain propagation point affecting downstream users.

Although the observed onion C2 is currently offline, that should not be considered remediation. Since the malware uses a Tor hidden service, the attacker can redeploy infrastructure while retaining the same onion identity. Defenders should prioritize:

  • Monitoring for suspicious npm preinstall hooks or unexpected execution from hidden project directories such as ./.claude/
  • Auditing for unauthorized package publishes, access to ~/.npmrc / ~/.git-credentials, and immediate revocation of compromised tokens
  • Alerting on unexpected Tor/Arti activity or user-level persistence via ~/.local/bin/ and systemd user services

References

CloudSEK TRIAD
CloudSEK Threat Research and Information Analytics Division
No items found.

Related Blogs