Updated and Ongoing Supply Chain Attack Targets CrowdStrike npm Packages
Socket detected multiple compromised CrowdStrike npm packages, continuing the "Shai-Hulud" supply chain attack that has now impacted nearly 500 packages.
Kush Pandya
Peter van der Zee
Olivia Brown
Socket Research Team
September 16, 2025
Multiple CrowdStrike npm packages published by the crowdstrike-publisher npm account were compromised. This looks like a continuation of the ongoing malicious supply chain campaign known as the “Shai-Hulud attack” that previously compromised tinycolor and 40+ other packages. The malware is identical to this previous campaign, which includes a bundle.js script that:
Downloads and executes TruffleHog, a legitimate secret scanner
Searches host systems for tokens and cloud credentials
Validates discovered developer and CI credentials
Creates unauthorized GitHub Actions workflows within repositories
Exfiltrates sensitive data to a hardcoded webhook endpoint
The affected packages were quickly removed by the npm registry. The malware includes a workflow file named shai-hulud.yaml, a nod to the sandworms in Dune. While not a unique reference, its presence reinforces that the attacker deliberately branded the campaign “Shai-Hulud.”
In our previous analysis we found the payload writes a GitHub Actions workflow file named shai-hulud-workflow.yml. Around the same time, nearly 700 public repositories titled “Shai-Hulud Migration” appeared on GitHub. While the precise role of these repos is still under investigation, their naming and timing suggest they may be artifacts of attacker automation used to persist or stage the workflow.
Our previous post has further details on the malware itself. The bash block uses a GitHub personal access token if present, writes a GitHub Actions workflow into .github/workflows, and exfiltrates collected content to a webhook.
The script combines local scanning with service specific probing. It looks for environment variables such as GITHUB_TOKEN, NPM_TOKEN, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY. It validates npm tokens with the whoami endpoint, and it interacts with GitHub APIs when a token is available. It also attempts cloud metadata discovery that can leak short lived credentials inside cloud build agents.
The workflow that it writes to repositories persists beyond the initial host. Once committed, any future CI run can trigger the exfiltration step from within the pipeline where sensitive secrets and artifacts are available by design.
The threat actor created 7 different versions of the worm, which we know because the worm has no ability to edit itself. There were multiple different seeder events as well as bursts of previous worms, as evidenced in the Timeline section.
V1 v. V2
Difference between version 1, de0e25a3e6c1e1e5998b306b7141b3dc4c0088da9d7bb47c1c00c91e6e4f85d6
These lines implement automated reconnaissance and credential harvesting, then exfiltrate that collected data to a GitHub repo under the attacker’s control. They gather local system/runtime info and cloud / credential-related data, build an object containing that telemetry, and if a GitHub client F is authenticated and a repo named "Shai-Hulud" does not exist create that repo and pass the collected object to it. The exfiltration mechanism remains identical.
The new version adds a single log line, Configuring, likely for debugging purposes.
Next, the new version checks if the AWS credentials are valid before requesting secret values, likely to avoid throwing or failing when provider credentials are missing. It also now targets Google Cloud instead of Azure, and collects secrets into arrays rather than embedding a valid status. The second has improvements in the code overall, like establishing an empty array and then filling it conditionally to reduce exceptions on invalid clients.
V2 v. V3
The difference between version 2, 81d2a004a1bca6ef87a1caf7d0e0b355ad1764238e40ff6d1b1cb77ad4f595c3,
and version 3, 83a650ce44b2a9854802a7fb4c202877815274c129af49e6c2d1d5d5d55c501e :
Version 3 makes the code overall smoother and stealthier. Version three corrects race conditions and prevents double callbacks, and uses chaining to improve robustness. It also avoids throwing exceptions from cleanup by swallowing errors. The third version will kill long running scans 30 seconds earlier than version 2, and exits when the trufflehog binary is not available. It also removes the Configuring logging message.
The GitHub token abuse happens earlier in the exploit, making the attack more reliable.
V3 v. V4
There is only one difference between version 3, 83a650ce44b2a9854802a7fb4c202877815274c129af49e6c2d1d5d5d55c501e, and version 4, 4b2399646573bb737c4969563303d8ee2e9ddbd1b271f1ca9e35ea78062538db. In Version 4, the tool attempts to iterate up to 20 packages of a single maintainer instead of 10 in one pass, accelerating the propagation.
V4 v. V5
Between Version 4, 4b2399646573bb737c4969563303d8ee2e9ddbd1b271f1ca9e35ea78062538db, and Version 5, dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c, the threat actor removes a check to see if the repo, Shai-Hulud already exists. Now, the repo is always created as long as the token is authenticated. This likely handles any race condition that may occur if multiple infected hosts run version 4 concurrently. Version 5 likely improves the yield, and removes one less API call, reducing obvious reconnaissance fingerprints in GitHub audit logs.
V5 v. V6
Version 5, dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c, and Version 6,46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09 have a few differences.
Version 6 is more succinct overall. It reduces noise and acts more stealthily by removing helper logs and adding a skip switch on the filesystem scan, which is the loudest step of the campaign. It also renames some variables and now exfiltrates the GitHub username.
V6 v. V7
Between Version 6, 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09, and Version 7, b74caeaa75e077c99f7d44f46daaf9796a3be43ecf24f2a1fd381844669da777, the threat actor only removes this:
# Convert to a regular repo temporarily to make changes\\n
git config --unset core.bare\\n
git reset --hard\\n\\n
# Remove .github/workflows directory if it exists and commit\\n if [[ -d ".github/workflows" ]]; then\\n
rm -rf .github/workflows\\n
git add -A\\n
git commit -m "Remove GitHub workflows directory"\\n
fi\\n\\n
# Convert back to bare repo for mirroring\\n
git config core.bare true\\n
rm -rf *\\n\\n
Version 6 used both the bare-repo filesystem manipulation technique and workflow-creation/exfil logic present in the above snippet. Version 7 removed the filesystem (git config / rm -rf) technique and retained the workflow/Actions + GitHub-API + webhook exfiltration behavior. This evasion is less noisy, and no longer leaves obvious forensic artifacts.
Throughout each of the versions, the actor tries to become stealthier and more efficient. Notably, there is no cryptowallet draining or obfuscation, which is different than the campaign targeting Nx npm packages from a few weeks ago.
Worm Behavior
The malware can self-propagate by automatically stealing credentials and then using those credentials to insert workflows into other repos. It automatically modifies and republishes packages as part of its propagation chain. However, it cannot self-propagate without those credentials, meaning eventually it will run out of credentials to steal from the packages it’s compromised.
Once it steals credentials and gains write and publish capabilities from those credentials, it obtains the target package artifact, unpacks the package, and then creates or replaces the package’s bundle.js file with its own malicious bundle.js. It also may edit the package.json file to add a postinstall script. Then it repacks the tarball and publishes the poisoned version. Now, when downstream users install the package, the malicious postinstall or injected bundle.js executes and can run the same discovery and publish code on the new host. Harvested credentials are used to target other repos and packages.
This is an example package.json from the first version, in rxnt-authentication:
Clearly, there is a postInstall script, calling to node bundle.js.
The tarball injection ensures the malicious code is present in the artifact itself, and is harder to notice if downstream users just npm install the package. Adding a postinstall script in package.json ensures automatic execution during npm install even if the package’s normal runtime doesn’t import the injected file.
This also explains why certain hashes re-emerged. It may not be because the threat actor decided versions 5 and 6 were superior, but actually because those versions found other accounts to propagate through. The GitHub Actions workflows trigger on events, not continuously, which would explain the gaps in time between the bursts from the malware. This may indicate that we have not seen the last of this malware yet.
Socket CEO Feross Aboukhadijeh and a16z partner Joel de la Garza discuss vibe coding, AI-driven software development, and how the rise of LLMs, despite their risks, still points toward a more secure and innovative future.