CanisterWorm: npm Publisher Compromise Deploys Backdoor Across 29+ Packages

CanisterWorm: npm Publisher Compromise Deploys Backdoor Across 29+ Packages

Socket’s Threat Research Team uncovered a worm-enabled npm supply chain attack that compromised legitimate publisher namespaces including @emilgroup and @teale.io, replacing package contents with a Python implant that polls an ICP canister for rotatable second-stage payloads. The campaign, dubbed CanisterWorm, uses postinstall hooks, a systemd –user persistent service named pgmon, and a deploy.js republishing worm that leverages npm publishing tokens (often published with –tag latest) to propagate. #CanisterWorm #EmilGroup

Keypoints

  • Socket independently identified a worm-enabled npm supply chain compromise affecting multiple legitimate packages under the @emilgroup scope and @teale.io/eslint-config.
  • The attacker replaced legitimate package functionality with a compact malware kit consisting of a postinstall backdoor (index.js), a propagation tool (deploy.js), and an embedded Python dropper that becomes persistent.
  • Persistence is achieved at user level via systemd –user: the loader writes ~/.local/share/pgmon/service.py and installs/enables ~/.config/systemd/user/pgmon.service with Restart=always (service name pgmon).
  • The implanted Python backdoor polls an Internet Computer Protocol (ICP) canister (tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io) as a dead-drop C2 to retrieve URLs for second-stage binaries, which are downloaded to /tmp/pglog and executed.
  • The deploy.js worm enumerates packages accessible to compromised npm tokens, bumps versions, preserves README content for camouflage, and republishes malicious releases—later variants explicitly publish with –tag latest to increase downstream impact.
  • Likely victims include developers, CI runners, and build systems (especially Linux hosts with systemd –user); Socket continues to investigate scope and attribution and is tracking the campaign as CanisterWorm.

MITRE Techniques

  • [T1195.001 ] Software Dependencies and Development Tools – Compromised legitimate npm publisher space and republished malicious package versions to downstream users using publishing access. (‘used that access to replace legitimate package contents with malicious code, then republish the payload across additional packages reachable by the compromised credentials’)
  • [T1059 ] Command and Scripting Interpreter – Used Node.js execution via npm postinstall hooks to launch the loader and decode/install the implant. (‘the postinstall hook invoked node index.js’)
  • [T1543.003 ] Create or Modify System Process (systemd user) – Established persistence by creating a user-level systemd service and enabling it to restart automatically. (‘writes the Python script to ~/.local/share/pgmon/service.py, creates ~/.config/systemd/user/pgmon.service, reloads the user daemon, enables the service, and starts it immediately’)
  • [T1552.001 ] Credentials in Files – Harvested npm publishing tokens from local files and environment/configuration to enable autonomous propagation. (‘harvested npm tokens from .npmrc, environment variables, and npm configuration’)
  • [T1105 ] Ingress Tool Transfer – Downloaded follow-on binaries from URLs returned by the ICP canister and executed them from /tmp. (‘downloaded a binary to /tmp/pglog, marked it executable, ran it in a detached process’)
  • [T1102 ] Web Service – Used an Internet Computer Protocol (ICP) canister as a rotatable dead-drop C2 channel for remote payload delivery. (‘polled https://tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io/ every 3,000 seconds’)
  • [T1078 ] Valid Accounts – Leveraged valid npm publishing tokens or equivalent CI/CD publishing access to publish malicious package versions under legitimate namespaces. (‘appears to have obtained one or more npm publishing tokens, or equivalent CI/CD publishing access’)

Indicators of Compromise

  • [npm packages/versions ] Compromised package releases – @emilgroup/[email protected], @emilgroup/[email protected], and many other @emilgroup releases (and other affected packages such as @teale.io/[email protected]) — and 40+ other package versions listed.
  • [C2 URL / Domain ] Remote command channel – tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io (polled by the implant to retrieve follow-on payload URLs).
  • [ICP canister ID ] Internet Computer identifier – tdtqy-oyaaa-aaaae-af2dq-cai (used as the dead-drop C2 canister).
  • [File paths ] Second-stage and state files on infected hosts – /tmp/pglog (downloaded binary), /tmp/.pg_state (tracks prior downloads), and ~/.local/share/pgmon/service.py (embedded Python script).
  • [File names / scripts ] Malicious package components – index.js (install-time loader/backdoor), deploy.js (propagation/publishing tool), service.py (Python implant).
  • [Credential sources ] Token harvesting locations – .npmrc, environment variables, and npm configuration (used to collect npm publishing tokens).


Read more: https://socket.dev/blog/canisterworm-npm-publisher-compromise-deploys-backdoor-across-29-packages