# Joyfill npm Packages Compromised with Blockchain C2 Loader

> Two @joyfill npm packages (@joyfill/components and @joyfill/layouts) were published on 2026-07-28 with 62 lines of obfuscated JavaScript injected directly into production dist bundles, executing at require()/import time with no install script. The payload uses a three-chain blockchain dead-drop (Tron -> BSC -> Aptos fallback) to relay an encrypted 77KB RAT client matching the PolinRider bot structure, with a detached node -e child-process fallback for persistence. Dead-drop and relay infrastructure is shared with the prior astro.config.mjs supply-chain campaign, tying this incident to the DPRK-attributed PolinRider/JADESNOW cluster.

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

## Description

On 2026-07-28, threat actors published malicious versions of two legitimate npm packages, @joyfill/components and @joyfill/layouts, at 10:54 UTC and 11:03 UTC respectively. Unlike typical npm supply-chain attacks that rely on postinstall scripts, the compromise here was injected directly into the packages' already-built production bundles (dist/index.cjs.js and dist/index.es.js): 62 lines of obfuscated code were spliced between the last legitimate utility function (sortLayoutItemsByColRow) and the FieldLayoutTypes constant, and the layouts bundle itself was swapped from a clean 37,318-line file for a 1,271-line malicious replacement built with vite-plugin-css-injected-by-js. Because the payload runs as a side effect of require()/import, any application pulling these versions executed attacker code automatically with no install-time trigger to detect.

The loader implements a three-stage, cross-chain dead-drop C2. Stage 1 sets a campaign marker in global scope (global["!"] = "9-0135-3") and exposes require/module globally, then queries the Tron blockchain (api.trongrid.io) for the latest transaction on a hardcoded address to obtain routing data, falling back to a Binance Smart Chain lookup (bsc-dataseed.binance.org, with bsc-rpc.publicnode.com as a secondary RPC) and decrypting the result with a hardcoded XOR key. Stage 2 reads the campaign marker to select one of three hardcoded HTTP C2 endpoints (166.88.134.62:443, 198.105.127.210:443, or a fallback at 23.27.202.27:27017 deliberately using the MongoDB port to blend in) while independently resolving a second Tron dead-drop address for command routing. If the primary path fails, the malware spawns a detached `node -e` child process with `windowsHide: true` and `stdio: "ignore"`, allowing the payload to persist after the parent process (and even the host application) exits. Decryption of the relayed blockchain transaction yields a 77KB LZ-compressed RAT client whose structure matches the PolinRider bot family, providing reverse-shell, credential-harvesting, file-exfiltration, and persistent-backdoor capability consistent with public reporting on PolinRider/DEV#POPPER-linked payloads.

Both malicious versions were unpublished by roughly 21:00 UTC the same day (~10 hours of exposure), but remained available on registry mirrors such as registry.npmmirror.com after the upstream takedown. Critically, the primary Tron dead-drop address (TMfKQEd7TJJa5xNZJZ2Lep838vrzrs7mAP) and the Aptos fallback hash pattern are identical to those used in the prior astro.config.mjs supply-chain campaign (a malicious pull request against the Egonex-AI/Understand-Anything repository, disclosed 2026-06-12), which used the same decoder function, shuffle marker, and dead-drop pattern documented by OpenSourceMalware as PolinRider-attributed. That campaign, in turn, sits inside a much larger 2026 wave of DPRK-linked npm/GitHub/Go/Composer/Chrome-extension compromises (ChainVeil, ViteVenom/SuccessKey, the mgc RAT package, and the 108-package PolinRider campaign) all sharing blockchain dead-drop infrastructure across Tron, BSC, and Aptos. The underlying technique -- storing and resolving C2 commands via public blockchain transactions instead of seizable domains/IPs (EtherHiding) -- was first documented by Mandiant in October 2025 as a DPRK tradecraft shift, and is formalized in Malpedia as the js.jadesnow (aka ChainedDown) downloader family, attributed to the WageMole/Famous Chollima/UNC5342/PurpleBravo cluster (a Lazarus Group / Contagious Interview offshoot) that typically delivers INVISIBLEFERRET as a follow-on persistent backdoor.

Because command resolution depends on public, unseizable blockchain RPC infrastructure rather than attacker-controlled domains, blocking the observed HTTP C2 IPs alone does not stop the second-stage compromise -- the Tron/BSC/Aptos dead-drop can still deliver a new payload pointer to any already-infected host. Defenders should treat outbound calls to Tron/BSC/Aptos public RPC endpoints from Node.js application processes, and detached `node -e` child-process spawns with `windowsHide`, as high-fidelity detection opportunities independent of any single IP or domain blocklist.

## MITRE ATT&CK

- T1587.001 Malware
- T1588.001 Malware
- T1583.006 Web Services
- T1608.001 Upload Malware
- T1195.001 Compromise Software Dependencies and Development Tools
- T1195.002 Compromise Software Supply Chain
- T1199 Trusted Relationship
- T1059.007 JavaScript
- T1129 Shared Modules
- T1554 Compromise Host Software Binary
- T1027 Obfuscated Files or Information
- T1027.010 Command Obfuscation
- T1140 Deobfuscate/Decode Files or Information
- T1564.003 Hidden Window
- T1552.001 Credentials In Files
- T1555 Credentials from Password Stores
- T1005 Data from Local System
- T1102.001 Dead Drop Resolver
- T1071.001 Web Protocols
- T1132.002 Non-Standard Encoding
- T1105 Ingress Tool Transfer
- T1571 Non-Standard Port
- T1041 Exfiltration Over C2 Channel

## Sources

- [Joyfill npm Packages Compromised with Blockchain C2 Loader](https://safedep.io/joyfill-npm-blockchain-c2-supply-chain/)
- [Malpedia Library: Joyfill npm Packages Compromised with Blockchain C2 Loader](https://malpedia.caad.fkie.fraunhofer.de/library/1378cdf1-d3a6-4067-91b0-c1658360c5f0/)
- [astro.config.mjs Supply Chain Attack via Blockchain C2](https://safedep.io/astro-config-blockchain-c2-supply-chain/)
- [JADESNOW (Malware Family)](https://malpedia.caad.fkie.fraunhofer.de/details/js.jadesnow)
- [Seven Malicious Vite npm Packages Use Blockchain C2 to Deliver a RAT](https://thehackernews.com/2026/07/seven-malicious-vite-npm-packages-use.html)
- [North Korean Hackers Publish 108 Malicious Packages and Extensions in PolinRider Campaign](https://thehackernews.com/2026/07/north-korean-hackers-publish-108.html)
- [Hijacked npm Package Attempts to Deliver PolinRider-Linked RAT](https://www.sonatype.com/blog/hijacked-npm-package-attempts-to-deliver-polinrider-linked-rat)
- [PolinRider: DPRK Threat Actor That Compromised Hundreds of GitHub Repos Is Unmasked](https://opensourcemalware.com/blog/polinrider-attack)
- [PolinRider Jumps the Fence to Go, Packagist, npm, PyPI](https://opensourcemalware.com/blog/polinrider-jumps-the-fence)
- [Famous Chollima Adversary Profile](https://www.crowdstrike.com/en-us/adversaries/famous-chollima/)
- [WageMole (Threat Actor)](https://malpedia.caad.fkie.fraunhofer.de/actor/wagemole)

## 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-1771
