TanStack npm 供应链攻击复盘:Pwn Request + 缓存投毒 + OIDC 内存提取的完整攻击链
2026 年 5 月 11 日 19:20–19:26 UTC,攻击者向 42 个 @tanstack/* npm 包发布 84 个恶意版本。攻击链组合利用了 GitHub Actions 的三个机制——pull_request_target Pwn Request、Actions 缓存投毒、以及从 runner 内存提取 OIDC token——在不窃取任何 npm token 的情况下完成了伪装发布。

恶意版本在约 20 分钟内被 StepSecurity 研究员 ashishkurmi 发现并公开报告。TanStack 已废弃全部受影响版本,npm 安全团队已介入移除 tarball。
影响范围
- 42 个包、84 个恶意版本(每个包两个版本,间隔约 6 分钟发布)
- 受影响:
@tanstack/router全系列子包 - 确认未受影响:
@tanstack/query*、@tanstack/table*、@tanstack/form*、@tanstack/virtual*、@tanstack/store、@tanstack/start(元包)
完整受影响包列表见 tracking issue #7383。
攻击链拆解
第一步:Pwn Request
2026-05-10 17:16 UTC,攻击者 fork TanStack/router 仓库,命名为 zblgg/configuration(刻意改名以规避 fork 列表搜索)。
次日 10:49 UTC,攻击者以 zblgg 身份向 TanStack/router 提交 PR #7378,标题"WIP: simplify history build"。
PR 中包含一个 3 万行的混淆 JS 文件 packages/history/vite_setup.mjs,提交者身份伪造为 [email protected]。
关键点:TanStack 的 bundle-size.yml 使用 pull_request_target 触发,PR 自动进入 CI 运行,无需维护者批准。而使用 pull_request 触发的 pr.yml 被首次贡献者审核机制正确拦截。
攻击者随后多次 force-push,每次都触发新的 pull_request_target 运行,反复执行恶意代码。
第二步:GitHub Actions 缓存投毒
恶意代码在 CI 运行期间向 pnpm store 目录写入污染数据。缓存 key 为 Linux-pnpm-store-${hashFiles('**/pnpm-lock.yaml')},与正式发布流程 release.yml 使用的完全一致。
当 benchmark-pr job 结束时,actions/cache@v5 的 post-step 将被污染的 pnpm store 保存到该 key。下一次 release.yml 在主分支运行时,Setup Tools 步骤会恢复这个被污染的缓存。
这是一个跨 fork↔base 信任边界的缓存投毒攻击。
第三步:OIDC Token 内存提取
release.yml 声明了 id-token: write(npm OIDC trusted publishing 需要)。当被污染的 pnpm store 在 runner 上恢复后,攻击者控制的二进制文件被调入执行:
- 通过
/proc/*/cmdline定位 GitHub Actions Runner.Worker 进程 - 读取
/proc/<pid>/maps和/proc/<pid>/mem导出 runner 内存 - 从内存中提取 OIDC token(runner 在
id-token: write设置时惰性生成) - 用该 token 直接 POST 到
registry.npmjs.org,绕过 workflow 的 Publish Packages 步骤
npm 收到的发布请求携带合法的 OIDC trusted-publisher binding(TanStack/router release.yml@refs/heads/main),因此通过了验证。但发布来自恶意代码在 test/cleanup 阶段的执行,而非正常的 Publish Packages 步骤。
恶意载荷分析
安装受影响版本后,npm install 解析恶意 optionalDependencies,从 fork 网络获取孤儿 payload commit,执行约 2.3MB 的混淆脚本 router_init.js。
该脚本具备三大能力:
凭据窃取: AWS IMDS / Secrets Manager、GCP metadata、Kubernetes service-account token、HashiCorp Vault token、~/.npmrc、GitHub token(环境变量、gh CLI、.git-credentials)、SSH 私钥。
加密外泄: 通过 Session/Oxen messenger 文件上传网络(filev2.getsession.org、seed{1,2,3}.getsession.org)外泄。端到端加密、无攻击者控制的 C2 服务器,只能通过域名/IP 级别网络阻断防御。
自我繁殖: 枚举受害者在 npm 上维护的其他包(registry.npmjs.org/-/v1/search?text=maintainer:<user>),自动注入并重新发布恶意版本。一个开发者中招,可能波及整个维护链。
时间线
| 时间 (UTC) | 事件 |
|---|---|
| 05-10 17:16 | 攻击者创建 fork zblgg/configuration |
| 05-10 23:29 | 恶意 commit 提交(伪造 claude 身份) |
| 05-11 ~10:49 | PR #7378 提交,触发 pull_request_target CI |
| 05-11 11:01–11:11 | 多次 force-push,反复触发 CI |
| 05-11 19:20:39 | 第一批恶意版本发布(42 个包) |
| 05-11 19:26 | 第二批恶意版本发布 |
| 05-11 ~19:40 | StepSecurity 研究员发现并报告 |
| 05-11 当天 | 全部版本废弃,npm 安全团队介入 |
响应与加固
TanStack 在事件后采取了以下措施:
- 废弃全部 84 个恶意版本,联系 npm 安全团队移除 tarball
- 清除被污染的 GitHub Actions 缓存条目
- 合并加固 PR(#6773):重构
bundle-size.yml,添加 repository-owner guard,pin 第三方 action 引用 - 发布完整 postmortem,公开时间线和技术细节
GitHub Security Advisory:GHSA-g7cv-rxg3-hmpx
对开源生态的启示
OIDC trusted-publishing 作为 npm 与 GitHub 的信任桥梁,一旦 Actions 层被攻破,整个发布链的信任模型断裂。这次事件暴露了几个结构性问题:
pull_request_target的信任边界需要更严格的隔离——fork 代码不应拥有写入 base 仓库缓存的权限- Actions 缓存跨 fork↔base 的共享机制是一个系统性风险点
- Runner 进程内存中 OIDC token 的安全性依赖 Linux
/proc权限模型,容器化环境并非天然安全 - 对
pull_request_target的防护和第三方 action 的 pin 策略,已成为开源项目 CI 加固的基本要求
据 Socket 追踪,这起事件属于 "Mini Shai-Hulud" 系列活动的一部分,已有多个 npm 和 PyPI 包受害。
自查建议: 如果你在 2026-05-11 当天安装过 @tanstack/router* 相关包,应将安装主机视为可能被入侵,轮换 AWS、GCP、Kubernetes、Vault、GitHub、npm、SSH 等全部可达凭据。
来源:TanStack Blog · Socket Blog
- Hugging Face 最大开源仓库快被 AI 垃圾 PR 淹没了3/19/2026
- 苹果警告:iOS 13/14 用户需立即升级至 iOS 153/20/2026
- DarkSword 被披露:Safari 打开恶意网页即可被入侵,旧版 iPhone 该升级了3/19/2026
- 苹果锁定模式近四年零攻破记录3/28/2026
- 装个App等24小时:Google给Android侧载上了把锁3/20/2026
- GrapheneOS 警告起诉:安卓统一认证标准背后的生态之争3/18/2026
- Google公布Android侧载新规:安装未验证应用须等24小时3/20/2026
- GrapheneOS 拒绝把操作系统做成身份核验入口3/24/2026
- 白宫官方 App 逆向拆解:付费墙绕过、休眠定位追踪与供应链风险3/28/2026
- 有人在 GitHub 上开源了一个「前任 Skill」3/31/2026
- 通义开源影视级配音大模型 Fun-CineForge,关键变化是把“时间”也做进了模型3/16/2026
- Android 17 后量子加密落地:系统可信根的量子抗性升级3/26/2026
- OpenAI 开始给内部编码代理配“监工”,数千万轨迹里未见最高风险失调3/21/2026
- ImageGlass 10 Beta 1 发布:十五年来最大重构,首次支持 macOS 和 Linux3/15/2026
- Apifox 桌面端被曝遭供应链投毒,恶意脚本可窃取 SSH 密钥与 Git 凭证3/25/2026
- ICAO 紧急限制航班充电宝:每人最多 2 个,机上禁充3/28/2026
- Apifox 承认遭遇供应链攻击后,开发团队现在最该做的是全面轮替凭证3/26/2026
- Vercel 确认安全事件,暗网卖家声称掌握核心访问权限4/19/2026
- 谷歌推出 DBSC 技术:通过设备绑定强化 Cookie 安全防护4/11/2026
- LLM 供应链安全警钟:超 20% 免费 API 路由器存在恶意行为4/10/2026
- ShinyHunters 入侵 Rockstar Games,通过第三方工具窃取 Snowflake 数据4/14/2026
- 金山毒霸与 360 安全卫士内核驱动曝高危漏洞4/13/2026
- CPU-Z 官网遭黑客入侵,部分下载包被植入恶意代码4/10/2026
- 逆向 Claude Code 请求签名:cch 是怎么算出来的4/3/2026
- GitHub Issues 遭遇大规模垃圾广告攻击,开源社区成黑产引流池3/29/2026
- Claude 曝出「身份混淆」缺陷:AI 代理误认自身推理为用户指令,触发安全风险4/10/2026
- 法国政府承诺以 Linux 取代 Windows,覆盖 250 万公务员桌面4/10/2026
- 百度开源 8B 文生图模型 ERNIE-Image:文字渲染达 SOTA,消费级显卡即可运行4/15/2026
- PaddleOCR 超越 Tesseract,成为 GitHub 星标最高的 OCR 项目3/30/2026
- 开源项目分享:SwiftMTP——macOS 平台的安卓文件传输工具4/10/2026