跳到正文
Hacker News· Sudhanshu231020·· 10 天前精选AI 评分78

Tensorlake npm 包遭 Mini Shai-Hulud 蠕虫投毒

Tensorlake is compromised using mini Shai Hulud

AI 导读

安全公司 SafeDep 在 Tensorlake 的 npm 包中发现凭据窃取蠕虫 Mini Shai-Hulud,该版本在安装时即运行,npm 曾将其作为月下载量约 106,000 的包的最新版本分发。

推荐理由

原文完整还原了攻击者绕过 npm token、借 GitHub 网页提交与发布流程投毒的时间线,可看到供应链攻击的新路径。

正文 · 原文

We found a credential-stealing worm in [email protected]. It runs on install, and npm was serving it as latest for a package with about 106,000 monthly downloads.

npm records the publish at 01:12:07 UTC. SafeDep’s automated analysis flagged it at 01:20 UTC (6:50 AM IST), eight minutes later.

SafeDep automated monitoring alert for tensorlake@0.5.144. The alert lists the new preinstall hook,
the Bun download, the Math_Symbol.js worm and its build tag, credential theft, the C2
domain and Ethereum contract, gh-token-monitor persistence, the injected workflow and hooks, and
the setup.mjs SHA-256 hash.

The payload is a new build of Mini Shai-Hulud, the 2026 wave of the Shai-Hulud worm family. It is the same worm as in the keyv and cacheable compromise.

  • Steals cloud, GitHub, npm, SSH, browser, and wallet credentials.
  • Spreads through npm packages and GitHub repositories.
  • Runs remote code from its command-and-control (C2) server.
  • Deletes the home directory on some machines when someone revokes the stolen GitHub token.

npm removed the version. The maintainers reverted the source in pull request #1016 and released 0.5.145.

What is new in this build

  • A hard-coded C2 domain, iseekaigogo[.]com.
  • A new Ethereum contract for C2 domain lookup.
  • A browser password stealer.
  • A remote code channel that runs C2 responses with eval.
  • A known deletion handler, rm -rf ~/. The keyv analysis could not name it.

How the payload reached npm

The attacker did not need an npm token. They used a repository administrator account to commit the payload to main through the GitHub web interface. Then they ran the project’s own release workflow.

Time, 2026-10-07 UTCCommitChange
01:20:15e90c47bbAdds typescript/lib/setup.mjs and typescript/lib/Math_Symbol.js
01:24:19c79bc475Adds the preinstall script
01:43:23ad5cf5a7Repairs the package.json syntax
02:13 to 03:44066b9d75, 936302da, 2ca2a7f1, 41b38f09Four revisions of the loader and one payload rebuild
03:57:54e65ed94aSets the version to 0.5.144

GitHub signs web interface commits, so all eight show as verified. The first preinstall edit broke the JSON:

typescript/package.json @ c79bc475

DIFF 5 lines

The fix came 19 minutes later. This suggests a person who edited files in a browser (inference).

On 8 October at 00:08 UTC, the account started publish_npm.yaml by hand. The build log shows the payload files in the tarball:

Publish to npm job, run 37706134202

TEXT 6 lines

  • The release used npm trusted publishing.
  • The sibling tensorlake-native-*@0.5.144 packages carry valid SLSA provenance for this run. The provenance is valid for a build of poisoned source.
  • The preinstall hook ran in this job and ended in 0.1 seconds. The loader skips CI, so the payload probably did not run on the runner (inference).

The loader

The shipped lib/setup.mjs uses RC4 string obfuscation. The worm carries a plain copy of it, which it plants in other packages and repositories:

Embedded loader in lib/Math_Symbol.js, decrypted

JS 15 lines

  • It downloads Bun 1.3.13 from the official GitHub release.
  • It runs Math_Symbol.js with Bun.
  • It exits on CI runners.

The shipped setup.mjs decodes to the same strings.

The payload

lib/Math_Symbol.js is 856,501 bytes. Its first line names the build:

lib/Math_Symbol.js, first line

JS 2 lines

Three layers hide the code:

  • An obfuscator string array hides identifiers.
  • A custom cipher hides configuration strings. It uses salt svksjrhjkcejg, the same as keyv.
  • AES-256-GCM hides eleven embedded files, including scripts, hooks, and two RSA public keys.

All layers decode without running the sample. An earlier 849,332-byte build in the commit history has the same configuration.

Command and control

