CanisterWorm: Compromised npm Publisher Allows Worm Propagation in Supply Chain Attack
March 27th, 2026
High

Our Cyber Threat Intelligence Unit is actively monitoring the CanisterWorm campaign, a software supply chain attack targeting developer machines, CI/CD pipelines, and Kubernetes environments during application installation and build stages. First disclosed on March 20, 2026, by Socket and Aikido Security, with further analysis by Kodem Security on March 23, the campaign involves compromising legitimate npm publisher accounts to distribute malicious versions of popular packages under the @emilgroup and @teale.io namespaces. According to a Wiz investigation cited by Socket, the activity has been linked to TeamPCP, a threat actor previously associated with supply chain compromises targeting Aqua Security's Trivy tool, though attribution remains evolving. The attack executes concealed code during postinstall lifecycle hooks, deploys a persistent Python-based backdoor via systemd --user, and communicates with attacker-controlled infrastructure through an Internet Computer Protocol (ICP) canister acting as a dead-drop command-and-control (C2) channel. A worm component allows autonomous propagation by harvesting npm publishing tokens and republishing malicious releases across additional package namespaces. By March 21, 2026, the number of malicious package artifacts had increased from 29 to 141, affecting more than 64 unique packages.
Technical Details
Vulnerability Type: Supply Chain Attack — Compromised npm Publisher Account
Severity: High
Affected Systems: npm ecosystem (141+ malicious artifacts across 64+ packages); Linux-based developer machines, CI/CD runners, and build containers where systemd --user is available
Threat Actor: TeamPCP (attribution per Wiz investigation; remains evolving)
First Disclosed: March 20, 2026 (Socket and Aikido Security)
Attack Chain:
Initial Access:
Compromise of a legitimate npm publisher account associated with the cover42devs publisher identity (Emil Group).
The exact compromise method has not been confirmed.
No npm vulnerability was exploited; the attack relied entirely on abusing a trusted publishing credential.
Weaponization:
Malicious versions of legitimate packages were published under the @emilgroup and @teale.io namespaces.
The malicious releases preserved original README content as camouflage while replacing all legitimate SDK functionality with a compact malware kit.
In an initial staging phase, packages included a postinstall hook that read a base64-encoded payload from an environment variable (PAYLOAD) or package configuration and exited silently if none was present, indicating a dry-run or staging phase prior to full weaponization.
In the weaponized phase, the base64 payload was hardcoded directly into the package.
When decoded and executed, it dropped a Python-based backdoor implant onto the host.
Worm Propagation (deploy.js):
The malicious packages included a tool named deploy.js, which is not triggered by npm install, but used by the attacker to republish malicious releases at scale. The script:
Accepted one or more npm publishing tokens from the environment.
Resolved the associated publisher usernames via npm's /-/whoami endpoint.
Enumerated all packages accessible to each token.
Bumped the patch version, copied the original README content, and republished the malicious code under the legitimate package name.
In later variants, used --tag latest during publish to maximize the likelihood that routine npm install activity resolved to the malicious version by default.
The @teale.io/eslint-config namespace was observed to skip the staging phase entirely and appear directly in a weaponized state.
This is consistent with cross-namespace propagation via the worm or the compromise of multiple publisher tokens.
Execution:
Malicious code was triggered via the postinstall lifecycle hook, executing node index.js immediately after package installation, before any application code ran.
The loader decoded the embedded Python dropper, wrote it to disk, and established persistence.
Persistence (systemd –user):
The Python implant was written to: ~/.local/share/pgmon/service.py
A systemd user service unit was created at: ~/.config/systemd/user/pgmon.service
The service was configured with Restart=always, allowing the implant to recover from crashes without requiring root privileges.
The service name pgmon was hardcoded during the weaponization phase, replacing an earlier dynamic naming scheme based on the package name.
Command and Control (ICP Canister Dead Drop):
Once persistent, the Python implant entered an endless polling loop, querying an Internet Computer Protocol (ICP) canister every 3,000 seconds:
C2 URL: https[:]//tdtqy-oyaaa-aaaae-af2dq-cai.raw[.]icp0.io/
When the canister returned a URL, the implant downloaded a second-stage binary to /tmp/pglog, marked it executable, and ran it in a detached process.
The downloaded URL was written to /tmp/.pg_state to prevent re-downloading the same payload.
This dead-drop design allows the attacker to rotate second-stage payloads without modifying or republishing the already-installed implant.
A dormant/disarm state was implemented: URLs containing youtube[.]com were ignored by the implant.
Post-Exploitation:
Credential harvesting: API keys, npm tokens, cloud credentials, and deployment secrets from environment variables, .npmrc, .docker/config.json, and cloud metadata endpoints.
Exfiltration of harvested secrets over HTTP/DNS channels.
Enumeration of host and container environment (network configuration, IAM roles, mounted volumes, Kubernetes service account tokens).
Depending on the second-stage payload delivered via the ICP canister, later-stage behavior has included destructive wiper activity targeting Kubernetes clusters and persistent backdoor deployment on other systems, as reported by Aikido Security on March 22, 2026.

