
BLOG
FragMiner: A Triple Threat Hiding in npm, Kernel Exploit, Supply Chain Worm, and Cryptomining from a Single Package
Michael
Baker
TL;DR: crypto-javascript@4.2.5
is one npm package that installs three things at once: a pre-disclosure kernel exploit, a supply chain worm that spreads across six build ecosystems, and a Monero miner. All three run from memory, leaving no named file on disk. The embedded kernel exploit carries a GCC build timestamp of 2026-04-30 1, seven days before public disclosure of the DirtyFrag vulnerability 23. ELF timestamps can be forged, but if this one is accurate, the actor had working weaponized code while the kernel team was still under embargo.
Executive Summary
crypto-javascript@4.2.5 appeared on npm on May 12 2026, masquerading as a cryptography utility. Its package.json contained a preinstall hook pointing at .claude/settings, a filename chosen to hide in plain sight within Claude Code project directories, which normally contain a .claude/settings.json file. One missing extension is all that separates a JSON config from a Linux ELF binary that fires the instant npm install runs.
Panther’s npm analysis pipeline alerted on the package within hours of publication. Querying Panther’s index of the npm registry for all packages associated with that account’s email (s.js.bc.ak.sggs.ksv@gmail.com) uncovered eight more packages across two additional publisher accounts, bringing the FragMiner cluster to nine packages in total. SafeDep independently flagged related packages on 2026-05-13 4. CloudSEK published a deeper technical analysis on 2026-05-14 5.
What makes this campaign notable is what was packed into a single npm package: DirtyFrag, a linux kernel exploit compiled before public disclosure, a supply chain worm that crosses six build ecosystems using a C2-delivered targeting list, and a Monero miner. The operator had pre-staged infrastructure well ahead of the attack — a cluster of 22 Tor Guard relays was deployed on 2026-04-06, 37 days before SafeDep’s public report. One relay, 207.90.194[.]2:443, appears hardcoded in the Stage 2 binary’s fallback relay table as the only entry carrying no fingerprint and no Ed25519 identity key; every other entry carries both. The cluster operator and the malware actor may be the same party. The embedded DirtyFrag LPE binary carries a GCC build timestamp of 2026-04-30 — if accurate, working weaponized code existed while the kernel team was still under embargo.
The actor published crypto-javascript@4.3.1 on 2026-05-15, two days after SafeDep’s report. Two packages remain live on npm as of this writing.
Attack flow
The diagram below shows the full kill chain for crypto-javascript@4.2.5. The worm and mining loop start automatically from Stage 2, requiring no user interaction, or operator command. The LPE (kernel exploit) is operator-gated via a C2-delivered lpe_flag, so it fires only when the operator directed. The five related packages (auth-javascript, iceberg-javascript, ms-graph-types, microsoft-applicationinsights-common, supabase-javascript) use a different Stage 0 variant with an embedded Stage 2 fallback — see “Guidance for Defenders” for how their execution profile differs.

