Mini Shai-Hulud Targets SAP-Related npm Packages With Bun Payloads and IDE Persistence

Summary
StepSecurity reports four compromised SAP-related npm packages carrying a Bun-based credential stealer. Its analysis details npm worm propagation, GitHub Actions secret theft, encrypted exfiltration and persistence through Claude Code and VS Code hooks.
Key points
- StepSecurity identifies [email protected], @cap-js/[email protected], @cap-js/[email protected] and @cap-js/[email protected] as compromised, with identical preinstall loaders launching an obfuscated 11.6 MB payload through Bun.
- Researchers attribute the mbt release to a stolen npm token and describe abuse of a compromised developer GitHub account and repository-wide OIDC publishing trust for the @cap-js releases.
- A controlled GitHub Actions installation captured the payload spawning Python to read Runner.Worker memory through /proc and extract serialized secrets.
- Recovered code harvests npm, GitHub and cloud credentials, injects workflows to collect repository secrets, and uses stolen npm tokens to publish infected packages directly through registry APIs.
- The payload installs Claude Code SessionStart hooks and VS Code folder-open tasks in accessible repositories to enable re-execution and further propagation.
- Collected data is encrypted with AES-256-GCM, with keys wrapped using RSA-4096, before exfiltration through GitHub repositories. The payload also exits when it detects a Russian locale.
- Recommended recovery includes removing compromised versions, rotating exposed credentials, auditing unauthorized npm releases and GitHub repositories, and removing malicious IDE configurations and injected workflows across branches.
Article Details
- Attack Vectors
- Compromised npm releases use a preinstall hook to launch setup.mjs, download Bun, and execute an obfuscated credential-stealing payload.
- A stolen static npm automation token was used to publish [email protected]; the source does not establish how the token was initially obtained.
- A compromised SAP developer GitHub account was used to modify release automation and abuse repository-wide npm OIDC trust, exposing a double-base64-encoded publishing token in workflow logs.
- Stolen npm tokens enable autonomous propagation through direct registry API publishing of infected package versions.
- Repository modifications install VSCode folderOpen tasks and Claude Code SessionStart hooks that re-execute copied malware.
- Injected GitHub Actions workflows serialize repository secrets into downloadable workflow artifacts.
- Credential collection targets local files, environment variables, cloud secret stores, and readable memory of the GitHub Actions Runner.Worker process.
- Encrypted stolen data is committed to public repositories created under victim accounts; an alternative sender uses an embedded attacker token to locate a C2 repository.
- Defensive Notes
- Check for [email protected], @cap-js/[email protected], @cap-js/[email protected], and @cap-js/[email protected]. The article's unpublishing paragraph reverses the postgres and db-service version assignments relative to its confirmed-package list.
- The source recommends removing compromised releases and installing [email protected], @cap-js/[email protected], @cap-js/[email protected], or @cap-js/[email protected] with --ignore-scripts.
- Rotate credentials accessible on affected developer machines and CI jobs, including npm and GitHub tokens, cloud credentials, SSH keys, Kubernetes tokens, environment secrets, and accessible cloud secret-store contents.
- Inspect all repository branches for malicious .vscode/tasks.json, .claude/settings.json, copied execution.js payloads, and setup.mjs loaders; the malware targets up to 50 branches.
- Remove the dependabout/github_actions/format/setup-formatter branch and inspect .github/workflows/format-check.yml for toJSON(secrets) and the format-results artifact.
- Find and remove unauthorized public repositories with the description 'A Mini Shai-Hulud has Appeared' or the documented Dune-themed naming pattern.
- Audit npm publishing history for unauthorized versions and unpublish infected releases; pin known-good exact dependency versions.
- Monitor Runner.Worker memory access through /proc and unexpected processes during package installation. StepSecurity reports that Harden-Runner flagged this behavior in its controlled test.
- Treat discovered exfiltration repositories as evidence of potential credential exposure even when their contents are encrypted.
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| SHA256 | 4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34 | Malicious setup.mjs loader hash reported identical across all four compromised packages. |
| SHA256 | 6f933d00b7d05678eb43c90963a80b8947c4ae6830182f89df31da9f568fea95 | Malicious execution.js payload in @cap-js/sqlite. |
| SHA256 | 80a3d2877813968ef847ae73b5eeeb70b9435254e74d7f07d8cf4057f0a710ac | Credential-stealing execution.js payload in [email protected]. |
MITRE ATT&CK
T1003 · OS Credential DumpingThe Python collector dumps readable Runner.Worker address-space segments and extracts plaintext secrets using the runner's internal serialization format.T1027 · Obfuscated Files or InformationThe 11.6 MB payload uses rotated string tables, a custom base64 alphabet, and the ctf-scramble-v2 cipher to conceal sensitive strings.T1036 · MasqueradingMalicious commits impersonate claude or dependabot[bot], use routine maintenance messages, and create a dependabout branch resembling Dependabot activity.T1059.006 · PythonAn embedded Python script scans /proc and reads Runner.Worker memory to recover CI secrets.T1059.007 · JavaScriptThe preinstall hook executes setup.mjs with Node.js, then launches the JavaScript execution.js payload under Bun.T1070.004 · File DeletionThe loader deletes its temporary Bun runtime directory after payload execution.T1078 · Valid AccountsThe attacker used a compromised SAP developer GitHub account and stolen npm publishing credentials to modify release workflows and publish malicious releases.T1140 · Deobfuscate/Decode Files or InformationThe malware decodes scrambled strings and decompresses its embedded RSA public key at runtime.T1195.002 · Compromise Software Supply ChainFour compromised SAP-related npm packages distribute a malicious preinstall loader and credential stealer.T1528 · Steal Application Access TokenCollectors steal npm tokens, GitHub PATs and OAuth tokens, and cloud access credentials for subsequent access and propagation.T1552.001 · Credentials In FilesThe stealer harvests credential files including .npmrc, cloud credential stores, .git-credentials, Kubernetes configuration, and .env files.T1552.004 · Private KeysFilesystem harvesting includes private SSH keys such as ~/.ssh/id_rsa, ~/.ssh/id_ed25519, and host-key paths.T1552.005 · Cloud Instance Metadata APICredential collection references cloud instance and task metadata endpoints, including EC2 IMDS, ECS task metadata, and Azure IMDS.T1567.001 · Exfiltration to Code RepositoryThe payload commits encrypted stolen credentials into public GitHub repositories created on victim accounts.T1614.001 · System Language DiscoveryThe payload checks Intl locale and POSIX locale environment variables and exits when they contain ru.
Threat Actors
Malware
Vendors
Amazon Web Servicesnew aa(), // AWS credentials + Secrets ManagerGitHubwithin minutes of the first malicious publish. We responsibly disclosed[1][2] the breach by creating GitHub issues on the affected repositories and directly notifying SAP’s security team.SAPdisclosed[1][2] the breach by creating GitHub issues on the affected repositories and directly notifying SAP’s security team.StepSecurityStepSecurity’s OSS AI Package Analyst and Harden-Runner detected this compromise within minutes of the first malicious publish. We responsibly disclosed[1][2] the breach by creating GitHub issues on the affected
Products
@cap-js/db-serviceFour confirmed compromised packages, [email protected], @cap-js/[email protected], @cap-js/[email protected], and @cap-js/[email protected], all carry an identical malicious preinstall hook that bootstraps the Bun JavaScript runtime@cap-js/postgresFour confirmed compromised packages, [email protected], @cap-js/[email protected], @cap-js/[email protected], and @cap-js/[email protected], all carry an identical malicious preinstall hook that bootstraps the Bun JavaScript runtime@cap-js/sqliteFour confirmed compromised packages, [email protected], @cap-js/[email protected], @cap-js/[email protected], and @cap-js/[email protected], all carry an identical malicious preinstall hook that bootstraps the Bun JavaScript runtimeBunand @cap-js/[email protected], all carry an identical malicious preinstall hook that bootstraps the Bun JavaScript runtime and executes a heavily obfuscated 11.6 MB credential stealer. The setup.mjs loader isClaude CodeThe payload commits a .claude/settings.json abusing Claude Code's SessionStart hook and a .vscode/tasks.json with "runOn": "folderOpen" into every accessible repo, signed [email protected] with "chore:GitHub ActionsTo observe the malware's behavior at runtime, we installed @cap-js/[email protected] in a controlled GitHub Actions workflow with StepSecurity Harden-Runner enabled in audit mode. Harden-Runner monitors all outbound networkHarden-RunnerStepSecurity’s OSS AI Package Analyst and Harden-Runner detected this compromise within minutes of the first malicious publish. We responsibly disclosed[1][2] the breach by creating GitHub issues on the affectedmbtFour confirmed compromised packages, [email protected], @cap-js/[email protected], @cap-js/[email protected], and @cap-js/[email protected], all carry an identical malicious preinstall hook that bootstraps the Bun JavaScript runtimenpmaction: a developer or CI/CD pipeline installs the first compromised package, the stealer harvests their npm token, and the worm publishes itself to every other package that token can reach. Stolen credentials areVSCodeThe payload commits a .claude/settings.json abusing Claude Code's SessionStart hook and a .vscode/tasks.json with "runOn": "folderOpen" into every accessible repo, signed [email protected] with "chore:
