Threat reportSupply ChainTL-2026-1805

Joyfill npm Supply-Chain Compromise: @joyfill/components and @joyfill/layouts Ship Obfuscated Worm-Like RAT and Credential Stealer

criticalACTIVE

Joyfill npm Supply-Chain Compromise (TL-2026-1805), also tracked as Joyfill npm Compromise, is a critical-severity supply-chain compromise, first published 2026-07-28 and last reviewed 2026-08-13. It is attributed to WageMole (North Korea) with medium confidence, affects Joyfill @joyfill/components, maps to 34 MITRE ATT&CK techniques (T1005, T1027, T1036), and is covered by 9 detection rules and 48 indicators of compromise.

Severity
CRITICALAssessed severity
CVEs
0None referenced
Techniques
34MITRE ATT&CK
Actors
3WageMole
Detection rules
9SPL · KQL · Sigma
IOCs
48Indicators of compromise

Key facts for TL-2026-1805

Threat ID
TL-2026-1805
Also known as
Joyfill npm Compromise, js.jadesnow campaign, JADESNOW npm vector
Severity
CRITICAL
Status
ACTIVE
Category
SUPPLY_CHAIN
First published
Last reviewed
Attribution
WageMole, Contagious Interview — DEV#POPPER, PolinRider loader family
Attribution confidence
MEDIUM
Nation-state nexus
North Korea
Motivation
FINANCIAL
Target sectors
software development, open source ecosystem, technology, cryptocurrency, devops ci-cd
Target regions
Global
Detection rules
9
Indicators of compromise
48
Updates
2026-08-13 · 4 updates · revalidated 2×

Malware and tooling in Joyfill npm Supply-Chain Compromise

Malware and tooling: DEV#POPPER, JADESNOW, OmniStealer, EtherHiding, Socket.IO

How Joyfill npm Supply-Chain Compromise works

Malicious beta releases of @joyfill/components and @joyfill/layouts, published to npm on 2026-07-28, embed a five-stage obfuscated payload that resolves C2 addresses through Tron/BNB Smart Chain/Aptos blockchain transactions (an EtherHiding-style dead-drop resolver), opens a Socket.IO remote-access channel, stages a Python credential stealer, and achieves worm-like persistence by injecting a self-reloading loader into the global npm CLI, VS Code, Discord, and GitHub Desktop. Socket-based reporting links the loader family to the DEV#POPPER RAT and OmniStealer credential thief distributed by the North Korea-linked PolinRider supply-chain campaign (Contagious Interview / Famous Chollima), while Malpedia's initial classification (js.jadesnow) ties the blockchain-C2 technique to the separate UNC5342/WageMole DPRK cluster that pioneered EtherHiding.

On 2026-07-28, threat actors published malicious pre-release versions of two legitimate npm packages, @joyfill/components (4.0.0-rc24-2773-beta.4, -beta.5, -beta.6) and @joyfill/layouts (0.1.2-2773.beta.0, -beta.1, -beta.2), within hours of each other. The malicious code was injected exclusively into the compiled distribution bundles shipped in the published tarballs (dist/index.js, dist/index.esm.js, dist/joyfill.min.js for components; dist/index.cjs.js, dist/index.es.js for layouts) — the visible source repositories were untouched, indicating compromise of the registry publishing pipeline or a maintainer's publish credentials rather than a malicious pull request.

The payload's most consequential design choice is that it executes at package IMPORT time, not at npm install time. Because it never uses an npm lifecycle hook (no postinstall script), the widely-recommended defense `npm install --ignore-scripts` provides no protection whatsoever — any application that imports the compromised module triggers the chain the moment it loads.

Stage 1 is an obfuscated bootstrap loader that plants a campaign marker (`global["!"] = "9-0135-3"`), uses a seeded-PRNG string-shuffle decoder to alias core Node primitives under innocuous global names (`global.r = require`, `global.m = module`), and builds a Function-constructor ladder that never spells out the literal words "Function" or "eval" in cleartext to defeat static string-matching detections. A 30-second re-entry guard (`global._p_t`) prevents repeated execution during sandbox detonation, and the decoder function checksums its own source against a stored constant — any tampering (e.g., a debugger inserting breakpoints or a security tool patching the function) silently neuters execution instead of throwing, an anti-analysis pattern consistent with MITRE's Execution Guardrails: Environmental Keying (T1480.001).

