深海军蓝背景上的抽象技术插画:中左部有一个小巧的扁平矢量软件包立方体,一条细琥珀色线条向下勾连到一个紧凑的开发者工作站表示;一束纤细的青色和钢蓝色丝线从工作站向右展开,伸向松散网格上的许多小圆形节点,其中两个节点向上弯回软件包立方体,而最右侧的一个节点孤立且断开连接。画面中没有出现人物、徽标或文字。OrcaRouter 标志合成在右下角。
AI Safety Incidents

Tensorlake 的 npm SDK 被投毒,意在窃取 AI 代理凭据——并且如果你撤销了令牌,还会清空你的主目录

作者

Alistair Wren

发布日期

最新模型 · 20查看全部模型 →
基准测试:Artificial Analysis · 每日更新
返回全部文章

2026年10月8日,tensorlake的一个被投毒版本登上了 npm 注册表——它是 Tensorlake 平台的 npm SDK,该平台在隔离沙箱中运行不受信任的、由 LLM 生成的代码。版本 0.5.144 的发布时间为 01:12:07 UTC;Socket 的研究人员称,他们在大约十一分钟后对其进行了标记。这并非一次获授权的发布。Tensorlake 表示,攻击者将代码直接推送到该公司的 GitHub 仓库,并通过项目自身的发布工作流将其发布,因此这个包带有 有效的 npm provenance——即已签名的证明,标明其构建位置,而其源代码早已被投毒。该公司称,该版本存活了大约 1 小时 40 分钟,从 01:12 到 02:54 UTC,直到 npm 将其移除。

该载荷不需要导入,也不需要运行中的代理程序。一个 preinstall 钩子在包清单中执行了 node lib/setup.mjs,这是一个经过混淆的加载器,会把 Bun 运行时投放到机器上,并用它来运行 lib/Math_Symbol.js——一个 856,501 字节的凭据窃取程序和自传播蠕虫。任何对该版本进行普通安装的人都会执行它。因为它被打上了该包的 latest 标签,所以当该版本仍在线时,默认安装解析到的就是这个版本。

这份记录之所以值得留存,不在于载荷的大小——Shai-Hulud 家族此前已有记录。而在于目标列表。除了 npm 和 GitHub 令牌、AWS 凭据、Kubernetes 和 Vault 密钥、SSH 密钥、.env 文件、浏览器密码和加密货币钱包外,这个构建版本还专门收集了 AI 开发工具配置和 MCP 文件——这些与 Claude Code、Cursor、Kiro、Windsurf 和 Zed 相关,据 Socket 称。它还会把自己的 .claude/settings.json 和 .vscode/tasks.json 写入其能够触及的代码库,这样下次开发者在 Claude Code 或 VS Code 中打开该项目时,代码就会再次运行。这是一次直指人们用于构建智能体的机器的供应链攻击。

发布版本是如何从维护者的账户中流出的

Socket、SafeDep 和 Aikido 均于10月8日各自发布了分析,它们对相关机制的描述相互一致。该记录在档案中的置信度等级为 A,未被标记为存在争议,且来源中未发现任何与三者相矛盾的内容。

SafeDep 的文章将八个提交追溯至该仓库的 main 分支,时间为 UTC 01:20 至 03:57,日期为 10 月 7 日,归因于一个仓库管理员账户,并且是通过 GitHub 网页界面完成的。存档指认为第一个恶意提交的是 41b38f0,这是 10 月 7 日 03:44:48 UTC 的一次包含四个文件的修订,其提交信息为 “Add files via upload”,并带有 GitHub 的 已验证 签名——GitHub 会为其网页编辑器中所做的提交签名,因此 “已验证” 记录的是编辑界面,而不是人。该提交没有触及任何文件,除了 typescript/lib/Math_Symbol.js 和 typescript/lib/setup.mjs,根据 GitHub API:有效载荷,别无其他。