Impact
Credential and Secrets Compromise: Theft of API keys, deployment tokens, cloud access credentials, and npm publisher tokens from build and runtime environments can allow unauthorized access to production systems.
Supply Chain Propagation: The worm component allows the attack to spread autonomously across additional package namespaces accessible to any harvested npm token, compounding downstream exposure beyond the initial set of affected packages.
CI/CD Pipeline Compromise: Malicious code executing during the build phase can corrupt build artifacts, tamper with container images, or inject backdoors into deployment configurations before any runtime scanning occurs.
Destructive Impact on Kubernetes Environments: Later-stage payloads have exhibited wiper behavior targeting Kubernetes clusters, according to Aikido Security.
Organizations running Kubernetes workloads that installed affected packages should treat this as a potential destructive incident, not merely a credential exposure.
Lateral Movement: Stolen credentials and Kubernetes service account tokens enable pivoting into cloud workloads, container clusters, and adjacent infrastructure.
Operational Disruption: Compromise of CI/CD pipelines may result in delayed deployments, corrupted releases, or service downtime across downstream environments.
Business and Regulatory Exposure: Credential breaches and supply chain compromise carry significant incident response costs, regulatory obligations, and reputational risk.
Detection Method
Security teams should monitor endpoint, network, and build environment telemetry for the following behavioral indicators:
Endpoint and Process Monitoring:
Monitor process creation events (Sysmon Event ID 1) for npm or node spawning shell processes (sh, bash, curl, wget) during package installation.
Flag execution of node index.js from within node_modules directories immediately following npm install activity.
Detect creation or modification of files at the following paths, which are consistent with confirmed implant behavior:
~/.local/share/pgmon/service.py (Python backdoor implant)
~/.config/systemd/user/pgmon.service (persistence service unit)
/tmp/pglog (second-stage binary download path)
/tmp/.pg_state (C2 state tracking file)
Detect systemd --user service registrations under the name pgmon, particularly on Linux-based CI runners or developer machines.
Identify short-lived processes performing environment reconnaissance or credential access during or immediately after package installation.
Network and C2 Monitoring:
Monitor outbound HTTPS traffic to the confirmed ICP canister C2 endpoint: tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io
Flag recurrent outbound connections at approximately 3,000-second intervals to Internet Computer (icp0.io) infrastructure from build systems or developer machines.
Detect outbound HTTP requests from /tmp/pglog or other temporary binaries running in detached processes.
Monitor DNS queries and HTTP/HTTPS traffic for encoded data consistent with credential exfiltration.
CI/CD and Build Pipeline Monitoring:
Review pipeline logs (GitHub Actions, Jenkins, GitLab CI) for unexpected postinstall lifecycle script execution during npm install.
Identify pipeline activity accessing sensitive files or environment variables, such as .npmrc, cloud provider credential files, or tokens during build stages.
Alert on unexpected npm publish commands issued from CI runners, particularly those using --tag latest, which may indicate worm propagation activity.
Detect access to npm's /-/whoami endpoint from CI environments, which is consistent with the deploy.js token enumeration routine.
Dependency Integrity Monitoring:
Track unauthorized or unexpected changes to package.json, package-lock.json, or node_modules/ directories.
Alert on patch-version bumps in @emilgroup or @teale.io packages that do not correspond to expected release activity.
Monitor for environment-aware script execution: scripts checking for CI, KUBERNETES_SERVICE_HOST, or similar variables during install phases.
Indicators of Compromise
Note: The following 141 malicious package artifacts reflect the current scope tracked by Socket as of March 21, 2026. The presence of malicious packages across unrelated publisher namespaces confirms successful worm-driven token harvesting and cross-account propagation. Organizations should monitor Socket's live tracking page for any further updates: https://socket.dev/supply-chain-attacks/canisterworm
Affected npm Packages and Versions
Indicator Type | Indicator |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @emilgroup/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @teale.io/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @opengov/[email protected] |
npm Package | @virtahealth/[email protected] |
npm Package | @pypestream/[email protected] |
npm Package | @leafnoise/[email protected] |
npm Package | @airtm/[email protected] |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package | |
npm Package |
Network / C2 and Host Indicators
Indicator Type | Indicator |
C2 URL | https[:]//tdtqy-oyaaa-aaaae-af2dq-cai.raw[.]icp0[.]io/ |
ICP Canister ID | tdtqy-oyaaa-aaaae-af2dq-cai |
File Path | ~/.local/share/pgmon/service.py (Python backdoor implant) |
File Path | ~/.config/systemd/user/pgmon.service (systemd persistence unit) |
File Path | /tmp/pglog (second-stage binary download path) |
File Path | /tmp/.pg_state (C2 state tracking file) |
Service Name | pgmon (systemd --user persistence service) |
Malicious File Hashes (SHA256) — index.js
Indicator Type | Indicator |
SHA256 | e9b1e069efc778c1e77fb3f5fcc3bd3580bbc810604cbf4347897ddb4b8c163b (Wave 1: : staging, empty payload) |
SHA256 | 61ff00a81b19624adaad425b9129ba2f312f4ab76fb5ddc2c628a5037d31a4ba (Wave 2: armed ICP backdoor, manual deploy) |
SHA256 | 0c0d206d5e68c0cf64d57ffa8bc5b1dad54f2dda52f24e96e02e237498cb9c3a (Wave 3: self-propagating, test payload) |
SHA256 | c37c0ae9641d2e5329fcdee847a756bf1140fdb7f0b7c78a40fdc39055e7d926 (Wave 4: self-propagating + armed ICP backdoor) |
Malicious File Hashes (SHA256) — deploy.js
Indicator Type | Indicator |
SHA256 | f398f06eefcd3558c38820a397e3193856e4e6e7c67f81ecc8e533275284b152 (Wave 1: verbose, no --tag latest) |
SHA256 | 7df6cef7ab9aae2ea08f2f872f6456b5d51d896ddda907a238cd6668ccdc4bb7 (Wave 2: added --tag latest) |
SHA256 | 5e2ba7c4c53fa6e0cef58011acdd50682cf83fb7b989712d2fcf1b5173bad956 (Wave 3+: minified, silent) |