FragMiner kill chain diagram
Technical Analysis
DirtyFrag weaponized before public disclosure
The crypto-javascript@4.2.5 Stage 2 carries a fully compiled 813 KB kernel exploit ELF: source file lpe.c, GCC 16.1.1. The GCC build timestamp embedded in the binary reads 2026-04-30.6 The five related packages carry a distinct 924 KB DirtyFrag variant, cc452646fd25653738be3ec264b55598d681838a2ceaa2208bdae0506587a183, embedded in their separate Stage 2 — both implement the same mechanism. Hyunwoo Kim (@v4bel) submitted DirtyFrag to security@kernel.org on April 29–30 7. The vulnerability was publicly disclosed on oss-security on May 7.8 Sophos published advisory SA-20260508 on May 8.9 Patches were not merged until May 8–10 10.
ELF build timestamps can be modified after compilation, so we cannot verify the actual compilation date from binary analysis alone. If the timestamp is accurate, the actor compiled a working weaponized kernel exploit during the responsible disclosure window, before any public advisory and before any patch.
DirtyFrag (CVE-2026-43284 / CVE-2026-43500) synthesizes two already-patched techniques into a new attack path on kernels where both prior fixes are in place. It escapes to a user and network namespace, installs 54 XFRM Security Associations via NETLINK_XFRM, then races vmsplice and splice to alias pipe buffer pages to /usr/bin/su’s kernel page cache, overwriting the SUID binary without an authorized write syscall. The process doesn’t need root. When it’s done, the malware can exit cleanly and the backdoor stays. /usr/bin/su is just sitting there, corrupted, waiting for the next su invocation.
Affected kernels: CVE-2026-43284 affects ≥4.11; CVE-2026-43500 affects ≥5.3. Any unpatched Linux host that ran this package should be treated as having a root backdoor in
/usr/bin/su.
A worm that crosses six build systems
From the moment Stage 2 starts, the worm reads its own binary from /proc/self/exe, queries npm for up to 250 packages the victim maintains, and walks their GitHub repositories. For each uninfected repository, it uploads the Stage 2 ELF via the GitHub Git Data API and injects build hook files for whichever of these six ecosystems the target repository uses:
Ecosystem | Injected file | Trigger |
|---|---|---|
Rust |
|
|
Cargo |
| build directive → |
Python setuptools |
|
|
Python PEP 517 |
| build-backend hijack |
CMake |
|
|
npm |
|
|
Propagation starts the moment Stage 2 runs, there is no trigger or delay. The C2 delivers a targeting list the binary calls Futureproof, specifying which crates and repositories to prioritize on top of the victim’s own npm footprint.
Each infected repository receives 2–7 commits authored by the attacker’s GitHub account. The PAT is embedded in the binary, not fetched from C2. Commit messages follow the pattern "update <owner>/<repo>". GitHub code search returns no hits for the payload strings, consistent with propagation targeting private maintainer repositories.
Any downstream developer or CI system building from an infected repository repeats the cycle.
A Tor relay cluster deployed before the campaign
The C2 runs over Tor and the Stage 2 uses Arti, the Rust Tor client, containing a hardcoded fallback relay table. Binary analysis of the table at rodata offset 0x21600–0x21900 shows each entry follows the Arti format: IP:port, 40-character fingerprint, base64 Ed25519 key. 207.90.194[.]2:443 is the only entry in the table with no fingerprint and no Ed25519 key — followed by the string unmeasured value, an Arti bandwidth sentinel. Every other entry carries both.
Tor Metrics confirms that IP as fluffypancakes002ca, part of a 22-relay cluster operated by fluffypancakes.dev. All 22 relays share first_seen: 2026-04-06 23:00:00 — simultaneous deployment, 37 days before SafeDep’s blog post 11. Every relay carries Guard and HSDir flags, spread across Canada, the Netherlands, Germany, and the US.
The hardcoded onion address todlfavo4lhvwgvuy6akeutlawypmdf6wkwpdxq74rncy26rsx7sivid[.]onion appears in the same binary at BSS offset 0x285cc8. Both the relay IP and the C2 address are embedded in the same Stage 2 binary.
During Tor bootstrap, before any anonymous circuit exists, Arti contacts a fallback relay directly over TCP. If 207.90.194[.]2:443 is contacted at this stage, the relay sees the victim’s real IP without any Tor anonymization in place.
Twenty-two Guard+HSDir relays across four countries, all deployed simultaneously five weeks before the campaign was publicly detected, with the attacker’s relay as the only entry in the fallback table carrying no identity credentials.
Attribution
The crypto-javascript packages have no confirmed attribution. The related crypto-javascri cluster was attributed to an actor called Sukob by CloudSEK 12, but the two campaigns share a toolkit, not a confirmed operator. Two separate sets of accounts published packages with the same underlying toolkit in the same week.
enge31980@outlook.com published crypto-javascri@1.2.1, @1.4.5, and @3.0.1 — packages published May 11 that CloudSEK attributed to Sukob.
The crypto-javascript packages were published across three distinct accounts:
Publisher email | Packages |
|---|---|
|
|
|
|
|
|
The capability overlap between the two sets of packages is exact: Arti Tor client, XMRig, .claude/settings payload filename, preinstall hook delivery, /usr/bin/su page-cache targeting, supply chain worm, Rust ELF. Six independent characteristics matching across packages from distinct publisher accounts pointing at two different onion C2 addresses. The most likely explanation is a shared toolkit — a single tooling developer, possibly Sukob itself, supplying the Arti+XMRig dropper framework to at least two operators. The operator behind the crypto-javascript packages remains unidentified.
All three crypto-javascript publisher accounts use the same dot-fragment Gmail construction — letter fragments separated by dots — a pattern documented in the Famous Chollima April 2026 npm campaign (see Panther blogs 1, 2, 3). We don’t think it’s sufficient to attribute this campaign to a DPRK-linked actor. The pattern is easy to copy, no binary-level fingerprint matches any named actor. If the same construction appears in a confirmed campaign, this data will be relevant.
The GitHub token used by the worm is embedded in the binary’s RW data trailer at file offset 0x73c3d0, past the last ELF section — the same trailer mechanism that holds the Tor/C2 credentials. The analysis sample was deliberately sanitized: that region is zeroed, so the actual token is not recoverable. Static analysis ruled out C2 delivery (no token field in the Serde schema), environment variable reads (no getenv calls for GITHUB_TOKEN/NPM_TOKEN), and victim credential file access (no path lookups for .git-credentials, .npmrc, or ~/.config/gh/). The operator’s GitHub account identity is not recoverable from the sanitized binary.
Guidance for Defenders
If your team runs npm install on Linux, including in CI/CD pipelines, treat any system that installed crypto-javascript (or associated packages) in any version as fully compromised.
Check
/usr/bin/suimmediately. If the LPE ran, thesubinary was overwritten in the kernel page cache. DirtyFrag does not invoke an authorized write syscall, so inode metadata including mtime may appear unchanged. Compare the SHA-256 of/usr/bin/suagainst your distribution’s package database directly. Any Linux host, developer workstation or CI runner, that ran this package on a kernel ≥4.11 may have a root backdoor that persists after the malware process exits.Look for Arti keystore artifacts.
$HOME/.local/state/arti/keys/tor+fp-sk/on a non-Tor user’s system is a strong indicator of Stage 2 having run and beaconed.Audit recently installed npm packages in CI. If you have npm install logs from 2026-04-06 onwards on Linux builders, check for
crypto-javascript(versions4.2.5and4.3.1),supabase-javascript(versions2.98.2and2.98.3),auth-javascript@0.0.17,ms-graph-client@3.0.7,ms-graph-types, andmicrosoft-applicationinsights-commonin any version from 2026-04 onwards.crypto-javascript@4.3.1was published on 2026-05-15, two days after SafeDep’s detection, and is confirmed malicious (binary analysis pending).ms-graph-client@3.0.7carries no install scripts; current assessment is target fingerprinting before dropper deployment (analysis pending).ms-graph-typesandmicrosoft-applicationinsights-commonare still live and still accumulating installs.Check your GitHub repositories for unexpected commits. A commit message matching
"update <owner>/<repo>"that adds a binary.claude/settingsfile or a_buildutils.py, modifiedbuild.rs, orpyproject.tomlwithbuild-backend = "_buildutils"is the worm’s signature. The attacker’s GitHub account authored each commit, so it will not look like your own account.Block the onion and relay. Add
todlfavo4lhvwgvuy6akeutlawypmdf6wkwpdxq74rncy26rsx7sivid[.]onionand207.90.194[.]2to your blocklists. If Tor exit traffic is visible in your environment, filter for connections to these addresses.Do not clear the five support packages as benign. If your team installed
auth-javascript@0.0.17,iceberg-javascript@0.8.2,ms-graph-types@2.43.2,microsoft-applicationinsights-common@3.4.2, orsupabase-javascript@2.98.2/2.98.3, the full Stage 2 payload ran — not just Stage 0. These packages carried a Stage 0 variant with an embedded fallback: when the npm download of the Stage 2 payload failed (thenpmj2rgpackage that Stage 0 tried to fetch returned 404), Stage 0 decompressed and executed Stage 2 from its own body, writing it to/dev/shm/.{name}. Stage 2 is a 7.6 MB Rust binary with Arti Tor client, DirtyFrag LPE, systemd persistence, and a Stage 3 downloader — but no mining code of its own. XMRig would have been Stage 3, delivered separately via Tor C2 or npm. Whether the operator ever served that Stage 3 payload is unknown. Check/dev/shmfor any hidden files (names starting with.), check for unexpected services in/usr/lib/systemd/system/, and apply the same/usr/bin/suand Arti keystore checks described above.
Detection
At install time: Alert on package.json preinstall, install, or postinstall hooks pointing at .claude/ or any executable in a hidden dotfile directory.
In syscall telemetry (auditd, eBPF, Falco): memfd_create with name uPX or upx from a process whose parent is npm or node is the loader chain firing. unshare(CLONE_NEWUSER|CLONE_NEWNET) followed by NETLINK_XFRM socket creation and 54+ XFRM_MSG_NEWSA sends within a few seconds is DirtyFrag.
In host forensics: SHA-256 of /usr/bin/su does not match your distribution’s package database (DirtyFrag corrupts the page cache without an authorized write syscall, so inode mtime is not a reliable indicator). $HOME/.local/state/arti/keys/tor+fp-sk/ on a non-Tor user. /dev/shm/.<name> — Stage 2 for the five 19d8f3e7 packages is written here before execution (present unless cleared by the operator). Unexpected service files in /usr/lib/systemd/system/ installed around the time of package install — Stage 2 installs a system service (requiring root via DirtyFrag).
In GitHub: Commits adding a binary .claude/settings file alongside _buildutils.py, a modified build.rs containing fn main() { let _ = std::process::Command::new(".claude/settings").status(); }, or a pyproject.toml with build-backend = "_buildutils". Commits attributed to an account with no prior contribution history to that repository.
Ready for more threat research? View our team's library of work here.
Indicators of Compromise
npm packages
Package | Version(s) | Notes |
|---|---|---|
|
| Primary dropper, CRITICAL; full three-stage loader, DirtyFrag LPE, worm, XMRig |
|
| Re-arm published 2026-05-15, two days post-SafeDep detection, confirmed malicious, binary analysis pending |
|
| Small install-script stub |
|
| Full payload 4.5 MB |
|
| Confirmed malicious |
|
| CRITICAL; still live on npm |
|
| CRITICAL; preinstall ELF hook; bundles legitimate Application Insights SDK; still live on npm |
|
| Recon: no install scripts; target fingerprinting before dropper deployment; analysis pending |
Hashes
SHA-256 | Role |
|---|---|
| Stage 0 install-hook ELF |
| Stage 1 memfd loader (3,339 bytes) |
| Stage 2 recovered Rust ELF |
| Stage 0 variant, 1/64 VT detections |
| Stage 0 variant, 0/65 VT detections |
| Stage 0 variant, 1/65 VT detections |
| Stage 2 ELF — |
| DirtyFrag LPE embedded in |
Network
Value | Type | Notes |
|---|---|---|
| Tor onion | Primary C2 |
| IP:port | Attacker relay |
| Domain | 22-relay sybil cluster operator contact |
| IP:port | Tor guard relay; fingerprint |
| IP:port | Tor guard relay; fingerprint |
| IP:port | Tor guard relay; confirmed in Stage 2 ( |
Host artifacts
Artifact | Notes |
|---|---|
| Hidden ELF payload in npm package; worm propagation filename in victim repos |
| SUID binary overwritten in page cache post-LPE; root backdoor |
| Arti keystore written on first beacon; persistent indicator |
| Stage 0 anonymous backing object |
| Stage 1 backing object |
| LPE exploit environment variable, identifies exploit family |
| Stage 2 staging path ( |
| Systemd persistence service; Stage 2 installs this after DirtyFrag root; |
GitHub worm markers
String | File | Ecosystem |
|---|---|---|
|
| Rust |
|
| Python |
|
| Python PEP 517 |
|
| CMake |
|
| npm |
| Git commit message | All ecosystems |
GCC build date extracted from the embedded LPE ELF; value
2026-04-30. The build date field is written by GCC at compile time and is not independently verifiable from the binary alone.↩︎Hyunwoo Kim (@v4bel) described the submission date in his oss-security disclosure post of 2026-05-07, placing the kernel security team submission on April 29–30.↩︎
oss-security mailing list, 2026-05-07; public disclosure of DirtyFrag (CVE-2026-43284 / CVE-2026-43500).↩︎
SafeDep, “Malicious npm Packages Using Claude Code Hooks,” 2026-05-13. https://safedep.io/malicious-npm-packages-claude-code-hooks/[↩︎](about:blank#fnref4)
CloudSEK, “Inside a Tor-Backed Supply Chain Worm,” 2026-05-14. https://www.cloudsek.com/blog/inside-a-tor-backed-supply-chain-worm#stage-3-lpe-payload; documents
crypto-javascrinpm package with Arti+XMRig payload,.claude/settingsfilename, preinstall hook,/usr/bin/sutargeting, and supply chain worm; attributed to Sukob/TeamPCP (ex-HellCat affiliate).↩︎GCC build date extracted from the embedded LPE ELF; value
2026-04-30. The build date field is written by GCC at compile time and is not independently verifiable from the binary alone.↩︎Hyunwoo Kim (@v4bel) described the submission date in his oss-security disclosure post of 2026-05-07, placing the kernel security team submission on April 29–30.↩︎
oss-security mailing list, 2026-05-07; public disclosure of DirtyFrag (CVE-2026-43284 / CVE-2026-43500).↩︎
Sophos advisory SA-20260508, published 2026-05-08.↩︎
Linux kernel git log: CVE-2026-43284 (xfrm-ESP) fix merged 2026-05-08; CVE-2026-43500 (RxRPC) fix merged 2026-05-10.↩︎
SafeDep, “Malicious npm Packages Using Claude Code Hooks,” 2026-05-13. https://safedep.io/malicious-npm-packages-claude-code-hooks/[↩︎](about:blank#fnref11)
CloudSEK, “Inside a Tor-Backed Supply Chain Worm,” 2026-05-14. https://www.cloudsek.com/blog/inside-a-tor-backed-supply-chain-worm#stage-3-lpe-payload; documents
crypto-javascrinpm package with Arti+XMRig payload,.claude/settingsfilename, preinstall hook,/usr/bin/sutargeting, and supply chain worm; attributed to Sukob/TeamPCP (ex-HellCat affiliate).↩︎
Share:
RESOURCES






