Skip to content

Back to the catalog

Compromised npm Packages: @joyfill/components and @joyfill/layouts Ship an Obfuscated Remote Access Trojan
stepsecurity.iowriteup

Compromised npm Packages: @joyfill/components and @joyfill/layouts Ship an Obfuscated Remote Access Trojan

StepSecurity dissects malicious beta versions of @joyfill/components and @joyfill/layouts published to npm on July 28, 2026, which inject a five-stage obfuscated payload that executes on import rather than via a postinstall hook, so npm install --ignore-scripts does not stop it. The chain resolves its C2 server by reading a transaction on the Tron blockchain (with Aptos as fallback), decrypts a BNB Smart Chain transaction to find the next stage, and deploys a Socket.IO remote access trojan with worm-like self-propagation that patches the global npm CLI, VS Code/Cursor's @vscode/deviceid module, Discord desktop, and GitHub Desktop, plus a Python credential stealer targeting browser data, wallets, and Git/GitHub tokens. The writeup includes fully deobfuscated source code, IOCs (C2 IPs, Tron addresses, file hashes), and results from detonating the packages under Harden-Runner in a sandbox.

TL;DR Hijacked Joyfill npm packages ship an import-time, blockchain-C2-resolving RAT that worms through npm CLI, VS Code, Cursor, Discord, and GitHub Desktop and steals dev workstation credentials.
Document

What it is

A targeted supply-chain compromise of two hijacked npm packages, @joyfill/components and @joyfill/layouts, published July 28, 2026. Only the "2773" beta prereleases are malicious — confirmed on @joyfill/layouts@0.1.2-2773.beta.0, flagged CRITICAL with a security score of 0 and a REJECTED verdict by StepSecurity's OSS AI scan. The malicious code exists only in the published tarballs; there is no matching source change in the projects.

How it works

The implant is compiled into the package entry bundles and runs on import, not install — package.json has no install hooks, so npm install --ignore-scripts does not stop it. In @joyfill/components the block is appended after ~3.2 MB of legitimate React code; in @joyfill/layouts it is spliced into dist/index.cjs.js and dist/index.es.js:862. The loader plants a campaign marker (global["!"] = "9-0135-3"), then uses a seeded string-shuffle decoder plus a two-step Function-constructor ladder to reassign global.r = require and global.m = module — hiding require, module, Function, and eval from static analysis. Later stages resolve C2 via a public blockchain transaction and open a Socket.IO RAT.

Preconditions / Affected

Any repository, CI/CD pipeline, or developer machine that installed a 2773 prerelease of either package. Note the layouts malicious tarball is a partial build — ~45 KB versus ~322 KB for clean siblings — and only exports PDFRenderer, a useful triage signal.

What to do

If any 2773 prerelease of @joyfill/components or @joyfill/layouts was installed, treat those environments as compromised:

  • Remove the affected versions.
  • Pin to a known-good release published before July 28, 2026.
  • Rotate any credentials present on affected machines.

Detection anchors from the page: the decoder at dist/index.es.js:862 beginning with the 9-0135-3 marker; the anomalous ~45 KB layouts tarball exporting only PDFRenderer.

Caveats

The hardcoded network indicators in the first two stages are only legitimate public blockchain APIs — the real C2 address is resolved at runtime and never appears as a literal, so scanning for a wallet address, socket.io, or the C2 host statically finds nothing. Every stage is decrypted in memory only.

flowchart TD
    A[Project imports package] --> B[In-bundle loader<br/>index.es.js:862]
    B --> C[global.r=require, global.m=module]
    C --> D[Blockchain C2 resolver]
    D --> E[Two staged downloaders]
    E --> F[Socket.IO RAT + Python stealer]
    G[npm install --ignore-scripts] -.does NOT block.-> A
    H[Pin pre-2026-07-28 release] -->|cuts chain| A

Organisations named here

  • A9-0135-3

Technologies named here

  • StepSecurity Harden-Runner
  • @joyfill/components
  • StepSecurity OSS AI scan
  • @joyfill/layouts
  • @vscode/deviceid
  • npm Package Cooldown and Compromised Package checks
  • Discord desktop core module
  • Dev Machine Guard
  • GitHub Desktop
  • npm CLI
  • npm
  • Node.js
  • Python
  • Socket.IO
  • VS Code
  • Cursor
  • TRON
  • Aptos
  • BNB Smart Chain

Techniques named here

  • Supply chain compromise via hijacked npm package
  • Import-time execution
  • Multi-layer string obfuscation
  • Blockchain-based C2 resolution
  • Detached child process persistence
  • Campaign-gated branching
  • Anti-analysis / sandbox evasion
  • Dependency self-healing at runtime
  • Socket.IO remote access trojan
  • Clipboard and credential theft
  • Worm-like self-propagation

Threat actors named here

  • Unnamed threat actor