Stage 2 resolves command-and-control infrastructure entirely through blockchain lookups rather than a hardcoded server, letting the operator rotate infrastructure at will while making takedown far harder — smart contracts and transactions on public chains are immutable and cannot be seized like a domain or IP. The resolver queries a fixed Tron address (TMfKQEd7TJJa5xNZJZ2Lep838vrzrs7mAP) for its latest outbound transaction; the `raw_data.data` field of that transaction contains a reversed BNB Smart Chain transaction hash. The loader then calls `eth_getTransactionByHash` against a BSC RPC endpoint, and the returned transaction's input field holds an XOR-encrypted payload that is decrypted with a repeating-key cipher. If this path fails, the loader falls back to an Aptos account as a secondary dead-drop pointer. This exact technique — using blockchain read calls as a censorship-resistant C2 dead-drop resolver — is known as EtherHiding, first documented by Google Threat Intelligence Group/Mandiant in October 2025 against the DPRK-linked UNC5342 (aka WageMole/Famous Chollima/Nickel Tapestry/Storm-1877/Void Dokkaebi/WaterPlum/PurpleBravo) cluster's JADESNOW downloader. The resolver runs on two parallel branches — an in-process branch using direct `eval()` and a detached child process — each with its own XOR key and blockchain pointer set, and the detached branch separately queries 23.27.13.43 for a secondary payload.

A campaign-tag variable (`_V`, rendered as HTTP header `Sec-V: A9-0135-3` for the npm infection vector) selects the live C2 endpoint: the npm campaign resolves Socket.IO and upload traffic to 166.88.134.62 (ports 80/443), while other campaign variants of the same loader point to 198.105.127.210 and 23.27.202.27.

Stage 3 is a roughly 77 KB Socket.IO-based RAT (LZString-compressed string table, 337 entries) that fingerprints the host — detecting CI/sandbox environments via hostname markers (`github-runner`, `buildbot`, `sandbox-pool-`, `buildkitsandbox`, `cloudchamber`, WSL2, running as `root`) — and self-heals its own dependency by running `npm install socket.io-client` at runtime if the module is missing. Supported remote commands include `ss_info` (full host/campaign/UUID report), `ss_ip` (geolocation via the legitimate ip-api.com service), `ss_cb` (clipboard theft via PowerShell/pbpaste/xclip-xsel), `ss_upf`/`ss_upd` (file/directory exfiltration via multipart POST to `/u/f`), `ss_eval:`/`ss_eval64:` (arbitrary JavaScript execution, plaintext or base64), `ss_inz:`/`ss_inzx:` (loader injection into other local applications — the worm-propagation primitive), `ss_connect:` (C2 redirection), and `~py` (staging of the Python credential stealer as a detached process).

Stage 4, the Python credential stealer, targets developer workstations directly: browser-stored passwords and cryptocurrency-wallet browser extensions, Git and GitHub CLI credentials/tokens, npm publish tokens, and OS keychains. Stolen data is packed into an encrypted archive staged at `%USERPROFILE%\.npm` (Windows) or `/tmp/.npm` (Linux/macOS) before upload to the C2. Socket-based analysis assesses this stealer with medium confidence as a variant of OmniStealer, a Python infostealer previously documented targeting cryptocurrency wallet private keys, browser session cookies, Git credentials, and cloud API tokens on developer machines.

Stage 5 achieves persistence and self-propagation by injecting self-reloading code blocks — guarded by comment sentinels (`/*C250617A*/`, `/*C250618A*/`, `/*C250619A*/`, `/*C260511A*/`, `/*C260512A*/`, `/*RS260605*/`) that prevent re-injection — into `@vscode/deviceid` (affecting VS Code, Cursor, and Antigravity), the Discord desktop core module, GitHub Desktop's `main.js`, and critically the globally installed npm CLI at `<npm root -g>/npm/lib/cli.js`. Once the global CLI is infected, every subsequent `npm` invocation on that machine re-executes the loader, and any package later built or published from that machine can carry the infection forward — closing a worm-like propagation loop across the npm ecosystem.

Attribution carries two overlapping but distinct hypotheses, both pointing to North Korea. Socket researchers matched the Stage 3 loader's code patterns to the PolinRider loader family and linked the final payload to the DEV#POPPER RAT family, explicitly noting their findings are "based on technical similarities and published research rather than attributing the compromise to a specific threat actor." PolinRider is an active, larger supply-chain campaign (ongoing since December 2025; 108 malicious packages/extensions across npm, Go, Packagist, and a Chrome extension; 162 release artifacts; 1,951 compromised public GitHub repositories across 1,047 owners as of 2026-04-11) attributed to North Korea's Contagious Interview / Famous Chollima cluster, which recruits developers via fake job interviews and poisoned coding-assessment repositories to deliver DEV#POPPER and OmniStealer. Separately, Malpedia's initial classification filed the delivered RAT under the js.jadesnow malware family, an alias of the JADESNOW downloader associated with UNC5342/WageMole — a related but distinct DPRK cluster credited with pioneering the EtherHiding blockchain-C2 technique this payload's Stage 2 resolver reuses. Both attributions converge on financially-motivated, state-linked North Korean operations that fund the regime through developer-targeted credential and cryptocurrency theft; this research documents both hypotheses rather than forcing a single unverified conclusion.

