
Análisis de causa raíz independiente y prueba de concepto para la inyección SQL no autenticada a RCE en WordPress (CVE-2026-63030 + CVE-2026-60137), con laboratorio Docker y documentación detallada de la cadena de explotación.
WordPress core divulgó wp2shell el 2026-07-17 como un RCE crítico no autenticado que afecta a las instalaciones por defecto (WordPress 6.9.0-6.9.4, 7.0.0-7.0.1, sin necesidad de plugins). El descubridor original (Searchlight Cyber) retuvo los detalles técnicos en el momento de la divulgación. Este repositorio documenta un análisis independiente de causa raíz derivado enteramente del diff del código fuente de WordPress core entre las versiones vulnerable (6.9.4) y parcheada (6.9.5), además de la verificación en vivo en un laboratorio local.
Crédito: la reproducción aquí sigue la forma de la petición de sergiointel/wp2shell-poc, confirmada byte a byte y contrastada con el diff real del parche.
root_cause_analysis.md - informe completo: los dos fallos encadenados, el
código vulnerable exacto, el diff del parche, la cadena de explotación y la
verificación en vivo antes/después contra una instancia real de WP 6.9.4.poc_upstream.py - copia sin modificar del PoC original de sergiointel (solo SQLi).poc_upstream_rce.py - el PoC de sergiointel actualizado unas 22 horas
después de la divulgación, que añade escalada de privilegios no autenticada
y RCE sin descifrar ninguna credencial. Véase la sección 7 de
.root_cause_analysis.mdpoc_extract.py / poc_extract2.py - una variante ajustada (SLEEP() más
grande, umbral de temporización más alto, límite máximo de búsqueda de
caracteres más largo) necesaria para obtener una señal fiable en un
laboratorio Dockerizado/con proxy donde el SLEEP(0.15) por defecto del
script original se perdía en la fluctuación de la red.docker-compose.yml - levanta el mismo laboratorio WordPress 6.9.4 + MariaDB
usado para la verificación.payload.json, response.json, response_patched.json - una petición
hecha a mano que replica la estructura del PoC y las respuestas crudas del
servidor, antes y después de aplicar los archivos del parche 6.9.5 en la
instalación.Una segunda PoC escrita de forma independiente (github.com/Icex0/wp2shell-poc)
fue revisada por completo y describe el mismo mecanismo de causa raíz de forma
independiente, corroborando el análisis siguiente. Usando su extracción SQLi
ciega basada en contenido (booleana, no basada en temporización), el hash de
credenciales del administrador de este laboratorio se recuperó con un 100% de
precisión y, con esa credencial conocida, se demostró en vivo una cadena
completa de SQLi a RCE: inyección SQL, extracción de credenciales, subida de
webshell mediante plugin autenticado y ejecución de código como www-data.
Véase la sección 6 de root_cause_analysis.md.
Ese camino de descifrado de credenciales es real, pero no es el único. El PoC
de sergiointel se actualizó posteriormente para lograr RCE no autenticado sin
descifrar credencial alguna, usando UNION SELECT para forjar filas falsas de
base de datos que el propio código de Customizer/nav-menu/oEmbed-cache de
WordPress confía lo suficiente como para permitir que una petición no
autenticada de creación de usuario tenga éxito. Véase la sección 7 de
root_cause_analysis.md. La inyección SQL no autenticada por sí sola es
suficiente para un RCE completo en una instalación por defecto.
CVE-2026-63030 (confusión de rutas en lotes REST, CWE-436):
WP_REST_Server::serve_batch_request_v1() añade a un array $matches[]
mediante un simple push, pero omite la inserción cuando la ruta de una
sub-petición no se puede parsear (p. ej., una entrada deliberadamente
malformada "http://:"). Esa única omisión desincroniza $matches[] respecto
a $requests[] en un índice por cada entrada siguiente. En el momento del
despacho, $matches[$i] ya no se corresponde con $requests[$i], por lo que
el código termina ejecutando los datos de una petición bajo el manejador de
una ruta diferente de aquella contra la que realmente se validó.
CVE-2026-60137 (inyección SQL): WP_Query::get_posts() solo aplicaba la
sanitización absint() sobre author__not_in dentro de una rama
is_array(). Un valor escalar de tipo cadena omitía la sanitización por
completo y se concatenaba directamente en ... post_author NOT IN ($value).
Encadenados: el fallo de confusión de rutas permite a un atacante conseguir
que una petición declarada contra /wp/v2/categories (que no reconoce
author_exclude, por lo que nunca se sanitiza) se despache realmente a través
del controlador de posts (que sí lee author_exclude y lo reenvía a
WP_Query). No se requiere autenticación.
Sobre la afirmación del RCE: se confirmó en vivo que la inyección SQL ciega
no autenticada funciona y puede exfiltrar contenido arbitrario de la base de
datos, incluido wp_users.user_pass. Una sola pasada de extracción basada en
temporización en un laboratorio virtualizado presentaba un ruido de errores de
bit significativo (~94% de precisión por carácter en las pruebas de aquí); un
oráculo basado en contenido (booleano) no compartía ese modo de fallo y
recuperó el mismo campo con un 100% de precisión. Se confirmó RCE mediante dos
vías distintas: descifrando un hash de contraseña recuperado (condicionado a
la fortaleza de la contraseña) y una cadena de gadgets de escalada de
privilegios no autenticada que usa filas de base de datos forjadas con UNION
que el propio código de Customizer/nav-menu/oEmbed-cache de WordPress confía
(incondicional, sin necesidad de comprometer credencial alguna). Véase la
sección 7 de root_cause_analysis.md.
docker compose up -d
# wait for WordPress install wizard to be reachable on :8890, then complete setup
HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 \
python3 poc_extract2.py http://localhost:8890 "SELECT DATABASE()"
Consulte root_cause_analysis.md para el informe técnico completo.
WordPress ha publicado parches oficiales: 6.9.5 y 7.0.2 (7.1 Beta 2 para la rama beta), lanzados el 2026-07-17. Actualice de inmediato si ejecuta una versión afectada. Este repositorio se publica con fines defensivos y educativos después de que el parche ya fuera público. Ejecute cualquiera de los códigos aquí únicamente contra sistemas que le pertenezcan o sobre los que tenga autorización explícita para realizar pruebas.