Two Joyfill npm Beta Releases Compromised to Deliver DEV#POPPER Remote Access Trojan
Two beta releases of the @joyfill npm packages were compromised at build time with a multi-layered obfuscated implant that triggers on module import rather than via npm lifecycle hooks. The implant resolves payloads through Tron/Aptos/BNB Smart Chain transactions to fetch a 77KB Node.js RAT (Socket.IO C2, ss_* command set) and, via a detached process, a separate bootstrap that can deploy a Python infostealer targeting credentials, browser data, and wallet extensions. Code and operational indicators tie the loader to the PolinRider family and the final payload to the DEV#POPPER campaign, with the compromise attributed to maintainer-side access rather than a fake-repo lure.
- domainapi[.]trongrid[.]ioTron RPC endpoint queried to obtain blockchain-based payload pointers
- domainbsc-dataseed[.]binance[.]orgBNB Smart Chain RPC endpoint queried via eth_getTransactionByHash to retrieve encrypted payload
- domainbsc-rpc[.]publicnode[.]comAlternate BNB Smart Chain RPC endpoint used for payload transaction retrieval
- domainfullnode[.]mainnet[.]aptoslabs[.]comAptos RPC endpoint used as fallback for payload pointer resolution
- domainip-api[.]comThird-party IP geolocation service queried by the RAT to collect victim public IP details
- ip166[.]88[.]134[.]62Primary Socket.IO C2 and file-upload host used by the recovered Node.js RAT
- ip198[.]105[.]127[.]210Alternate C2/upload profile embedded in both the RAT loader and detached bootstrap
- ip23[.]27[.]202[.]27Alternate C2 profile (also using port 27017) embedded in loader and bootstrap
- sha2561352ad22c99983d91e600348b7cbf58235131b1ee34cea9f09623206d5b7dea7Components dist/index.js bundle containing implant
- sha25626351aed0397158d3a3b8cc8fd3047d4c015d264c9895f10f20f1521b974ed18Final 77KB Node.js remote access trojan (clientCode) recovered from the blockchain-resolved payload chain
- sha25626e679eaf1e9baeb7c55eb48db482301171d4d26e1728544b23734a90dc70e1bPreserved /$/boot response capture matching the Joyfill campaign key
- sha2562cfede38fb121a71a2f3607474aa8cd588a99f51b37e5e6f0d8cb789fa275032Second preserved /$/boot response capture matching the Joyfill campaign key
- sha25636ff00b45e67baa7e3674b0c80f48e88737264c61e5c6b3b091200972de8157cPython infostealer payload (assessed as likely OmniStealer) delivered via the detached bootstrap chain
- sha2565f6a92006ca2ea4b464d66fb41af777edce7296939a7c6ee491e2b3cbfe09848Layouts ESM bundle containing implant
- sha25667c6ef602cc850f10d935fee53fa40440df841adf081563bf4fc2631a71249ceComponents dist/index.esm.js bundle containing implant
- sha25678f0de8682e0e894a5784eb7e95db4da6088f528918ca3107dd1e76f80a561d8Detached second-stage JavaScript bootstrap fetched via /$/boot
- sha2568e8b90dedd456ded0c5748119836e1ca1066112bc569c1b41ca70eb931d1d4dcLayouts CommonJS bundle containing implant
- sha256adc4af90540d33cd1e98f44b51482ae9250fbeb97d6f8d7841c81b618cb2c6e6Compromised @joyfill/layouts npm package archive
- sha256ae7565109fd01b88d82acf7f73ab20709cbc2c9f26fdea13e429ccc87a55d4fbDecoded detached bootstrap payload
- sha256bcc93dc55bc7daedf4ca57254f0e7a7f1c40e09851eab98fe10cde801982db17Compromised @joyfill/components npm package archive
- sha256c5742ea1875ecd2360022624149994909cd0546e221e4203dffd01f48de45469Components dist/joyfill.min.js bundle containing implant
- sha256cb46f12d70824ea24ed1f8bcf45bf3f86680e02a9089aafc03b27f691be57be3In-process first-stage JavaScript loader retrieved via blockchain pointer
- sha256f452f9cfa539f4a1fe25187a99a484391290d5dbaa422ba455edf6b04f81b7d1Decoded tier-two resolver script used to fetch final RAT payload
Detection / HunterAnthropic
What Happened
Attackers slipped malicious code into two test ('beta') versions of a popular set of coding tools called Joyfill, which developers use to add forms and PDFs to their apps. Anyone who installed and used those specific test versions - including automated build systems - unknowingly ran hidden code that contacts the attackers using cryptocurrency network transactions as a disguised messaging system, then downloads a full remote-control tool and, in some cases, a data-stealing program that can grab saved passwords, browser data, and cryptocurrency wallet information. This matters because it affects software developers and any automated systems (like testing or deployment pipelines) that used the compromised versions, giving attackers the ability to remotely control affected machines and steal sensitive data. Anyone who installed these two specific beta versions should treat the affected computer as compromised, disconnect it, and follow their organization's incident response process rather than simply removing the package.
Key Takeaways
- Two beta versions of @joyfill/layouts and @joyfill/components were compromised at the source/bundle level with an import-time JavaScript implant, meaning npm install --ignore-scripts does not prevent execution.
- The implant resolves its payload through a multi-chain blockchain lookup (Tron, Aptos, BNB Smart Chain) rather than a static C2 domain, making conventional domain blocking insufficient.
- The final recovered payload is a 77KB Node.js remote access trojan using Socket.IO for C2, capable of arbitrary command execution, file upload, clipboard theft, and persistence inside developer tools (VS Code, Cursor, Discord, GitHub Desktop, npm CLI).
- A parallel detached process branch retrieves an additional bootstrap from a hardcoded IP that can deploy a Python credential/browser/wallet stealer assessed with medium confidence as OmniStealer.
- The compromise shows exact code overlap with the PolinRider loader family and operational overlap with the DEV#POPPER campaign, and appears to stem from maintainer account/build compromise rather than a fake repository clone.
Affected Systems
- Node.js development environments, CI runners, test tooling, SSR and build pipelines that import @joyfill/[email protected] or @joyfill/[email protected]
- Windows, macOS, and Linux developer workstations
- VS Code, Cursor, Antigravity, Discord Desktop, GitHub Desktop, and global npm CLI installations (persistence targets)
Vulnerabilities (CVEs)
None identified.
Attack Chain
- Initial Access: Attacker compromises Joyfill maintainer/build pipeline and publishes two npm beta packages with injected implant appended to legitimate code.
- Execution: Implant runs at module import time (not install time), exposing Node.js primitives via globals and decoding an embedded resolver.
- C2 Resolution: Resolver queries Tron/Aptos for a pointer, retrieves a BNB Smart Chain transaction, and XOR-decrypts embedded JavaScript from the transaction input.
- Secondary Delivery: A detached, unref'd Node.js child process independently requests a boot payload from a hardcoded IP using a Sec-V marker header.
- Payload Deployment: In-process branch loads a 77KB Node.js RAT using Socket.IO C2 with ss_* commands; detached branch can deliver a Python infostealer (assessed as OmniStealer) via additional provisioning steps.
- Persistence & Impact: RAT injects self-reloading code into developer tool files (VS Code, Discord, GitHub Desktop, npm CLI) and exfiltrates host data, clipboard contents, credentials, browser data, and wallet/extension storage.
Detection Availability
- YARA Rules: No
- Sigma Rules: No
- Snort/Suricata Rules: No
- KQL Queries: No
- Splunk SPL Queries: No
- EQL Queries: No
- Other Detection Logic: No
The article provides file hashes, network indicators, blockchain addresses, and behavioral descriptions but does not include YARA, Sigma, Snort/Suricata, KQL, SPL, or EQL detection content.
Detection Engineering Assessment
| Dimension | Rating | Rationale |
|---|---|---|
| EDR Visibility | Medium | EDR with Node.js process/child-process monitoring could observe the detached node -e spawn pattern, file modifications to developer-tool files, and clipboard access commands, but the initial implant execution occurs inside a legitimate Node.js process at import time, which may blend with normal build/test activity. |
| Network Visibility | Medium | Outbound requests to public blockchain RPC endpoints (Tron, Aptos, BSC) from build agents or developer workstations are unusual and would stand out in network logs if monitored, but the payload content itself is encrypted within legitimate-looking blockchain transaction data, limiting content-based network detection. |
| Detection Difficulty | Hard | The implant avoids install-time hooks, uses layered obfuscation, resolves C2 via legitimate blockchain RPC traffic rather than static domains, and spawns a detached process that outlives the parent build/test job, making both static and behavioral detection challenging without specific signature or hunting knowledge. |
Required Log Sources
- Process creation logs (Sysmon Event ID 1) for detached node processes
- Network connection logs for outbound HTTPS/HTTP to blockchain RPC domains and hardcoded IPs
- File integrity monitoring for developer tool files (VS Code, Discord, GitHub Desktop, npm CLI)
- npm/package registry proxy logs for install of flagged package versions
- CI/CD pipeline execution and dependency resolution logs
Hunting Hypotheses
| Hypothesis | Telemetry | ATT&CK Stage | FP Risk |
|---|---|---|---|
| Look for developer workstations or CI runners making outbound RPC calls to blockchain infrastructure (Tron/Aptos/BSC RPC endpoints) that have no legitimate business reason to interact with these services. | Network/proxy logs, DNS query logs | Command and Control | Low to Medium - legitimate blockchain development or testing environments could generate similar traffic |
| Identify detached/orphaned Node.js child processes with hidden windows or ignored stdio spawned from build tools, test runners, or package managers shortly after a dependency install or import. | Process creation events, parent-child process relationships | Execution | Medium - some legitimate tooling spawns detached background processes |
| Search for unexpected modifications to files associated with VS Code's @vscode/deviceid module, Discord Desktop's core module, GitHub Desktop's main.js, or the global npm CLI file, particularly additions of small reloading code blocks. | File integrity monitoring, EDR file modification events | Persistence | Low - these files are rarely modified outside of application updates |
| Hunt for processes reading clipboard data via PowerShell, pbpaste, xclip, or xsel from non-interactive or automated contexts such as CI agents or headless build servers. | Process command-line logging, EDR API call monitoring | Collection | Medium - some legitimate automation tools interact with clipboard utilities |
| Look for npm install/build activity involving the flagged Joyfill beta package versions across lockfiles, caches, and CI artifact repositories. | Package manager logs, artifact repository/proxy logs, lockfile diffs | Initial Access | Low - specific version strings make this a high-fidelity search |
Control Gaps
- npm install --ignore-scripts does not prevent execution since the implant triggers on module import, not install lifecycle hooks
- Traditional domain/IP blocklists alone will not stop the initial payload retrieval since it relies on mutable public blockchain transaction data
- Static signature-based antivirus may be insufficient given heavy multi-layer obfuscation and dynamic Function construction
Key Behavioral Indicators
- Detached Node.js child process spawned with stdio ignored and window hidden shortly after package import
- Outbound requests from build/CI/developer systems to Tron, Aptos, or BNB Smart Chain RPC endpoints
- Presence of a distinctive custom HTTP request header used as a version/campaign marker
- Self-reloading code blocks appended to developer tool application files
- Process avoidance behavior based on hostname patterns associated with CI/sandbox environments
False Positive Assessment
Medium - some indicators (blockchain RPC traffic, detached Node.js processes, clipboard access) can occur in legitimate development or blockchain-related workflows, requiring correlation with the specific package versions, hashes, or header markers described to achieve high-confidence detection.
Recommendations
Immediate Mitigation
- Verify against your organization's incident response runbook and team escalation paths before acting; consider isolating any host that imported the affected @joyfill/layouts or @joyfill/components beta versions, treating it as potentially compromised with an active interactive remote shell.
- Consider removing both affected package versions from lockfiles, caches, internal mirrors, build images, and deployment artifacts, and pin to independently verified prior versions.
- If applicable, rotate credentials reachable from any affected Node.js process from a separate, uncompromised machine before further remediation steps.
- Consider preserving logs and dependency artifacts from affected hosts for forensic review prior to remediation.
Infrastructure Hardening
- Evaluate blocking or alerting on outbound traffic from CI runners and build agents to public blockchain RPC endpoints unless explicitly required for business purposes.
- Consider implementing dependency policy tooling or a registry proxy to block known-malicious package versions from being installed or resolved automatically.
- Where supported by your tooling, avoid using the beta dist-tag for third-party packages in production or CI pipelines until vendor remediation is confirmed.
- If feasible, implement file integrity monitoring on developer tool application files (VS Code, Discord, GitHub Desktop, global npm CLI) to detect unauthorized modifications.
User Protection
- Consider auditing developer workstations for unexpected staging directories (e.g., under user profile .npm paths) that may indicate a follow-on Python stealer executed.
- If the Python follow-on may have run, consider rotating browser-saved passwords, cookies, and any wallet or password-manager browser-extension data on affected hosts.
- Where supported by EDR, monitor for and alert on detached Node.js processes with hidden windows spawned from developer or CI contexts.
Security Awareness
- Consider briefing development teams on the risk of beta/pre-release package tags and the limits of --ignore-scripts protections for import-time threats.
- Consider incorporating supply-chain compromise scenarios (maintainer account takeover, build-time code injection) into existing security awareness and tabletop exercises.
- Encourage developers to verify package integrity and review dependency changes, especially for beta or release-candidate versions, before broad adoption.
MITRE ATT&CK Mapping
Initial Access
Execution
Collection
Command and Control
Additional IOCs
- Ips:
166[.]88[.]134[.]62- Socket.IO C2 endpoint on ports 443/80 for the RAT
- Domains:
api[.]trongrid[.]io- Tron RPC endpoint queried to obtain blockchain-based payload pointersfullnode[.]mainnet[.]aptoslabs[.]com- Aptos RPC endpoint used as fallback for payload pointer resolutionbsc-dataseed[.]binance[.]org- BNB Smart Chain RPC endpoint queried via eth_getTransactionByHash to retrieve encrypted payloadbsc-rpc[.]publicnode[.]com- Alternate BNB Smart Chain RPC endpoint used for payload transaction retrievalip-api[.]com- Third-party IP geolocation service queried by the RAT to collect victim public IP details
- Urls:
23.27.13.43/$/boot- Boot payload retrieval endpoint requiring Sec-V marker header
- File Hashes:
adc4af90540d33cd1e98f44b51482ae9250fbeb97d6f8d7841c81b618cb2c6e6(SHA256) - Compromised @joyfill/layouts npm package archive8e8b90dedd456ded0c5748119836e1ca1066112bc569c1b41ca70eb931d1d4dc(SHA256) - Layouts CommonJS bundle containing implant5f6a92006ca2ea4b464d66fb41af777edce7296939a7c6ee491e2b3cbfe09848(SHA256) - Layouts ESM bundle containing implantbcc93dc55bc7daedf4ca57254f0e7a7f1c40e09851eab98fe10cde801982db17(SHA256) - Compromised @joyfill/components npm package archive1352ad22c99983d91e600348b7cbf58235131b1ee34cea9f09623206d5b7dea7(SHA256) - Components dist/index.js bundle containing implant67c6ef602cc850f10d935fee53fa40440df841adf081563bf4fc2631a71249ce(SHA256) - Components dist/index.esm.js bundle containing implantc5742ea1875ecd2360022624149994909cd0546e221e4203dffd01f48de45469(SHA256) - Components dist/joyfill.min.js bundle containing implantcb46f12d70824ea24ed1f8bcf45bf3f86680e02a9089aafc03b27f691be57be3(SHA256) - In-process first-stage JavaScript loader retrieved via blockchain pointerf452f9cfa539f4a1fe25187a99a484391290d5dbaa422ba455edf6b04f81b7d1(SHA256) - Decoded tier-two resolver script used to fetch final RAT payload78f0de8682e0e894a5784eb7e95db4da6088f528918ca3107dd1e76f80a561d8(SHA256) - Detached second-stage JavaScript bootstrap fetched via /$/bootae7565109fd01b88d82acf7f73ab20709cbc2c9f26fdea13e429ccc87a55d4fb(SHA256) - Decoded detached bootstrap payload26e679eaf1e9baeb7c55eb48db482301171d4d26e1728544b23734a90dc70e1b(SHA256) - Preserved /$/boot response capture matching the Joyfill campaign key2cfede38fb121a71a2f3607474aa8cd588a99f51b37e5e6f0d8cb789fa275032(SHA256) - Second preserved /$/boot response capture matching the Joyfill campaign key
- File Paths:
%USERPROFILE%\.npm- Staging directory used by the Python infostealer to collect data before archiving/tmp/.npm- Linux/macOS staging directory used by the Python infostealer to collect data before archivingnode_modules/npm/lib/cli.js- Global npm CLI file targeted for self-reloading persistence injectionresources/app/main.js- GitHub Desktop core module file targeted for persistence injection
- Command Lines:
- Purpose: Launch a detached, hidden Node.js child process to execute a downloaded second-stage payload independent of the parent process lifecycle | Tools:
node.exe,child_process| Stage: Execution - Purpose: Read clipboard contents cross-platform for data collection | Tools:
PowerShell,pbpaste,xclip,xsel| Stage: Collection
- Purpose: Launch a detached, hidden Node.js child process to execute a downloaded second-stage payload independent of the parent process lifecycle | Tools:
- Other:
TA48dct6rFW8BXsiLAtjFaVFoSuryMjD3v- Tron address used by tier-two resolver to obtain pointer to final 77KB RAT payload0xbe037400670fbf1c32364f762975908dc43eeb38759263e7dfcdabc76380811e- Aptos fallback account address for in-process resolver0x3f0e5781d0855fb460661ac63257376db1941b2bb522499e4757ecb3ebd5dce3- Aptos fallback account address for detached-process resolver0x533b2dbcaeff19cd1f799234a27b578d713d8fcaa341b7501e4526106483e0b1- Aptos fallback account address for tier-two resolver0x9bc1355344b54dedf3e44296916ed15653844509- Additional blockchain address fingerprint associated with the loader