No CVE applies — this is a malicious publication to the npm registry, not a vulnerability in the legitimate Joyfill codebase. Remediation is therefore centered on version rollback, credential rotation, and injection-marker scanning rather than patching.

MITRE ATT&CK techniques used in TL-2026-1805

Collection

T1005 Data from Local System; T1074 Data Staged; T1115 Clipboard Data; T1560 Archive Collected Data

Defense Evasion

T1027 Obfuscated Files or Information; T1036 Masquerading; T1140 Deobfuscate/Decode Files or Information; T1480 Execution Guardrails; T1497 Virtualization/Sandbox Evasion; T1620 Reflective Code Loading

Exfiltration

T1041 Exfiltration Over C2 Channel

Discovery

T1057 Process Discovery; T1082 System Information Discovery; T1083 File and Directory Discovery; T1087 Account Discovery; T1518 Software Discovery; T1614 System Location Discovery

Execution

T1059 Command and Scripting Interpreter; T1129 Shared Modules

Command and Control

T1071 Application Layer Protocol; T1102 Web Service; T1105 Ingress Tool Transfer; T1573 Encrypted Channel

Lateral Movement

T1072 Software Deployment Tools; T1080 Taint Shared Content

Initial Access

T1195 Supply Chain Compromise

Credential Access

T1528 Steal Application Access Token; T1539 Steal Web Session Cookie; T1552 Unsecured Credentials; T1555 Credentials from Password Stores

Persistence

T1554 Compromise Host Software Binary

Resource Development

T1583 Acquire Infrastructure; T1587 Develop Capabilities; T1608 Stage Capabilities

Affected products and versions in Joyfill npm Supply-Chain Compromise

  • Joyfill — @joyfill/components
    Vulnerable versions: 4.0.0-rc24-2773-beta.4; 4.0.0-rc24-2773-beta.5; 4.0.0-rc24-2773-beta.6
    Fixed in: 4.0.0-rc24 (pre-compromise pinned release)
  • Joyfill — @joyfill/layouts
    Vulnerable versions: 0.1.2-2773.beta.0; 0.1.2-2773.beta.1; 0.1.2-2773.beta.2
    Fixed in: 0.1.1 (pre-compromise pinned release)

Remediation for Joyfill npm Supply-Chain Compromise

Patches

  • No CVE or vendor patch applies; the legitimate Joyfill codebase itself contains no defect — remediation is version rollback/removal of the malicious publications, not patching.

Immediate actions

  • Remove @joyfill/components versions 4.0.0-rc24-2773-beta.4, -beta.5, -beta.6 and @joyfill/layouts versions 0.1.2-2773.beta.0, -beta.1, -beta.2 from all package.json files, lockfiles, internal registries/mirrors, and build/CI images.
  • Grep all lockfiles for the '2773' pre-release marker: `grep -rEn 'joyfill.*2773' package-lock.json yarn.lock pnpm-lock.yaml`.
  • Delete node_modules and reinstall from a clean, pinned lockfile using known-good releases (e.g. @joyfill/components@4.0.0-rc24, @joyfill/layouts@0.1.1 or a later verified fixed release).
  • Inspect the global npm CLI (`<npm root -g>/npm/lib/cli.js`), `@vscode/deviceid`, the Discord desktop core module, and GitHub Desktop's `main.js` for the injection-marker comments (/*C250617A*/, /*C250618A*/, /*C250619A*/, /*C260511A*/, /*C260512A*/, /*RS260605*/) and reinstall or reimage any application where a marker is found.

Workarounds

  • Pin dependency resolution to pre-2026-07-28 published versions and block installation of any '2773' pre-release tag via registry policy (npm package allowlisting or a private registry proxy).