Recommendations
Organizations should take the following actions immediately:
Assess Exposure:
Audit all environments, including developer machines, CI runners, and build containers, for affected versions of the @emilgroup and @teale.io packages.
Consult Socket's live CanisterWorm tracking page for the current full scope.
Isolate and Contain:
Isolate systems that installed affected package versions and suspend related CI/CD jobs.
Remove malicious dependencies and reinstall only verified, trusted versions pinned in lockfiles.
Treat Kubernetes Environments as High Priority:
Organizations running Kubernetes workloads should treat any confirmed exposure as a potential destructive incident.
Inspect cluster state for signs of wiper payload execution and conduct a full forensic review.
Rotate All Exposed Credentials:
Revoke and rotate all potentially exposed credentials, including npm publishing tokens, API keys, cloud access credentials, CI/CD service tokens, and Kubernetes service account tokens.
Audit npm token access and revoke any long-lived tokens that may have been harvested by the worm.
Enforce MFA and Least Privilege:
Enable multi-factor authentication on all npm publisher accounts, CI/CD platforms, and privileged access points.
Restrict package publishing permissions to the minimum necessary and replace long-lived tokens with short-lived credentials where operationally feasible.
Search for Persistence Indicators:
Scan Linux-based systems for the presence of pgmon systemd user services, the Python implant at ~/.local/share/pgmon/service.py, and the C2 state files /tmp/pglog and /tmp/.pg_state.
Block C2 Infrastructure:
Implement network-level blocks on outbound connections to tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io and configure alerts for any attempted connections to this endpoint.
Strengthen Build Pipeline Security:
Disable or restrict execution of unnecessary lifecycle scripts (preinstall, postinstall) during package installation in CI environments.
Validate package integrity using checksums, lockfiles, and trusted registries.
Monitor for unexpected npm publish activity originating from CI runners.
Enhance Monitoring:
Configure alerts for unexpected changes to dependency files (package.json, package-lock.json, node_modules/), unexpected systemd --user service registrations on Linux build systems, and outbound connections from temporary file paths such as /tmp/pglog.
Conclusion
The CanisterWorm campaign is a sophisticated, actively evolving supply chain threat that exploits trust in legitimate npm publisher accounts. By embedding a Python-based backdoor into install-time lifecycle hooks and deploying an autonomous worm propagation mechanism, the attacker scaled a single publisher compromise into 141 malicious package artifacts within 24 hours. Using an ICP canister as a rotatable dead-drop C2 channel further allowed the attacker to deliver changing second-stage payloads without republishing the implant. We urge organizations to audit their development and build environments for affected packages, rotate all potentially exposed credentials, and prioritize investigation of any Kubernetes environments that may have been exposed. Proactive monitoring of build-time activity and strict enforcement of publishing token hygiene are essential controls to reduce exposure to this class of supply chain attack.