SnailSploit/CVE-2026-32885
GitHub: SnailSploit/CVE-2026-32885
一份针对 ddev 存档解压功能的 ZipSlip 路径遍历漏洞「CVE-2026-32885」的详细安全公告与技术分析文档。
Stars: 0 | Forks: 0
# CVE-2026-32885:ddev/ddev 存档解压中的 ZipSlip 路径遍历漏洞 (CVSS 6.5 中危)
[](https://github.com/ddev/ddev/security/advisories/GHSA-x2xq-qhjf-5mvg)
[](https://www.cve.org/CVERecord?id=CVE-2026-32885)
[](https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N)
[](https://pkg.go.dev/github.com/ddev/ddev)
[](https://cwe.mitre.org/data/definitions/22.html)
**关键词:** ZipSlip, path traversal, archive extraction, CWE-22, ddev, Go, tar, zip
## 目录
- [概述](#overview)
- [漏洞详情](#vulnerability-details)
- [技术分析](#technical-analysis)
- [攻击链](#attack-chain)
- [影响](#impact)
- [修复建议](#remediation)
- [CVSS 指标](#cvss-metrics)
- [时间线](#timeline)
- [参考](#references)
- [联系方式](#contact)
- [免责声明](#disclaimer)
## 概述
[ddev/ddev](https://github.com/ddev/ddev)(3K+ stars)中存在一个 ZipSlip 路径遍历漏洞,这是一个流行的开源本地开发工具,适用于 PHP、Python 和 Node.js 项目。`pkg/archive/archive.go` 中的 `Untar()` 和 `Unzip()` 函数均使用 `filepath.Join(dest, file.Name)` 而未进行任何路径包含验证,这使得精心构造的存档可以在开发人员机器上的任意位置写入文件。
ddev 会从远程源下载并解压存档,用于插件安装、数据库导入 (`ddev import-db`) 和文件导入 (`ddev import-files`),这使其成为通过供应链或社会工程学场景进行攻击的现实攻击向量。
## 漏洞详情
| 字段 | 值 |
|-------|-------|
| **Advisory** | [GHSA-x2xq-qhjf-5mvg](https://github.com/ddev/ddev/security/advisories/GHSA-x2xq-qhjf-5mvg) |
| **CVE** | [CVE-2026-32885](https://www.cve.org/CVERecord?id=CVE-2026-32885) |
| **CWE** | [CWE-22: Improper Limitation of a Pathname to a Restricted Directory](https://cwe.mitre.org/data/definitions/22.html) |
| **CVSS Score** | 6.5 (中危) |
| **Package** | github.com/ddev/ddev (Go) |
| **Affected Versions** | <= latest (所有版本) |
| **Patched Version** | None (修复进行中) |
| **Component** | `pkg/archive/archive.go` |
## 技术分析
### 漏洞代码
`pkg/archive/archive.go` 中的 `Untar()` 和 `Unzip()` 函数均通过直接将目标目录与存档条目名称拼接来构建输出路径,未进行任何清洗或包含检查。
**`pkg/archive/archive.go:235` (Untar):**
```
fullPath := filepath.Join(dest, file.Name) // NO SANITIZATION
```
**`pkg/archive/archive.go:342` (Unzip):**
```
fullPath := filepath.Join(dest, file.Name) // NO SANITIZATION
```
这两个函数随后都通过 `os.MkdirAll` 创建目录,并通过 `os.Create` 使用未经清洗的路径创建文件。
### 核心问题
Go 的 `filepath.Join()` 会解析拼接路径中的 `../` 序列。当存档条目的名称类似于 `../../../tmp/malicious` 时,生成的 `fullPath` 会脱离预期的目标目录:
```
// Example:
// dest = "/safe/extract/dir"
// file.Name = "../../../tmp/malicious"
// filepath.Join(dest, file.Name) = "/tmp/malicious" // ESCAPED!
```
### 安全模式(缺失)
修复需要在 `filepath.Join` 之后进行路径包含检查:
```
fullPath := filepath.Join(dest, file.Name)
if !strings.HasPrefix(filepath.Clean(fullPath), filepath.Clean(dest) + string(os.PathSeparator)) {
return fmt.Errorf("entry %q escapes destination directory", file.Name)
}
```
## 攻击链
```
+----------------------------------------------------------+
| 1. CRAFT MALICIOUS ARCHIVE |
| Tar/zip with ../../../ path traversal entries |
+---------------------------+------------------------------+
|
v
+----------------------------------------------------------+
| 2. DELIVER TO DEVELOPER |
| - Malicious ddev add-on |
| - Compromised database dump (ddev import-db) |
| - Trojan file archive (ddev import-files) |
+---------------------------+------------------------------+
|
v
+----------------------------------------------------------+
| 3. DDEV EXTRACTS ARCHIVE |
| Untar()/Unzip() calls filepath.Join(dest, file.Name) |
| No path containment check |
+---------------------------+------------------------------+
|
v
+----------------------------------------------------------+
| 4. ARBITRARY FILE WRITE |
| Files written outside extraction directory |
| Overwrite configs, inject code, plant backdoors |
+----------------------------------------------------------+
```
## 影响
| 方面 | 描述 |
|--------|-------------|
| **Direct Impact** | 开发人员机器上的任意文件写入 |
| **Attack Surface** | ddev add-ons, `ddev import-db`, `ddev import-files` |
| **Supply Chain Risk** | 恶意插件可覆盖项目文件或系统配置 |
| **Social Engineering** | 来自受损环境/同事/客户的数据库转储 |
| **Affected Users** | 3K+ GitHub stars,广泛用于 PHP/Drupal/WordPress 开发 |
维护者 (rfay) 确认风险不仅限于插件安装,还包括 `ddev import-db`(用户提供的数据库存档)和 `ddev import-files`(用户提供的文件存档),在这些情况下,开发人员没有理由怀疑内容是恶意的。
## 修复建议
**对于 ddev 维护者:**
- 在 `Untar()` 和 `Unzip()` 函数中的 `filepath.Join` 之后添加路径包含检查
- 验证 `filepath.Clean(fullPath)` 是否以 `filepath.Clean(dest)` + 路径分隔符为前缀
- 对于 tar 存档中的符号链接:解析链接目标并应用相同的包含检查
- 添加测试覆盖文件条目中的 `../` 遍历、符号链接目标以及绝对符号链接目标
**对于 ddev 用户:**
- 仅使用来自可信来源的插件
- 仅导入来源经过验证的数据库转储和文件存档
- 监控运行导入命令后是否出现意外的文件更改
## CVSS 指标
**Vector:** `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N`
| Metric | Value | Rationale |
|--------|-------|-----------|
| Attack Vector | Network | 恶意存档远程投递 |
| Attack Complexity | Low | 标准的 ZipSlip payload 构造 |
| Privileges Required | None | 无需认证 |
| User Interaction | Required | 开发人员必须解压存档 |
| Scope | Unchanged | 影响仅限于开发人员机器 |
| Confidentiality | None | 仅具备写入原语 |
| Integrity | High | 文件系统任意位置的任意文件写入 |
| Availability | None | 无拒绝服务 |
## 时间线
| Date | Event |
|------|-------|
| 2026-03-10 | 通过 GitHub PVRT 报告漏洞 |
| 2026-03-10 | 维护者 (rfay) 确认并承认漏洞 |
| 2026-03-15 | 由 stasadev 请求 CVE |
| 2026-03-17 | 由 GitHub 分配 CVE-2026-32885 |
## 参考
- [GHSA-x2xq-qhjf-5mvg](https://github.com/ddev/ddev/security/advisories/GHSA-x2xq-qhjf-5mvg)
- [CVE-2026-32885](https://www.cve.org/CVERecord?id=CVE-2026-32885)
- [CWE-22: Improper Limitation of a Pathname to a Restricted Directory](https://cwe.mitre.org/data/definitions/22.html)
- [Snyk ZipSlip 研究](https://security.snyk.io/research/zip-slip-vulnerability)
## 联系方式
- **Website:** [snailsploit.com](https://snailsploit.com)
- **GitHub:** [@SnailSploit](https://github.com/SnailSploit)
- **LinkedIn:** [/in/kaiaizen](https://linkedin.com/in/kaiaizen)
## 免责声明
本建议书是根据负责任披露原则,出于教育和防御目的而发布的。所提供的信息旨在帮助开发人员和安全团队理解和修复漏洞。请勿将此信息用于未经授权的测试或恶意目的。
标签:CVE-2026-32885, CVSS 6.5, CWE-22, ddev, DNS 解析, ESC8, Go语言安全, Tar文件处理, Web安全, ZipSlip, Zip文件处理, 中间人攻击, 任意文件写入, 供应链攻击, 归档提取, 文件包含漏洞, 日志审计, 本地开发环境, 社会工程学, 蓝队分析, 路径遍历, 输入验证缺失