
Detector no destructivo para CVE-2026-64638 (XSS2Shell) — primitiva de reflexión XSS pre-autenticación de WordPress
Detector no destructivo y sin dependencias para la primitiva de reflexión CVE-2026-64638 ("XSS2Shell") en wp-login.php de WordPress.
CVE-2026-64638 es un XSS reflejado preautenticado en el núcleo de WordPress, divulgado por PWN.AI el 2026-08-07 (CVSS 8.9, CWE-79) y corregido en 7.0.3 con backports a todas las ramas con soporte (6.9.6, 6.8.7, 6.7.6, 6.6.6, 6.5.9, …).
La causa raíz es una diferencia de análisis sintáctico entre strip_tags() de PHP y el sanitizador KSES de WordPress:
strip_tags() se niega a reconocer una etiqueta cuando hay espacios en blanco después de <, por lo que un nombre de usuario como < area id=x> sobrevive como texto plano.<area id="x"> vivo dentro del mensaje de error del inicio de sesión.Eso produce inyección de DOM elegida por el atacante con atributos id/class/href elegidos — la primitiva detrás de la cadena de múltiples etapas publicada (DOM clobbering → REST JSONP → robo de contraseñas de aplicación → subida de plugins → RCE contra una víctima administradora con sesión iniciada).
Dos sondas no destructivas (un inicio de sesión fallido con un nombre de usuario marcador aleatorio):
<b>marker</b> aparece sin escapar en la respuesta de error.< area id=marker> sobrevive a strip_tags() y reaparece como un elemento vivo/normalizado (p. ej. area id="marker") en la respuesta.Ningún payload ejecuta nada; la herramienta solo comprueba si el HTML del atacante se renderiza como DOM vivo. Extras opcionales:
--version — identifica la versión del núcleo mediante la etiqueta <generator> del feed.--jsonp — verifica que el envoltorio JSONP de REST (_jsonp/_envelope) usado por la cadena publicada es alcanzable.python3 xss2shell_check.py https://example.com
python3 xss2shell_check.py --jsonp --verbose https://example.com
python3 xss2shell_check.py --version https://example.com
python3 xss2shell_check.py --json https://example.com
Python 3.6+, solo biblioteca estándar. Sin dependencias.
| Código | Significado |
|---|---|
| 0 | VULNERABLE (primitiva de reflexión presente) |
| 1 | CORREGIDO / NO VULNERABLE |
| 2 | ERROR (inalcanzable, desafío WAF, respuesta inesperada) |
<area id="…">). El entrecomillado de atributos varía según la generación de KSES (antiguo analizador personalizado: <area id=x>; DOMDocument: <area id="x"> / <area id="x" />), por lo que se comparan todas las formas. Un paso directo de < area …> crudo sin re-análisis es texto inerte en el navegador, no la primitiva — se informa como RAW_TEXT_ONLY y no provoca un falso positivo.login_errors o una CDN/WAF reescriben las respuestas, o si el error de inicio de sesión nunca refleja el nombre de usuario.Solo pruebas de seguridad autorizadas. Esta herramienta está pensada exclusivamente para probar sistemas que usted posee o para los que tiene permiso explícito por escrito de evaluar. Las pruebas no autorizadas son ilegales en la mayoría de las jurisdicciones.
| Flag | Propósito |
|---|
--version | identifica la versión del núcleo (etiqueta generator del feed, respaldo mediante meta de la portada) |
--jsonp | además verifica que el envoltorio JSONP de REST (_jsonp/_envelope) es alcanzable |
-v, --verbose | imprime fragmentos de respuesta de las sondas (evidencia para informes) |
--json | resultado JSON legible por máquina (CI / automatización) |
| Comportamiento observado | Veredicto | Salida |
|---|
<b>marker</b> reflejado sin escapar | RAW_HTML — vulnerable | 0 |
< area id=marker> re-analizado en un <area id="marker"> vivo (cualquier formato de comillas / forma vacía) | AREA_BYPASS — vulnerable | 0 |
| Nombre de usuario escapado en HTML | ESCAPED — no vulnerable | 1 |
| Etiquetas eliminadas, el texto permanece | STRIPPED — no vulnerable | 1 |
El < area …> crudo pasa pero nunca se re-analiza | RAW_TEXT_ONLY — texto inerte, no vulnerable | 1 |
| Sin reflejo en absoluto | NOT_REFLECTED — no vulnerable | 1 |
| Sonda bloqueada (HTTP ≥ 400 / página de desafío) o destino inalcanzable | ERROR | 2 |