SafeDep 对操作者的行为给出了一种解读,并将其标注为自身的推断:对 preinstall 钩子的第一次编辑破坏了该文件的 JSON 语法,十九分钟后出现了一次修复提交,SafeDep 称这与一个人在浏览器中编辑文件、而非脚本化流水线的行为相符。Socket、SafeDep、Aikido、StepSecurity 或 Tensorlake 发布的任何内容,都没有指明维护者账号被接管所采用的方法。

攻击者并不需要 npm 令牌。SafeDep 报告称,该账户手动启动了该项目的 publish_npm.yaml 工作流,时间为 10 月 8 日 00:08 UTC;构建日志显示,已发布的 tarball 中包含有效载荷文件。此次发布使用了 npm trusted publishing,并且同级 tensorlake-native-* 包在同一次运行中发布,携带有效的 SLSA 来源证明——对于一次被投毒源代码的构建而言,这份来源证明确实有效。该发布在工作流启动十二分钟后上线。

存档记录显示,该代码仓库在发布触发之前已被入侵大约20 小时——下方的时间线以逐次提交的方式梳理出了这段空档——这与 Tensorlake 自己的说法一致:攻击者是"通过我们自己的发布工作流"完成发布的。

载荷一旦运行后做了什么

A screenshot of a card titled 'A credential stealer with an AI-tooling list', kicked 'tensorlake@0.5.144 · what the payload collected', subtitled 'Per Socket, SafeDep and Tensorlake's advisory — grouped by what each collector reached for'. Three columns of paired labels sit side by side. 'Publishing and source control' lists npm tokens from .npmrc, the token API and the OIDC exchange; GitHub tokens with a repository and workflow permission check; GitHub Actions secrets read from Runner.Worker memory; and cryptocurrency browser wallets across 14 extensions. 'AI agent tooling' lists ~/.claude.json for Claude Code settings; ~/.kiro/settings/mcp.json for Kiro MCP config; .cursor, Windsurf and Zed config and MCP files; and .claude/settings.json and .vscode/tasks.json written back into reachable repositories. 'Cloud, cluster and endpoint' lists AWS via STS, SSM Parameter Store and Secrets Manager; Kubernetes service-account tokens and kubeconfigs; HashiCorp Vault at 127.0.0.1:8200; SSH and GPG keys, .env files and Docker and CLI files; the Wallets row, wallet.dat, Exodus, Electrum and MetaMask; and saved passwords via a downloaded HackBrowserData binary. A footer reads 'Collector list per Socket's initial assessment, SafeDep's decoded payload analysis and the Tensorlake advisory (GHSA-8g63-53c9-65j2). Socket flags AI development tooling for Claude Code, Cursor, Kiro, Windsurf and Zed by name.'

Socket 的评估指出,该程序会从本地文件、CI 环境、Kubernetes 和 Vault 数据源中进行收集,此外还包括数据外泄、持久化以及执行远程提供的代码。SafeDep 复现了解码后的载荷:lib/setup.mjs从官方 GitHub 发布版本下载 Bun 1.3.13,并用它来运行该蠕虫,而它会在 CI 运行器上退出——目标是开发者机器,而非构建代理。SafeDep 还发现该载荷携带了同一个加载器的明文副本,并会将其植入其他软件包和代码仓库。

该载荷运行了三层混淆,SafeDep 在未执行样本的情况下就将其解码:一个混淆器字符串数组、一个使用盐值 svksjrhjkcejg 的自定义密码——与早前 keyv 遭入侵事件中报告的那一个完全相同——以及覆盖十一个内嵌文件(包括脚本、钩子和两个 RSA 公钥)的 AES-256-GCM 加密。其第一行带有构建标签 WORMTAG='tensrlake',该标签存在拼写错误,SafeDep 正是借此将此构建与正在进行的重复感染区分开来。

