概述
| 项目 | 内容 |
|---|---|
| CVE ID | CVE-2026-45321 |
| GHSA | GHSA-g7cv-rxg3-hmpx |
| 披露时间 | 2026年5月11日 19:20-19:26 UTC |
| 攻击手法 | GitHub Actions OIDC 信任publisher绑定 + pull_request_target 错误配置 + 运行时内存提取 |
| 影响包数量 | 42个 @tanstack/* 包,共84个恶意版本 |
| 恶意代码 | 约2.3MB混淆的 router_init.js |
| 窃取目标 | 云凭据、SSH密钥、GitHub Token、npm Token |
攻击时间线
| 时间 | 事件 |
|---|---|
| 2026-05-11 19:20 UTC | 攻击者开始向42个 @tanstack/* 包推送恶意版本 |
| 2026-05-11 19:26 UTC | 推送完成,共84个恶意版本上线 |
| 2026-05-11 晚 | 安全社区发现并报告 |
| 2026-05-11 夜间 | npm 紧急下架,TanStack 发布修复版本 |
攻击链分析
这次攻击不是简单的"攻击者拿到了 npm token",而是一个精心设计的三重漏洞链:
第一重:pull_request_target “Pwn Request” 错误配置
GitHub Actions 的 pull_request_target 触发器会在来自 fork 的 PR 中执行工作流,但这个工作流运行在基础仓库(target仓库)的上下文中,具有写权限。
正常配置应该是只信任已审核的 PR,但很多项目错误地配置为:pull_request_target 触发后,直接在有权限的上下文中执行来自 PR 的代码。
第二重:GitHub Actions 缓存投毒
攻击者利用 fork 与 base 仓库之间的信任边界,通过向合法仓库提交 PR 的方式,将恶意代码注入 Actions 缓存。当有权限的 CI 环境拉取缓存时,恶意代码随之执行。
第三重:运行时 OIDC Token 内存提取
GitHub Actions 的 OIDC 信任publisher机制允许从特定环境自动发布 npm 包。攻击者不是去偷 token,而是在运行时从 Actions runner 进程的内存中直接提取 OIDC token,从而以 TanStack 的合法身份发布恶意包。
整个过程不需要修改 TanStack 官方的发布 workflow——攻击者只是利用了发布流程本身的运行时行为。
恶意代码行为
安装受影响版本时,恶意 router_init.js(约2.3MB混淆代码)会执行以下操作:
1. 云凭据窃取
| 目标 | 路径/接口 |
|---|---|
| AWS | IMDS 元数据服务 + Secrets Manager |
| GCP | 元数据服务 |
| Kubernetes | Service Account Token |
| HashiCorp Vault | Token 文件 |
| SSH 私钥 | ~/.ssh/ 目录 |
| GitHub Token | 环境变量、gh CLI 配置、.git-credentials |
| npm Token | ~/.npmrc |
2. 数据外传
窃取的数据通过 Session/Oxen 信使文件上传网络 传输到以下地址:
filev2.getsession.org
seed1.getsession.org
seed2.getsession.org
seed3.getsession.org
传输使用端到端加密,无攻击者控制的 C2 服务器,按 IP 或域名封禁是唯一的网络层防御手段。
3. 横向传播
恶意代码还会:
- 枚举受害者在 npm 上维护的包
- 用相同的恶意代码重新发布这些包
- 形成自我传播的供应链污染
受影响包列表(部分)
| 包名 | 受影响版本 | 修复版本 |
|---|---|---|
| @tanstack/react-router | 1.169.5 – 1.169.8 | 1.169.9 |
| @tanstack/router-core | 1.169.5 – 1.169.8 | 1.169.9 |
| @tanstack/solid-router | 1.169.5 – 1.169.8 | 1.169.9 |
| @tanstack/vue-router | 1.169.5 – 1.169.8 | 1.169.9 |
| @tanstack/react-start | 1.167.68 – 1.167.71 | 1.167.72 |
| @tanstack/solid-start | 1.167.65 – 1.167.68 | 1.167.69 |
| @tanstack/router-generator | 1.166.45 – 1.166.48 | 1.166.49 |
所有42个受影响包的列表见 GitHub Advisory:https://github.com/advisories/GHSA-g7cv-rxg3-hmpx
如何检测是否中招
1. 检查已安装的包版本
# 检查是否安装了受影响版本
npm ls @tanstack/react-router
npm ls @tanstack/router-core
# ... 其他包
# 查看完整依赖树
npm ls --all | grep "@tanstack"
2. 检查 package-lock.json
恶意版本的 package-lock.json 中包含以下特征:
"optionalDependencies": {
"@tanstack/setup": "github:tanstack/setup"
}
如果看到这个条目,说明中招了。
3. 检查凭据访问时间
如果在 2026-05-11 19:20-19:26 UTC 之间执行过 npm install、pnpm install 或 yarn install,且安装了受影响版本,必须立即轮换所有凭据。
应急响应
立即行动
-
停止使用受影响版本
# 升级所有 @tanstack/* 包 npm update "@tanstack/*" -
轮换所有凭据
- AWS/GCP 云凭据
- GitHub Token(个人 Access Token、C.I. Token)
- npm Token
- SSH 私钥
-
检查云审计日志 查看5月11日-12日期间是否有异常云 API 调用
-
撤销并重新生成所有访问密钥
网络层防御
由于 C2 使用了 Session/Oxen 网络,无法通过传统域名封禁阻断:
# 防火墙规则示例(Linux)
iptables -A OUTPUT -d getsession.org -j REJECT
iptables -A OUTPUT -d filev2.getsession.org -j REJECT
检测工具
# GitHub 上有现成的检测脚本
git clone https://github.com/Caixa-git/tanstack-shield
cd tanstack-shield
npm install
npm run scan
防御建议
对开发者
- 不要盲目
npm install— 使用npm ci或锁定版本 - 检查包版本的发布时间 — 5月11日前后突然发布的大版本要警惕
- 使用 package-lock.json — 确保依赖版本固定
- 审查 GitHub Actions 权限 — 最小权限原则
- 不要在 Actions 中直接执行来自 PR 的代码
对企业
- npm audit — 定期扫描依赖漏洞
- 私有 npm 镜像 — 预审核后才放行
- 网络隔离 — CI 环境无法访问外部未知服务
- 凭据轮换策略 — 定期自动轮换,不依赖人工
思维延伸
这次攻击最值得思考的一点:攻击者没有偷任何人的密码,也没有拿下任何服务器。
他们只是利用了 GitHub Actions 生态中三个已知漏洞类的组合:
pull_request_target的错误信任- fork/base 之间的缓存边界
- OIDC token 运行时内存可提取
这意味着即使用最小的权限、最干净的环境,只要用了有漏洞的 Actions 配置,就可能成为受害者。
供应链安全不只是"保护好我的 token",更是"理解我依赖的每一个工具链的信任模型"。
参考
- GitHub Advisory:https://github.com/advisories/GHSA-g7cv-rxg3-hmpx
- TanStack 官方:https://tanstack.com/
- 检测工具:https://github.com/Caixa-git/tanstack-shield
- The Hacker News 报道:https://thehackernews.com