Longer-term hardening

  • Rotate all credentials present on any machine that imported the compromised packages: browser-stored passwords and extension wallet keys, Git/GitHub CLI tokens, npm publish tokens, cloud API tokens, and OS keychain secrets.
  • Deploy egress monitoring/allowlisting (e.g. GitHub Actions Harden-Runner) to flag anomalous outbound connections from Node.js/CI processes to blockchain RPC endpoints (Tron, BNB Smart Chain, Aptos) and to the identified C2 IPs during install/build/test phases.
  • Adopt npm provenance/Sigstore attestation verification and pin dependencies to hash-verified releases to reduce exposure to pre-release/beta-channel poisoning.
  • Train developers on Contagious Interview / Famous Chollima social-engineering vectors (fake recruiter outreach, poisoned coding-assessment repositories), given this payload's links to the broader PolinRider campaign.

Weaknesses (CWE) in Joyfill npm Supply-Chain Compromise

CWE-506, CWE-829, CWE-494, CWE-522

Timeline of Joyfill npm Supply-Chain Compromise

  • Google Threat Intelligence Group / Mandiant publishes the first documented nation-state use of EtherHiding (blockchain smart-contract C2) by DPRK-linked UNC5342/WageMole activity — the same dead-drop-resolver technique reused in this payload's Stage 2 blockchain C2 resolver.
  • The PolinRider supply-chain campaign, attributed to North Korea's Contagious Interview / Famous Chollima cluster, begins compromising open-source package maintainers via poisoned GitHub repositories to deliver the DEV#POPPER RAT and OmniStealer credential thief.
  • PolinRider's reach is documented at 108 malicious packages/extensions (162 release artifacts) across npm, Go, Packagist, and a Chrome extension, having compromised 1,951 public GitHub repositories across 1,047 unique owners.
  • The malicious beta versions are unpublished from the npm registry within hours of detection, though cached copies persist on registry mirrors.
  • StepSecurity detonates all six malicious versions in a Harden-Runner sandbox (confirming no install-time scripts or child processes, consistent with import-time execution) and binary-diffs the bundles against clean sibling releases, isolating ~333 lines of injected code.
  • StepSecurity's OSS AI scanning engine automatically flags @joyfill/layouts@0.1.2-2773.beta.0 as CRITICAL with a security score of 0/10 and a REJECTED verdict.
  • Analysts observe that the C2 servers and blockchain resolver pointer addresses were already offline or re-armed within hours of the compromise being disclosed.
  • Malpedia creates a library entry classifying the delivered payload under the js.jadesnow malware family, tying it to the JADESNOW/EtherHiding lineage.
  • StepSecurity researcher Varun Sharma publishes technical analysis of the compromise, documenting the import-time execution mechanism, global npm CLI injection, and the blockchain-based C2 resolver.
  • Malicious pre-release versions of @joyfill/components (beta.4, beta.5, beta.6) and @joyfill/layouts (beta.0, beta.1, beta.2) are published to the npm registry within hours of each other, with the payload injected only into compiled distribution bundles.
  • The Hacker News, GBHackers, CyberSecurityNews, CyberPress, and The Cyber Express republish and expand the technical analysis; Socket researchers link the payload's loader code patterns to the DEV#POPPER/PolinRider family based on technical similarity, without formally attributing the compromise to a specific actor.
  • Archive.org captures a snapshot of the StepSecurity report for preservation.
  • Vendor and community guidance converges on version rollback/pinning, developer credential rotation, and scanning for the Stage 5 injection-marker comments across the global npm CLI, VS Code, Discord, and GitHub Desktop.

Update history for TL-2026-1805

Sources cited for Joyfill npm Supply-Chain Compromise

Detection coverage for TL-2026-1805

As of 2026-08-13, Threadlinqs Intelligence publishes 9 detection rule(s) for TL-2026-1805 across Splunk SPL, Microsoft KQL and Sigma, covering 48 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.

9 detection rules (Splunk SPL, Microsoft KQL, Sigma) · Blue and above. Compare plans
48 indicators of compromise · Red and above. Compare plans

Community OSINT corroboration for TL-2026-1805

2 of this threat's indicators have also been reported by the open-source security community, which observed at least one of them before this report was published. Community sightings are unverified and are kept separate from Threadlinqs' curated indicators. Indicator values, reporters and campaign linkage are available to authenticated Red-tier users.

Further reading

Threadlinqs Intelligence — Real-Time Threat Detection Platform

[ 0 threats ] [ 0 det ] [ CRIT: 0 ] [ HIGH: 0 ]
// threat_feed
$ sort --newest
Showing all threats

Live intelligence console

Threat level
Fig. 01 · Threat weatherIndexing the archive…
1 square = 1 threat · click to open

Latest Threats