除了 AI 工具配置之外,收集列表既广泛又具体:~/.claude.json 和 ~/.kiro/settings/mcp.json 用于智能体设置;SSH 密钥、云 CLI 文件、.npmrc、.env、Docker 和 Kubernetes 文件以及 shell 历史记录;wallet.dat 以及 Exodus、Electrum 和 Ledger Live 数据,还有 MetaMask 和 Phantom 等浏览器钱包扩展;AWS STS、SSM Parameter Store 和 Secrets Manager;从 Runner.Worker 进程内存中读取的 GitHub Actions 密钥;以及 Kubernetes 服务账户令牌和 Vault 令牌。它从 iseekaigogo[.]com/hbd/<platform> 获取了一个按平台区分的二进制文件,并使用参数 "dump -b all -c password -f json" 运行它——这些参数与 HackBrowserData 工具匹配,不过 SafeDep 指出该二进制文件本身并未被恢复,并将此识别标记为推断。SafeDep 补充说,该蠕虫在俄语区域设置下会退出。

A screenshot of a card titled 'Persistence, resolution, and the wipe trigger', kicked 'tensorlake@0.5.144 · staying power', subtitled 'The release was removed in under two hours; the implant it installed was built to outlive that.' Three columns sit side by side. 'Persistence: gh-token-monitor' lists a systemd user service on Linux, a LaunchAgent com.user.gh-token-monitor on macOS, a scheduled task with a PowerShell monitor on Windows, and files left behind in ~/.local/bin, ~/.config and %LOCALAPPDATA%. 'Endpoint resolution order' lists, in numbered order, the primary endpoint iseekaigogo[.]com:443/router; an Ethereum contract 0xb614155Fd88...1Df091B9; the public RPC endpoints that contract is read through, given as ~30 by Socket and 35 by SafeDep; signed GitHub commits found by the string thebeautifulmarchoftime; and public dead-drop repositories described as 'Shai-Hulud: Here We Go Again'. 'The dead-man switch' lists a poll of api.github.com/user every 60s, for 24 hours, firing on a 40x response to that token, with the effect rm -rf ~/ (advisory: home dir), armed only if the account has no orgs, and the order that matters: remove monitor, then revoke. A footer reads 'Per Socket's blog and SafeDep's analysis, with the C2 endpoint, the contract and the gh-token-monitor service confirmed in Tensorlake's advisory. Socket and SafeDep give different counts of the public RPC endpoints the contract is read through.'

持久化是刻意为之。Socket 在谈及该载荷时写道:“这种组合将风险扩展到单个被盗 API 密钥之外,”并补充说,“持久化可以在受影响的依赖项被移除后仍保留攻击者的访问权限。”SafeDep 报告称,该蠕虫提交 Claude Code 和 VS Code 钩子文件,其 git 作者为 claude,并植入一个“Run Copilot”工作流,该工作流会转储所有仓库机密,然后删除该次运行和分支。Tensorlake 的公告提醒检查日志的开发者查找一个分支,其名称为 dependabot/github_actions/format/setup-formatter,以及由同一身份创作的提交。

Spread 靠窃取的令牌运行,而不是靠第二个漏洞。Socket 表示,该蠕虫会枚举与受害者发布身份相关联的软件包,构建 Sigstore 来源证明,并重新发布被投毒的版本。SafeDep 补充了一个细节:在 CI 内部,它会添加一个 git 依赖,并用 Fulcio 和 Rekor 对其输出签名——因此它接下来发布的软件包上的来源证明,与 0.5.144 上的来源证明一样有效。

发布前的二十小时

第一个恶意提交与发布之间的时间间隔,是时间线中解释一个被投毒的软件包如何获得有效来源证明的那一部分。所有时间均为 UTC;提交时间来自 SafeDep 和 GitHub API,而发布、移除和公告时间来自 Tensorlake。

