A sophisticated malware campaign dubbed CanisterWorm is exploiting the npm ecosystem, a critical hub for JavaScript developers, by compromising legitimate publisher accounts to distribute malicious code. This supply chain attack, attributed to a threat actor group known as “TeamPCP,” stealthily injects credential-stealing malware into widely used packages, turning trusted development tools into vectors for theft. Security researchers from Socket, Endor Labs, and JFrog have been instrumental in uncovering and tracking the evolving scope of this threat.
Recent findings by JFrog researchers have identified new, previously unreported compromised package versions linked to CanisterWorm, significantly expanding the known impact of the campaign. These poisoned packages often appear as routine SDK version updates, making them difficult for developers to distinguish from legitimate software. The malware’s insidious nature lies in its ability to spread autonomously, turning a single compromised account into a launchpad for poisoning numerous downstream projects.
Inside the Worm’s Infection Chain
The CanisterWorm attack chain begins when a developer unknowingly installs a tainted npm package. A malicious `postinstall` hook, embedded within the `package.json` file of the compromised package, executes automatically upon installation. This hook silently deploys a Python backdoor onto the host system without raising any immediate alerts. On Linux systems, the malware further establishes persistence by registering a background service named `pgmon` via `systemd`, ensuring it remains active even after system reboots.
Once installed, the Python backdoor communicates with a command-and-control (C2) server hosted on the Internet Computer Protocol (ICP) blockchain. This decentralized approach makes the malicious network traffic blend seamlessly with regular web requests, significantly complicating detection by traditional network monitoring tools. Secondary payloads are downloaded and executed from this C2 infrastructure, with execution state being meticulously tracked by the malware.
The most alarming aspect of CanisterWorm is its autonomous propagation mechanism. The malware actively scans for npm authentication tokens, commonly found in `.npmrc` files or as environment variables such as `NPM_TOKEN` or `NPM_TOKENS`. Upon successfully stealing these credentials, a `deploy.js` script within the malware queries the npm registry to identify all packages maintained by the compromised developer. It then systematically increments the patch version number of each of these packages and publishes the malicious updates automatically, thus expanding the attack surface exponentially.
Known Compromised Packages Identified
JFrog researchers have identified a comprehensive list of compromised npm packages and their affected versions. Developers using any of these packages should assume their environments may be compromised and take immediate action. The list includes a wide array of packages across various namespaces, with specific attention drawn to those under the `@emilgroup` and `@teale.io` namespaces, among others.
| Package Name | Compromised Version(s) | JFrog X-ray ID |
|---|---|---|
@pypestream/floating-ui-dom |
2.15.1 | XRAY-955001 |
@leafnoise/mirage |
2.0.3 | XRAY-954938 |
@opengov/ppf-backend-types |
1.141.2 | XRAY-954962 |
eslint-config-ppf |
0.128.2 | XRAY-954936 |
react-leaflet-marker-layer |
0.1.5 | XRAY-954942 |
react-leaflet-cluster-layer |
0.0.4 | XRAY-954943 |
react-autolink-text |
2.0.1 | XRAY-954959 |
opengov-k6-core |
1.0.2 | XRAY-954926 |
jest-preset-ppf |
0.0.2 | XRAY-954956 |
cit-playwright-tests |
1.0.1 | XRAY-954934 |
eslint-config-service-users |
0.0.3 | XRAY-954950 |
babel-plugin-react-pure-component |
0.1.6 | XRAY-954955 |
@opengov/form-renderer |
0.2.20 | XRAY-955058 |
@opengov/qa-record-types-api |
1.0.3 | XRAY-954970 |
@opengov/form-builder |
0.12.3 | XRAY-954953 |
@opengov/ppf-eslint-config |
0.1.11 | XRAY-954967 |
@opengov/form-utils |
0.7.2 | XRAY-954958 |
react-leaflet-heatmap-layer |
2.0.1 | XRAY-954931 |
@virtahealth/substrate-root |
1.0.1 | XRAY-955055 |
@airtm/uuid-base32 |
1.0.2 | XRAY-954937 |
@emilgroup/setting-sdk |
0.2.3, 0.2.2, 0.2.1 | XRAY-955067 |
@emilgroup/partner-portal-sdk |
1.1.3, 1.1.2, 1.1.1 | XRAY-955063 |
@emilgroup/gdv-sdk-node |
2.6.3, 2.6.2, 2.6.1 | XRAY-955060 |
@emilgroup/docxtemplater-util |
1.1.4, 1.1.3, 1.1.2 | XRAY-955062 |
@emilgroup/accounting-sdk |
1.27.3, 1.27.2, 1.27.1 | XRAY-955054 |
@emilgroup/task-sdk |
1.0.4, 1.0.3, 1.0.2 | XRAY-955056 |
@emilgroup/setting-sdk-node |
0.2.3, 0.2.2, 0.2.1 | XRAY-955064 |
@emilgroup/task-sdk-node |
1.0.4, 1.0.3, 1.0.2 | XRAY-954923 |
@emilgroup/partner-sdk |
1.19.3, 1.19.2, 1.19.1 | XRAY-955065 |
@emilgroup/numbergenerator-sdk-node |
1.3.3, 1.3.2, 1.3.1 | XRAY-955066 |
@emilgroup/customer-sdk |
1.54.5, 1.54.4, 1.54.3, 1.54.2, 1.54.1 | XRAY-954924 |
@emilgroup/commission-sdk |
1.0.3, 1.0.2, 1.0.1 | XRAY-955068 |
@emilgroup/process-manager-sdk |
1.4.2, 1.4.1 | XRAY-955069 |
@emilgroup/changelog-sdk-node |
1.0.3, 1.0.2 | XRAY-955061 |
@emilgroup/document-sdk-node |
1.43.6, 1.43.5, 1.43.4, 1.43.3, 1.43.2, 1.43.1 | XRAY-954947 |
@emilgroup/commission-sdk-node |
1.0.3, 1.0.2, 1.0.1 | XRAY-955053 |
@emilgroup/document-uploader |
0.0.12, 0.0.11, 0.0.10 | XRAY-955057 |
@emilgroup/discount-sdk |
1.5.3, 1.5.2, 1.5.1 | XRAY-954929 |
@emilgroup/discount-sdk-node |
1.5.2, 1.5.1 | XRAY-955059 |
@teale.io/eslint-config |
1.8.16–1.8.9 (8 versions) | XRAY-954945 |
@emilgroup/insurance-sdk |
1.97.6–1.97.1 (6 versions) | XRAY-954928 |
@emilgroup/account-sdk |
1.41.2, 1.41.1 | XRAY-954949 |
@emilgroup/account-sdk-node |
1.40.2, 1.40.1 | XRAY-954927 |
@emilgroup/accounting-sdk-node |
1.26.2, 1.26.1 | XRAY-954965 |
@emilgroup/api-documentation |
1.19.2, 1.19.1 | XRAY-954960 |
@emilgroup/auth-sdk |
1.25.2, 1.25.1 | XRAY-954966 |
@emilgroup/auth-sdk-node |
1.21.2, 1.21.1 | XRAY-954964 |
@emilgroup/billing-sdk |
1.56.2, 1.56.1 | XRAY-954951 |
@emilgroup/billing-sdk-node |
1.57.2, 1.57.1 | XRAY-954948 |
@emilgroup/claim-sdk |
1.41.2, 1.41.1 | XRAY-954961 |
@emilgroup/claim-sdk-node |
1.39.2, 1.39.1 | XRAY-954925 |
@emilgroup/customer-sdk-node |
1.55.2, 1.55.1 | XRAY-954944 |
@emilgroup/document-sdk |
1.45.2, 1.45.1 | XRAY-954941 |
@emilgroup/gdv-sdk |
2.6.2, 2.6.1 | XRAY-954930 |
@emilgroup/insurance-sdk-node |
1.95.2, 1.95.1 | XRAY-954933 |
@emilgroup/notification-sdk-node |
1.4.2, 1.4.1 | XRAY-954957 |
@emilgroup/partner-portal-sdk-node |
1.1.2, 1.1.1 | XRAY-954952 |
@emilgroup/partner-sdk-node |
1.19.2, 1.19.1 | XRAY-954935 |
@emilgroup/payment-sdk |
1.15.2, 1.15.1 | XRAY-954963 |
@emilgroup/payment-sdk-node |
1.23.2, 1.23.1 | XRAY-954969 |
@emilgroup/process-manager-sdk-node |
1.13.2, 1.13.1 | XRAY-954939 |
@emilgroup/public-api-sdk |
1.33.2, 1.33.1 | XRAY-954940 |
@emilgroup/public-api-sdk-node |
1.35.2, 1.35.1 | XRAY-954946 |
@emilgroup/tenant-sdk |
1.34.2, 1.34.1 | XRAY-954932 |
@emilgroup/tenant-sdk-node |
1.33.2, 1.33.1 | XRAY-954954 |
@emilgroup/translation-sdk-node |
1.1.2, 1.1.1 | XRAY-954968 |
Developers who have used any of the identified compromised package versions must treat their development environments as compromised. Immediate rotation of all npm publishing tokens, including those stored in `.npmrc` files, environment variables, and CI/CD pipeline secrets, is crucial. On Linux systems, it is recommended to stop and disable the `pgmon` service using `systemctl`, and remove its associated service files and directories. Additionally, the temporary files `/tmp/pglog` and `/tmp/.pg_state` should be deleted.
Affected `node_modules` directories should be purged and rebuilt from scratch using verified and safe package versions. For developers whose tokens have been compromised, manually unpublishing the malicious package versions from the npm registry is necessary, as simply publishing a newer version does not revoke access for users who may have already installed the infected release. A proactive defensive measure is to globally configure npm to ignore scripts by running `npm config set ignore-scripts true`, which will prevent `postinstall` hooks from executing silently on future installations.

