Executive Summary
Malicious versions of pgserve (1.1.11 through 1.1.14) were published to npm in a supply chain compromise. The package runs a credential harvesting and self-propagation payload at install time via a postinstall hook.
Ossprey Security has detected a new supply chain compromise, bearing strong similarities to attacks from earlier this year. Beyond credential theft, the package is also a worm. It discovers npm tokens on the infected system, enumerates all packages the token owner can publish, and injects itself into each one as a new version. It also crosses ecosystems, targeting PyPI using .pth file injection.
A technique that reminds of earlier attacks by TeamPCP sees the usage of the Internet Computer Protocol (ICP) as a secondary C2 Channel.
Key Judgments
What is notable?
ICP blockchain C2 was previously observed in the Trivy supply chain attack. Its reappearance here suggests it is becoming an established evasion technique in supply chain malware.
Who is at risk?
Any developer or CI pipeline that ran npm install pgserve from April 21 onwards, or installed any PyPI package that may have been infected via cross-ecosystem propagation. Any npm token on an infected machine may have been used to propagate the worm further.
Was this preventable?
Yes. Behavioural analysis flagging postinstall execution, credential file reads, and outbound network calls at install time would catch this. Additionally, the PyPI packages published by this payload have no corresponding GitHub releases on the source project - a signal that can be used to flag unauthorised releases.
What is the broader risk?
The npm install is just the entry point. The real damage is credential theft - cloud keys, CI tokens, SSH keys, database passwords, and crypto wallets swept in seconds. For most organisations, a compromised developer machine or CI pipeline has access to production infrastructure. These supply chain worms do not need to breach your perimeter; they ride in through a dependency and harvest everything reachable from that process.
Ossprey Detection
Ossprey's detection flagged the package on behavioural signals within seconds of each version appearing on the registry: postinstall execution, credential file reads across cloud provider configs, SSH keys, and crypto wallet paths, and outbound HTTP to infrastructure with no prior association with the pgserve package.
Technique Overlap
The pgserve payload shares notable techniques with malware observed in the ongoing wave of npm and PyPI supply chain compromises throughout early 2026. The payload code contains a comment explicitly naming a prior technique:
[PyPI] Technique: .pth file injection (TeamPCP/LiteLLM method)
This reference appears likely to be a callback or shoutout to previous attacks by TeamPCP, which we saw in March 2026 in the LiteLLM compromise. We are not attributing this compromise to any specific threat group at this time, and no group has claimed responsibility at the time of publication.
The shared characteristics worth noting:
.pth file injection for PyPI cross-ecosystem propagation
Credential harvesting targeting the same cloud provider and CI/CD patterns
postinstall hook execution vector
Dual-channel exfiltration strategy
Technical Breakdown
Execution Vector
The malicious payload is delivered via a postinstall script hook in package.json. It runs automatically when any developer or CI system installs the package.
Credential Harvesting
The harvest() function is one of the most comprehensive credential sweeps observed in npm supply chain malware. It operates in two phases:
Phase 1 - Environment variable sweep:
Over 50 regex patterns target credentials in the current process environment, including cloud providers, CI platforms, databases, AI APIs, and infrastructure tooling.
Phase 2 - Filesystem sweep:
Over 35 specific file paths are read, covering every major credential store on a developer machine, including:
~/.aws/credentials~/.config/gcloud/application_default_credentials.json~/.azure/accessTokens.json~/.kube/config~/.docker/config.json~/.terraform.d/credentials.tfrc.json~/.pulumi/credentials.json~/.git-credentials~/.config/gh/hosts.yml~/.npmrc~/.pypirc~/.gem/credentials~/.cargo/credentials.toml~/.pgpass~/.my.cnf~/.bash_history~/.zsh_history.env files in the current working directory and parent directory
Crypto Wallet Theft
The payload specifically targets seven cryptocurrency wallet applications by reading their local storage and IndexedDB files, including:
MetaMask
Phantom
Exodus
Atomic Wallet
Ethereum
Bitcoin/Electrum
Solana
Chrome Password Decryption
On Linux, the payload actively decrypts stored Chrome passwords at runtime using a hardcoded static key.
Process Memory Scanning
On Linux, the payload scans /proc//environ for the first 50 running processes, extracting environment variables from other running processes if they contain credential-related strings.
Encryption
Before exfiltration, the full payload is encrypted using a hybrid scheme:
A random 32-byte AES-256-CBC session key and 16-byte IV are generated
The harvested data is encrypted with AES-256-CBC
The session key is encrypted with RSA-4096-OAEP-SHA256 using a public key bundled with the package
If no public key is present, data is sent in plaintext
Dual-Channel C2 Exfiltration
Exfiltration attempts two channels simultaneously via `Promise.allSettled`:
hxxps://cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0[.]io/drop
The canister fires on every execution regardless of configuration. It uses ICP, a blockchain platform. The canister ID `cjn37-uyaaa-aaaac-qgnva-cai` acts as a decentralised, censorship-resistant data drop that cannot be taken down by domain seizure or IP blocking. Large payloads are chunked to 800KB with reassembly metadata (`_c`, `_id`, `_p`, `_t`, `_d` fields).
The canister receives a slim payload, with large browser and wallet data is stripped before sending:
The secondary exfil URL.
hxxps://telemetry.api-monitor[.]com/v1/telemetry
The webhook behaviour changed across versions. In 1.1.11 and 1.1.12, the webhook key defaults to an empty string and the webhook does not fire. In 1.1.13 and 1.1.14, the key is hardcoded:
In these versions the webhook is active by default. Requests are HMAC-SHA256 signed with this key and carry the full payload. Including Chrome passwords, wallet files, and browser data that is excluded from the canister.
npm Worm - Self-Propagation
The worm is disabled by default. Propagation only activates if two environment variables are explicitly configured by the operator:
With `DIST_SYNC=false` and `DIST_SCOPE` set to a number or `unlimited`, the worm proceeds to inject itself into every package the stolen token can publish:
1. Token Discovery - checks `NPM_TOKEN` env var, then `~/.npmrc` and project `.npmrc`. Resolves env var references (e.g. `${NPM_TOKEN}`) and validates against the registry via `/-/whoami`
2. Package Enumeration - three-stage fallback: write-permission API (`/-/user/org.couchdb.user:<username>/package`) → search API (`/-/v1/search?text=maintainer:<username>&size=250`) → `DIST_PACKAGES` env var list
3. Payload Injection - see version breakdown below
4. Evasion- patches `.npmignore` to remove any rules that would exclude the payload file; strips `prepare`, `prepublishOnly`, and `prepack` lifecycle scripts before publishing; publishes with `--ignore-scripts`
5. Publish - uses the stolen token scoped to the correct registry per-package; also bundles `public.pem` alongside the payload so future infections can encrypt their exfil
After propagation, a full report including the list of infected packages and the operator log is sent back to C2.
Payload Injection: Version Breakdown
Direct inspection of the four available package samples reveals a clear upgrade between 1.1.12 and 1.1.13:
Version | Webhook State | Injection technique |
|---|---|---|
1.1.11 | Disabled | Direct |
1.1.12 | Disabled | Direct |
1.1.13 | Enabled | Loader stub (base64 + temp exec) |
1.1.14 | Enabled | Loader stub (base64 + temp exec) |
1.1.11 and 1.1.12 - Direct copy: The payload script is copied wholesale into the target package. The actual malware file persists on disk in the installed package under `scripts/check-env.js`.
1.1.13 and 1.1.14 - Loader stub: A thin wrapper (`check-env.cjs`) is installed instead. It contains the full payload base64-encoded as a string literal. At runtime the stub decodes it into a process-specific temp directory (.n<pid>), executes it, and deletes the directory:
The payload decoded from the stub contains the hardcoded webhook key. Meaning all victims of 1.1.13 and 1.1.14 had their full credential harvest (including Chrome passwords and wallet data) sent to the webhook endpoint in addition to the ICP canister. Victims of 1.1.11 and 1.1.12 had data sent to the canister only.
Versions 1.1.13 and 1.1.14 also carry the old `check-env.js` from earlier infections within the package tarball, though it is no longer invoked.
Cross-Ecosystem PyPI Propagation
PyPI propagation is also gated. It requires a PyPI token (`TWINE_PASSWORD` env or `~/.pypirc`) and a target package list (`PY_DIST_PACKAGES` env var). Critically, the registry defaults to `http://pypiserver:8081` - a local internal registry - not real PyPI. Real propagation requires `PYPI_REGISTRY` to be overridden to `https://upload.pypi.org/legacy/`.
When active, the worm creates a new Python package with a `.pth` file (`<module>_init.pth`) that is installed into `site-packages` via a custom `PostInstall` class. A `.pth` file in `site-packages` is executed on every Python invocation on the machine, giving the attacker persistent credential harvesting across any Python process.
The .pth payload is a self-contained Python one-liner that sweeps environment variables, cloud credentials, SSH keys, `.env` files, and package registry configs, then exfils to a separate endpoint:
hxxps://telemetry.api-monitor[.]com/v1/drop
This is distinct from the main telemetry endpoint used by the npm payload, allowing the attacker to distinguish npm-origin vs PyPI-origin victims.
What Should You Do
1. Triage & Contain - Audit `package.json`, `package-lock.json`, and `node_modules` across all environments. If `pgserve` at any version 1.1.11-1.1.14 is present, treat the environment as compromised.
2. Rotate All Credentials - Any environment where `pgserve` was installed must have all credentials rotated. The harvest scope is extremely broad:
npm tokens and all package registry tokens
AWS, Azure, GCP credentials
GitHub, GitLab tokens and SSH keys
Kubernetes, Docker, Terraform, Pulumi credentials
Any API keys present in environment variables or `.env` files
Database passwords
3. Audit npm Packages - If an npm token was present in the infected environment, check whether any packages you maintain received unexpected new versions. The worm publishes under your own token - look for patch bumps you did not author.
4. Check for PyPI Propagation - If `TWINE_PASSWORD` or `~/.pypirc` was present, audit your PyPI packages for unexpected releases and check for `.pth` files in Python's `site-packages`.
5. Block C2 Infrastructure - Firewall `telemetry.api-monitor[.]com` and monitor for outbound connections to `*.icp0[.]io`.
6. Check Crypto Wallets - If MetaMask, Phantom, Exodus, Atomic, or other wallet apps were present on the infected machine, assume wallet data was exfiltrated. Consider those wallets compromised.
Affected Versions
Version | Published At |
|---|---|
1.1.11 | 2026-04-21 22:14 UTC |
1.1.12 | 2026-04-21 22:26 UTC |
1.1.13 | 2026-04-21 23:26 UTC |
1.1.14 | 2026-04-22 03:35 UTC |
Do not install any version of `pgserve` until a clean release is confirmed.
IOCs
Affected Package
pgserve v1.1.11 - v1.1.14 (npm)
C2 Infrastructure
ICP canister:
hxxps://cjn37-uyaaa-aaaac-qgnva-cai.raw.icp0[.]io/dropICP canister ID:
cjn37-uyaaa-aaaac-qgnva-caiWebhook (v1.1.13+):
hxxps://telemetry.api-monitor[.]com/v1/telemetryWebhook HMAC signing key:
ff423c72061d8ee71bfdbafc4970dcc7de9eeab543eaed8d131731b5631acb48PyPI exfil:
hxxps://telemetry.api-monitor[.]com/v1/drop
Injected Files
npm worm payload (v1.1.11-1.1.12, direct copy): `scripts/check-env.js`
npm worm loader stub (v1.1.13-1.1.14, evasive): `scripts/check-env.cjs`
npm worm payload (files-based packages): `lib/env-compat.js` or `lib/env-compat.cjs`
npm worm RSA key: `scripts/public.pem` or `lib/public.pem`
PyPI `.pth` payload: `<module_name>_init.pth` in `site-packages`
Behavioural Indicators
Session ID prefix in network traffic:
tel-npm registry user-agent:
npm/10.8.2 node/v20.18.0Temp directories created during worm propagation:
/tmp/dist-*(npm),/tmp/pydist-*(PyPI)Temp execution directory (loader stub, v1.1.13+):
/tmp/.n<pid>/Chrome temp database:
/tmp/chrome_login_data_<timestamp>.dbEnvironment variable
_PKG_INIT=1set on execution
SHA-1 Integrity Hashes
Version | SHA-1 |
|---|---|
1.1.11 |
|
1.1.12 |
|
1.1.13 |
|
1.1.14 |
|
MITRE ATT&CK
ID | Technique |
|---|---|
Supply Chain Compromise: Compromise Software Supply Chain | |
Command and Scripting Interpreter: JavaScript | |
System Information Discovery (hostname, platform, CI context, repository, branch, commit collected) | |
Process Discovery (scanning | |
Unsecured Credentials: Credentials In Files | |
Credentials from Password Stores: Credentials from Web Browsers | |
Data from Local System (wallet files, SSH keys, shell history) | |
Obfuscated Files or Information (payload base64-encoded inside loader stub in v1.1.13+) | |
Exfiltration Over C2 Channel | |
Web Service (ICP blockchain canister as C2) | |
Indicator Removal: File Deletion (loader stub deletes temp execution directory after payload runs) | |
Masquerading: Match Legitimate Name or Location ( | |
Software Deployment Tools (npm worm self-propagation via registry publish) | |
Event Triggered Execution (.pth file persistence on PyPI targets) |
Citations and Acknowledgements
pgserve on npm: https://www.npmjs.com/package/pgserve
Ossprey - Telnyx PyPI compromise: https://ossprey.com/blog/telnyx-pypi-malware-wav/
Ossprey - Trivy supply chain attack: https://ossprey.com/blog/trivy-supply-chain-attack/