A screenshot of a card titled 'Compromised source in the repo, then one manual publish', kicked 'tensorlake@0.5.144 · the ~20-hour window', subtitled 'All times UTC. Commit times per SafeDep's timeline and the GitHub API; publish and removal times per the Tensorlake advisory.' Three columns sit side by side. '7 October 2026 — source, main branch' lists 01:20 setup.mjs and Math_Symbol.js added; 01:24 the preinstall script added; 01:43 package.json JSON repaired; 02:13 to 03:44 four loader and payload revisions; 03:44 commit 41b38f0, the payload commit; 03:57 the version set to 0.5.144. '8 October 2026 — release and removal' lists 00:08 publish_npm.yaml started by hand; 01:12:07 version 0.5.144 published to npm; 01:22:02 issue #1014 opened by StepSecurity; 01:23:10 flagged by Socket, about eleven minutes in; 02:54 the version removed, after roughly 1h 40m live; 20:24 version 0.5.145 published next. '9 October 2026 — disclosure closed' lists advisory GHSA-8g63-53c9-65j2; pull request #1016, source reverted and 0.5.145 cut; issue #1014 closed as completed; 0.5.143 as the last version stated unaffected; and no usage seen, per Tensorlake's user-agent telemetry. A footer reads 'The ~20-hour gap between the first rogue commit and the publish is SafeDep's commit timeline, and matches the archive's own upstream record.'

纵观整条时间线:载荷文件于10月7日进入仓库的主分支,当天凌晨03:57版本被设为0.5.144,而直到次日00:08,才有一个账号手动启动了发布工作流。被污染的源代码在该分支中滞留了约二十个小时,随后项目自身的流水线才将其构建。这就是针对发布路径的攻击的全部——没有伪造的证明,没有重新发布的压缩包。溯源信息准确无误地记录了一次对本不该出现在该分支中的代码的构建。

失能开关

这次事件中,此前的 npm 入侵事件里没有先例的那一部分,是受害者在做出正确应对之后所发生的事情。gh-token-monitor:一项会轮询 api.github.com/user 的服务,它每 60 秒用那个被盗的 token 轮询一次,持续 24 小时。当 GitHub 返回 40x 时——也就是 token 被吊销的那一刻它就会返回的那样——该监控程序便会运行攻击者提供的处理程序,并删除用户的主目录。

Socket 将同一组件描述为使用一个通过 ONLOGON计划任务持久化的 PowerShell 监视器,并通过 Invoke-Expression执行攻击者提供的处理程序,并指出代码包含字面字符串“IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner”,该字符串曾出现在早期的 Shai-Hulud 攻击波中。SafeDep 将该处理程序命名为 rm -rf ~/。Socket 的措辞明确指出,无论撤销发生在哪里,这都会触发;监视器是本地的,而撤销不是。四个来源——Socket、SafeDep、StepSecurity 和 Tensorlake 自己的公告——给出相同的操作顺序:先移除监视器,再撤销。

什么是实有,什么只是推断出来的

档案对本条目记载没有已确认的危害,且来源对此予以支持,而不仅仅是没有与之相矛盾。

• Tensorlake 表示该版本未被使用。其公告称,该公司通过用户代理标头监控 SDK 的使用情况,并且“自该版本发布以来未发现任何使用”。这是厂商就其自身遥测数据所作的声明,而非经审计的统计数字。

• 下载量并非感染数量。Socket 报告该软件包的每周下载量约为12,000 次,并指出这反映的是该软件包的整体使用情况,而非恶意版本的下载量;SafeDep 则引用同一软件包的月下载量约为106,000 次。两者都不能证明 0.5.144 在任何地方运行过。Socket 标记的发布窗口与 Tensorlake 的两小时窗口相吻合,说明该版本在被传播之前就已被移除。

• 载荷自身的缺陷可能限制了它。SafeDep 发现了两处:npm 传播路径需要磁盘上存在 ./dist/Math_Symbol.js,否则就会停止;而植入的加载器寻找的是 ai_init.js,但蠕虫写入的却是 math_init.js。SafeDep 将这些发现标记为对传播的限制,而文章将其视为某一份分析对代码的解读,而非保证没有任何东西传播出去。

• 该来源证明是真实的。这并非有争议的说法:npm 来源证明记录了软件包的构建位置以及由哪个工作流构建,而在此例中,它记录的正是事实——项目自己的发布工作流,构建了被投毒的源码。Tensorlake 的安全公告原话就是这样说的:“该软件包带有有效的 npm 来源证明”。Socket 在指出来源证明“说明的是软件包在哪里构建,而不是代码是否安全”时,也把同样的观点讲得很明白。

