Skip to content

Back to the catalog

Socket: Two Joyfill npm Beta Releases Compromised to Deliver DEV#POPPER Remote Access Trojan
socket.devwriteup

Socket: Two Joyfill npm Beta Releases Compromised to Deliver DEV#POPPER Remote Access Trojan

Socket's research team found two beta releases of the @joyfill npm packages (@joyfill/layouts and @joyfill/components) contained an import-time JavaScript implant that resolves encrypted payloads through Tron, Aptos, and BNB Smart Chain transactions, ultimately dropping a 77KB Node.js remote-access trojan matching the DEV#POPPER family and sharing loader fingerprints with the PolinRider blockchain-loader campaign. The RAT establishes a Socket.IO C2 channel, executes arbitrary JS/shell commands, exfiltrates files and clipboard data, and persists by patching files inside VS Code, Cursor, Antigravity, Discord Desktop, GitHub Desktop, and the global npm CLI; a parallel detached-process branch fetches an additional bootstrap that can deploy a Python credential/browser-data stealer assessed as an OmniStealer variant. Because the implant fires on module load (not via npm lifecycle scripts), `--ignore-scripts` provides no protection, and the writeup includes full IOCs, blockchain addresses, C2 infrastructure, and MITRE ATT&CK mappings.

TL;DR Two compromised @joyfill npm beta packages load a blockchain-C2 implant that drops a DEV#POPPER-family RAT and, via a parallel branch, an OmniStealer-linked Python credential stealer.
Document

What it is

Concrete version boundaries and response actions for the compromised @joyfill npm beta releases the summary describes. The affected artifacts are exactly @joyfill/layouts@0.1.2-2773.beta.0 (published 2026-07-28T10:54:57Z) and @joyfill/components@4.0.0-rc24-2773-beta.4 (published ~9 minutes later by the same npm identity, Node 18.20.0/npm 10.5.0, both carrying the 2773 prerelease marker). The immediately preceding @joyfill/layouts@0.1.1 and @joyfill/components@4.0.0-rc24 are clean, as are other public @joyfill packages checked. Each package sees ~16,000 weekly downloads, but these overlap (components depends on layouts) and don't reflect beta-tag installs.

Preconditions / Affected

Only @joyfill/layouts@0.1.2-2773.beta.0 executes the implant reliably: it fires when Node loads the CommonJS entrypoint. The components beta contains the same source-level injection in dist/index.js, dist/index.esm.js, and dist/joyfill.min.js, but its Rollup bundle ships a throwing dynamic-require shim that prevents the loader resolving network dependencies in the normal bundled path — Socket assesses this limits execution in that artifact without making it safe. The source maps attribute the implant to src/utils/reactGridLayoutUtils.js, proving it was present at bundle time (maintainer/build compromise), not injected into a final tarball.

What to do

  • Pin to verified clean versions and block restoration of the bad ones:
  • @joyfill/layouts@0.1.1
  • @joyfill/components@4.0.0-rc24
  • Avoid the beta dist-tag for these packages until Joyfill confirms remediation. Purge both bad versions from lockfiles, caches, internal mirrors, build images, and deployment artifacts.
  • Do not rely on npm install --ignore-scripts — no lifecycle hook is used; import triggers it.
  • Treat any host that imported the layouts beta as compromised (interactive remote shell), not merely at risk of theft. Isolate, preserve logs/dependency artifacts, and rotate reachable credentials from a separate uncompromised machine, before other steps.

Detection signals the page calls high-value:

  • Blockchain RPC egress from a CI runner or dev workstation — api[.]trongrid[.]io, bsc-dataseed[.]binance[.]org, fullnode[.]mainnet[.]aptoslabs[.]com, bsc-rpc[.]publicnode[.]com — is "high-fidelity on its own."
  • Detached Node.js processes; the Sec-V header; the four C2 IPs (166[.]88[.]134[.]62, 198[.]105[.]127[.]210, 23[.]27[.]13[.]43, 23[.]27[.]202[.]27:27017).
  • Persistence injections (search for these tags) in @vscode/deviceid (VS Code, Cursor, Antigravity), Discord Desktop's core module, GitHub Desktop resources/app/main.js, and global npm CLI (npm root -g, node_modules/npm/lib/cli.js): /*C250617A*/, /*C250618A*/, /*C250619A*/, /*C250620A*/, /*C260511A*/, /*C260512A*/, /*RS260605*/.
  • If the Python follow-on ran: check staging dirs %USERPROFILE%\.npm or /tmp/.npm, and rotate browser-saved passwords/cookies and wallet/password-manager extension data — not just developer-tool creds.

The RAT avoids execution on hostnames including github-runner, buildbot, buildkitsandbox, and microsoft-standard-WSL2, so a clean sandbox may not reproduce it.

Caveats

The DEV#POPPER and PolinRider labels are family assessments from direct code overlap, not attribution to a group. The /$/boot and Python-stealer captures were pulled once from a disposable VM with no request provenance tying them to a specific infected Joyfill host — they're treated as matching downstream samples, and the OmniStealer identification is medium-likelihood. Source maps confirm bundle-time presence but don't identify the initial access vector (workstation, repo, CI, or publishing credential).

Organisations named here

  • eSentire
  • T1115

Technologies named here

  • @joyfill/layouts
  • Socket.IO
  • axios
  • @joyfill/components
  • pyzipper
  • Telegram
  • @vscode/deviceid
  • PowerShell / pbpaste / xclip / xsel
  • ip-api.com
  • Discord Desktop
  • GitHub Desktop
  • npm CLI
  • npm
  • Node.js
  • Python
  • JavaScript
  • Joyfill

Techniques named here

  • Compromise Software Supply Chain
  • Obfuscated Files or Information
  • Encrypted or Encoded File
  • JavaScript
  • Unix Shell
  • Web Protocols
  • Ingress Tool Transfer
  • Clipboard Data

Threat actors named here

  • PolinRider
  • DEV#POPPER
  • OmniStealer
  • ShoeVista

Also named here

  • T1195.002
  • T1027
  • T1027.013
  • T1059.007
  • T1059.006
  • T1059.004
  • T1071.001
  • T1105