Snippets below are deobfuscated and keep the original identifiers.

lib/Math_Symbol.js, C2 configuration (deobfuscated)

JS 2 lines

The worm posts data to hxxps://iseekaigogo[.]com:443/router. If that fails, it tries:

  1. Ethereum. It reads contract 0xb614155Fd88114d40549b259457Bcf921Df091B9 through 35 public RPC endpoints. On 8 October the contract returned iseekaigogo.com.
  2. Signed GitHub commits. It searches for thebeautifulmarchoftime and trusts only commits signed with an embedded RSA key.
  3. Dead-drop repositories. It commits encrypted data to new public repositories with the description Shai-Hulud: Here We Go Again.

Every C2 response can carry code. The worm runs it:

lib/Math_Symbol.js, applyRemoteCode (deobfuscated)

JS 7 lines

After exfiltration, the worm pings the C2 every 45 to 90 seconds and runs any code it gets back.

What it collects

CollectorTargets
File systemSSH keys, cloud CLI files, .npmrc, .env, Docker and Kubernetes files, ~/.claude.json, ~/.kiro/settings/mcp.json, shell history
Walletswallet.dat files, Exodus, Electrum, Ledger Live, and browser wallet extensions such as MetaMask and Phantom
Browser passwordsA binary from hxxps://iseekaigogo[.]com/hbd/<platform>
AWSSTS, SSM Parameter Store, and Secrets Manager
GitHub ActionsSecrets read from Runner.Worker process memory
Kubernetes, VaultService-account tokens and Vault tokens

The browser binary runs as dump -b all -c password -f json. These arguments match HackBrowserData (inference, the binary was not recovered). The worm exits on Russian locales.

The deletion watcher

The watcher arms only for stolen tokens whose account has no organizations:

lib/Math_Symbol.js, organization check (deobfuscated)

JS 3 lines

lib/Math_Symbol.js, deletion handler (deobfuscated)

JS 3 lines

  • gh-token-monitor checks the token every 60 seconds for 24 hours.
  • If GitHub returns 40x, for example after revocation, it deletes the home directory.
  • It runs as a systemd user service, a macOS LaunchAgent, or a Windows scheduled task.

Remove gh-token-monitor before you revoke GitHub tokens.

Propagation

  • npm tokens. It adds the payload and preinstall to every package the token can publish, bumps the patch version, and publishes.
  • npm trusted publishing. In CI, it adds a git dependency and signs the package with Fulcio and Rekor.
  • GitHub tokens. It commits Claude Code and VS Code hooks as author claude. It also plants a Run Copilot workflow that dumps all repository secrets, then deletes the run and branch.

The hook files and workflow match the keyv post. See also configuration files that run code.

Two bugs in the code may limit spread:

  • The npm path needs ./dist/Math_Symbol.js on disk and stops without it.
  • The planted loader looks for ai_init.js, but the worm writes math_init.js.

Indicators of compromise

TypeValue
Package[email protected], removed from npm
Tarball SHA-1843a898ab72793568d77c53ff9282e6ce7c66aea
lib/setup.mjs, 32,645 bytes25a0735d0db7dc40e5d45ce42d9c106067e6a66e184d967cfecfab17c3bcb5ef
lib/Math_Symbol.js, 856,501 bytesb50a00900399ba99fb6ce1fc151519cb99d44320ef2a631f2237e1aea0ad6fec
Earlier payload build, 849,332 bytes03aa53f01b5b0fc4899c44041da90d7c5aa29fd90e4d99a5b70270f672ee43e1
Build tagWORMTAG='tensrlake'
C2 domainiseekaigogo[.]com, paths /router and /hbd/
Ethereum contract0xb614155Fd88114d40549b259457Bcf921Df091B9, selector 0x53ed5143
Fallback search stringthebeautifulmarchoftime
Source commitse90c47bb through 6386121c in tensorlakeai/tensorlake
Persistencegh-token-monitor.service, com.user.gh-token-monitor, scheduled task gh-token-monitor
Persistence files~/.local/bin/gh-token-monitor.sh, ~/.config/gh-token-monitor/, %LOCALAPPDATA%\gh-token-monitor\monitor.ps1
Lock filetmp.ts018051808.lock in the temporary directory
Dead-drop descriptionShai-Hulud: Here We Go Again
Planted branchdependabot/github_actions/format/setup-formatter

References

来源:Hacker News · safedep.io