Security News

Cybersecurity news aggregator

HIGH Vulnerabilities Snyk

A Mini Shai-Hulud Has Appeared": Bun-Based Stealer Hits SAP @cap-js and mbt npm Packages

Attackers published malicious versions of four npm packages (`mbt@1.2.48`, `@cap-js/db-service@2.10.1`, `@cap-js/sqlite@2.2.2`, and `@cap-js/postgres@2.2.2`) containing a preinstall hook that downloads the Bun runtime to execute an obfuscated credential stealer. The payload self-propagates via npm and creates dead-drop repositories on compromised developer machines. Users must immediately remove these specific compromised versions and revert to a known-good prior release, as no patched versions are detailed in the provided text.
Read Full Article →

Snyk Blog In this article Affected packages and Snyk advisories Why this is called "Mini" Shai-Hulud (and what's actually different) How the attack works The persistence mechanism Indicators of compromise What to do A Mini Shai-Hulud Has Appeared": Bun-Based Stealer Hits SAP @cap-js and mbt npm Packages Written by Stephen Thoemmes April 29, 2026 0 mins read On April 29, 2026, attackers published malicious versions of four npm packages in the SAP development ecosystem: mbt , @cap-js/db-service , @cap-js/sqlite , and @cap-js/postgres . Each compromised release ships a preinstall hook that downloads the Bun JavaScript runtime from GitHub Releases and uses it to execute an ~11.6 MB obfuscated credential stealer. The payload tags itself with a hardcoded description, "A Mini Shai-Hulud has Appeared" , which is appearing in public GitHub search results in real time as compromised developer machines create dead-drop repositories on their own accounts. The campaign reuses the Shai-Hulud name (the dead-drop repositories are tagged with it) and includes functional npm self-propagation code per StepSecurity's full deobfuscation. As of publication, only the four originally compromised packages have been observed in the wild; the technical breakdown and the compromised npm account that enabled the initial publishes are detailed below. Snyk has published advisories for all four compromised versions, and the affected releases are flagged by snyk test and shown on each package's Snyk Advisor page. Affected packages and Snyk advisories Package Compromised version Snyk advisory Approx. weekly downloads mbt 1.2.48 SNYK-JS-MBT-16321404 ~52,000 @cap-js/db-service 2.10.1 SNYK-JS-CAPJSDBSERVICE-16321402 ~260,000 @cap-js/sqlite 2.2.2 SNYK-JS-CAPJSSQLITE-16321405 ~250,000 @cap-js/postgres 2.2.2 SNYK-JS-CAPJSPOSTGRES-16321403 ~10,000 Download estimates from Socket's analysis . All four packages are part of the SAP Cloud Application Programming Model toolchain. mbt is the npm-distributed Cloud MTA Build Tool used to build deployment archives for SAP cloud applications, and the @cap-js/* packages provide database services for CAP applications. The malicious versions were published in a tight window on April 29, 2026, all UTC, with timestamps verified directly from registry.npmjs.org and the GitHub API: 09:55:25 : mbt@1.2.48 published from the cloudmtabot npm account. 10:01:07 : first victim dead-drop repository appears on GitHub ( gruposbftechrecruiter/siridar-navigator-935 , per GitHub API timestamps). 11:25:47 : @cap-js/sqlite@2.2.2 published. 12:03 to 12:04 : StepSecurity files disclosure issues on cap-js/cds-dbs#1588 and SAP/cloud-mta-build-tool#1224 . An independent third disclosure, SAP/open-ux-tools#4616 by longieirl , is filed at 14:02. 12:14:00 : @cap-js/postgres@2.2.2 and @cap-js/db-service@2.10.1 published. 13:31 : cap-js maintainer chgeo responds : "Thanks for lettings us know. We are about to clean up the packages with highest priority and investigate the root causes." 13:46 : SAP publishes clean post-incident versions via the cap-npm GitHub Actions OIDC trusted publisher: @cap-js/db-service@2.11.0 , @cap-js/sqlite@2.4.0 , @cap-js/postgres@2.3.0 (with @cap-js/hana@2.8.0 as a coordinated bump). 14:02:50 : SAP engineer patricebender files cap-js/cds-dbs PR #1592 , gating npm publish behind a manual approval environment, with the verbatim root-cause statement quoted above. 14:24 : cloud-mta-build-tool collaborator kbarnold responds : "Thank you for the report. We're working on it with high priority." @cap-js/sqlite@2.2.2 was unpublished from npm shortly after detection. The remaining malicious versions carry npm deprecation strings: mbt@1.2.48 is flagged with "SECURITY: This version contains malicious code. Do not use." while the @cap-js/* malicious versions read "DO NOT USE. This version contains unknown content." Critically, mbt@1.2.48 was still the latest dist-tag for mbt at the time of writing, with no remediated mbt version published yet, meaning a bare npm install mbt still resolves to the malicious tarball. No CVE, GHSA , or OSV record had been assigned for any of the four packages at publication time; the only authoritative public sources are the four vendor blogs cited throughout this post and the three GitHub disclosure issues. Why this is called "Mini" Shai-Hulud (and what's actually different) The Shai-Hulud naming convention has appeared in npm supply chain incidents twice before. The original Shai-Hulud campaign in September 2025 hit @ctrl/tinycolor , ngx-bootstrap , ng2-file-upload , and a long tail of dependents (Snyk's zero-day vulnerability report tracked it as it unfolded). The follow-on wave SHA1-Hulud in November 2025 expanded to over 600 distinct packages, including releases from Zapier, PostHog, and Postman. Kaspersky's Securelist team has published independent technical analyses of both prior waves (detection name HEUR:Worm.Script.Shulud.gen ) at securelist.com/shai-hulud-worm-infects-500-npm-packages and securelist.com/shai-hulud-2-0 . Play Video: A Mini Shai-Hulud Has Appeared": Bun-Based Stealer Hits SAP @cap-js and mbt npm Packages Shai-Hulud NPM Attack: Remediation with Snyk (short walkthrough on identifying and remediating Shai-Hulud-affected dependencies in your projects using Snyk's CLI and advisor pages). Both prior campaigns demonstrated worm behavior: the payload would harvest a victim's npm token, then use it to publish itself into every other package the token had write access to. Public attention has tracked accordingly: the Wikipedia article on the Dune sandworm peaked at 2,772 views during the November 2025 SHA1-Hulud disclosure (the highest single-day count in a 16-month dataset, roughly four times the article's average), and the broader "supply chain attack" Wikipedia article reached its highest monthly average on record in April 2026, the month of Mini Shai-Hulud (310 views/day, per Wikimedia REST API ). The April 29 campaign reuses the Shai-Hulud branding (the dead-drop repositories are named with the description string) but the public evidence so far points to a narrower set of behaviors: Credential theft: observed and detailed by multiple researchers. Persistence injection into developer tooling configs: novel and detailed below. Autonomous npm self-publishing: the code is present and functional per StepSecurity's static deobfuscation . The payload harvests npm tokens with regex /npm_[A-Za-z0-9]{36,}/g , validates each one against registry.npmjs.org/-/npm/v1/tokens (filtering for bypass_2fa: true plus org-level write scope), enumerates accessible packages, patches setup.mjs and execution.js into a tarball copy, and publishes via a direct PUT to the npm registry without invoking the npm CLI. As of publication, no fifth package outside the original four has been observed in the wild carrying the malicious payload, but the worm capability is an empirical fact, not an inference. CI-pipeline hijack as the access vector: SAP's own first-party statement in PR #1592 by patricebender on cap-js/cds-dbs (filed 14:02 UTC the same day) reads: "On April 29, 2026, a supply chain attack compromised the repository — an unauthorized actor pushed malicious commits that hijacked the release workflow and triggered unauthorized npm publications. The attacker was able to publish compromised packages because the workflow had publish permissions without any manual approval gate." The fix is to gate npm publish behind an environment review. Per npm registry data, all four malicious versions were published from the cloudmtabot account (the legitimate mbt maintainer); SAP's clean post-incident versions at 13:46 UTC were published via the cap-npm GitHub Actions OIDC trusted publisher, not by cloudmtabot . The cloudmtabot account itself has since been suspended on npm. Maintainer-credential compromise is the recurring entry point for this class of incident, but the SAP statement specifically points to the workflow's missing approval gate as the structural root cause. The credential theft enables npm self-replication, but the immediately observed activity is exfiltration and a new persistence mechanism. Defensive priorities (lockfile audit, credential rotation, lifecycle script policy) are the same either way. How the attack works The compromise pattern is consistent across all four packages: the malicious tarball preserves the legitimate package files unchanged (so the CLI still works after install) and adds two new files, setup.mjs (a 4.5 KB plaintext Bun loader, byte-identical across all four packages) and execution.js (an obfuscated payload at exactly 11,678,349 bytes, with hashes that differ between mbt and the @cap-js/* packages). For mbt only, the malicious package.json also adds three dependencies ( axios , tar , unzip-stream ) that don't appear in the prior clean release; the three cap-js packages added only the preinstall hook to their existing package.json . Fetching a small loader at install time and using it to pull in a much larger runtime and payload is a pattern Snyk has seen elsewhere this year, including the axios cross-platform RAT incident where the install hook reached out for a native binary delivered through a separate dependency. For mbt , the diff between 1.2.47 (clean) and 1.2.48 (malicious) looks like this: 1 // 1.2.47: no scripts block 2 // 1.2.48: 3 { 4 "scripts" : { 5 "preinstall" : "node setup.mjs" 6 }, 7 "dependencies" : { 8 "axios" : "^1.13.5" , 9 "tar" : "^7.5.7" , 10 "unzip-stream" : "^0.3.4" 11 } 12 } preinstall runs before npm reports any output to the user and before any conditional --ignore-scripts policy can intervene if the flag isn't set. The hook executes setup.mjs , which: Detects platform and architecture (including Alpine/musl detection on Linux). Checks whether bun is already on PATH via hasCommand("bun") . If present, skips the download step and uses the existing binary. Otherwise downloads Bun 1.3.13 from GitHub Releases (following HTTP redirects without destinatio

Share this article