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

> 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.

- **Published:** 2026-07-28T00:00:00Z
- **Last reviewed:** 2026-08-13T10:34:10Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-1805
- **ID:** TL-2026-1805
- **Severity:** CRITICAL
- **Category:** SUPPLY_CHAIN
- **Status:** ACTIVE
- **Actor:** WageMole (North Korea)
- **Detections:** 9 · **IOCs:** 48 (full data via the Threadlinqs MCP server — Purple tier)

## Description

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

- T1195 Supply Chain Compromise
- T1059 Command and Scripting Interpreter
- T1129 Shared Modules
- T1554 Compromise Host Software Binary
- T1027 Obfuscated Files or Information
- T1140 Deobfuscate/Decode Files or Information
- T1497 Virtualization/Sandbox Evasion
- T1480 Execution Guardrails
- T1555 Credentials from Password Stores
- T1552 Unsecured Credentials
- T1528 Steal Application Access Token
- T1082 System Information Discovery
- T1614 System Location Discovery
- T1115 Clipboard Data
- T1005 Data from Local System
- T1560 Archive Collected Data
- T1102 Web Service
- T1071 Application Layer Protocol
- T1573 Encrypted Channel
- T1105 Ingress Tool Transfer
- T1041 Exfiltration Over C2 Channel
- T1087 Account Discovery
- T1518 Software Discovery
- T1080 Taint Shared Content
- T1074 Data Staged
- T1587 Develop Capabilities
- T1608 Stage Capabilities
- T1583 Acquire Infrastructure
- T1620 Reflective Code Loading
- T1036 Masquerading
- T1539 Steal Web Session Cookie
- T1083 File and Directory Discovery
- T1057 Process Discovery
- T1072 Software Deployment Tools

## Sources

- [Compromised npm Packages: @joyfill/components and @joyfill/layouts Ship an Obfuscated Remote Access Trojan](https://www.stepsecurity.io/blog/joyfill-npm-supply-chain-compromise)
- [Malpedia library entry: js.jadesnow (Joyfill incident)](https://malpedia.caad.fkie.fraunhofer.de/library/8823bb8a-dd26-4938-8765-53c7e32b7c89/)
- [JADESNOW (Malware Family) — Malpedia](https://malpedia.caad.fkie.fraunhofer.de/details/js.jadesnow)
- [Two Compromised joyfill npm Packages Run RAT When Imported Into Node.js](https://thehackernews.com/2026/07/two-compromised-joyfill-npm-packages.html)
- [Joyfill npm Credential Stealer Targets GitHub Tokens, Browser Passwords and Crypto Wallets](https://cyberpress.org/joyfill-npm-credential-stealer/)
- [Joyfill npm Supply-Chain Attack Deploys RAT and Developer Credential Stealer](https://gbhackers.com/joyfill-npm-supply-chain-attack/)
- [Joyfill Npm Packages Hit By DEV#POPPER Node.js Malware](https://thecyberexpress.com/joyfill-npm-packages-devpopper-nodejs-malware/)
- [North Korean threat actors turn blockchains into malware delivery servers](https://www.csoonline.com/article/4074916/north-korean-threat-actors-turn-blockchains-into-malware-delivery-servers.html)
- [PolinRider: North Korea-Linked Supply Chain Campaign Expands Across Open Source Ecosystems](https://socket.dev/blog/polinrider-north-korea-linked-supply-chain-campaign-expands)

## Full data

Detection queries (Splunk SPL / Microsoft KQL / Sigma) and IOC values require the Threadlinqs MCP server (Purple tier): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-1805