流传中的若干细节属于单一分析来源的说法,应据此理解。攻击者获取维护者账户的具体机制,调查此事的各家公司和 Tensorlake 均未公布。以太坊合约地址及其在解析端点中的作用出现在 SafeDep 和 Socket 的叙述中——Socket 称该载荷通过一个以太坊合约解析其端点,使用约 30 个公共 RPC 端点并带有 GitHub 回退,而 SafeDep 将 RPC 数量记为 35,并报告称 10 月 8 日该合约返回的是 iseekaigogo.com——Tensorlake 在其公告中确认了该端点和该合约。两份报告中的确切 RPC 数量不同,但设计并无差异。

公开记录中有一处空白值得直言不讳地说明。本文所查阅的所有来源,均未报告任何经确认的下游感染——即另一个软件包或代码仓库被该蠕虫利用从本案中窃取的凭据所污染。传播代码确实存在,SafeDep 也已读过;至于它是否制造出第二个受害者,目前尚不得而知。

后来发生了什么变化?

响应比事件本身更快、更协调。

• 该版本已不复存在。 据 Tensorlake 称,它于 10 月 8 日 01:12 至 02:54(UTC)期间处于上线状态;SafeDep 报告称,npm 已将其移除。10 月 10 日,本文作者直接查询 npm 注册表时,0.5.144 已不再出现在该软件包的已发布版本中,而该软件包的最新标签为 0.5.148。Tensorlake 的公告建议开发者使用 0.5.143 作为最后一个未受影响的版本。

• 该安全公告已发布,并附有修复方案。Tensorlake 的 GHSA-8g63-53c9-65j2 安全公告称,PyPI 和 crates.io 上的 tensorlake 软件包以及 tensorlake CLI 并未受到影响——只有 npm 软件包受到影响——并且 0.5.143 及更早版本不受影响。SafeDep 报告称,维护者在拉取请求 #1016 中还原了源代码,并发布了 0.5.145。

• 披露帖异常完整。StepSecurity 的研究员 Ashish Kurmi 于#1014 在 10 月 8 日 01:22:02 UTC 上开了一个 GitHub issue——大约发布于十分钟后——指向 StepSecurity 对其所称的“人质令牌”蠕虫的分析文章。Aikido 的 Oliver Smith 评论称,该载荷是在提交 41b38f0 中引入的,并将其识别为一个新的 Shai-Hulud 信息窃取程序。维护者 Lorenzo——npm 包上标注的维护者身份为 calavera——于 10 月 9 日回复,确认了该安全问题,并于当天以已完成状态关闭了该 issue。该 issue 的标题本身就体现了证据:它最初是一份“可能恶意”的报告,到关闭时已被改为将该载荷定性为恶意,且毫无保留。

• 本次事件所属的攻击活动规模远不止这一个软件包。Socket、SafeDep 和 Aikido 均将 Tensorlake 的这次发布归入最早于 2026 年 8 月初被记录的 ChainDrop / Mini Shai-Hulud 攻击浪潮,其 npm 受害软件包包括 keyv、cacheable 及相关软件包。Tensorlake 的安全公告也持相同说法,并援引了 Socket、SafeDep 和 Aikido。

Socket 对任何安装了该版本的人的建议是,应将主机视为已遭入侵:检查该版本的锁定文件、清单文件和构建日志;移除 gh-token-monitor服务在撤销任何令牌之前;然后从干净机器轮换凭证。Tensorlake 的公告补充了同样的顺序,并建议检查暴露的令牌可能写入的包注册表和仓库,看是否有意外发布。Aikido 的文章建议将 ignore-scripts=true在.npmrc 中设置,以阻止安装钩子,并将运行过该钩子的任何机器视为已完全遭入侵。

这是一条记录,由他人报告,此处引用并归之于他们。其他有记录的智能体事件,各自带有来源、严重程度、置信等级和争议标记,收录在AI 事件档案库中——因此,针对智能体工具链的供应链攻击,可以与其余真实发生过的事件并列对照阅读。