
Escáner de WordPress para CVE-2026-64638 basado en comportamiento que utiliza sondeos de inicio de sesión benignos; clasifica el comportamiento del saneador y genera PoCs de solo alerta para uso autorizado.
Escáner masivo basado en el comportamiento y generador de PoC de calidad probatoria para la cadena XSS a RCE sin autenticación previa de WordPress que afecta a más de 500 millones de sitios web.
Solo detección. Sin weaponización. Diseñado para programas de bug bounty y equipos azules.
¿Qué es esto? • Inicio rápido • Dorks de Shodan • Uso • Matriz de decisión • Detección • FAQ
Busca instancias de WordPress potencialmente vulnerables en Internet antes de escanear:
http.component:"wordpress" -http.title:"Just a moment"
Encuentra sitios WordPress excluyendo el modo "I'm Under Attack" de Cloudflare / las páginas de protección contra bots que bloquean o desafían las solicitudes automatizadas.
http.component:"wordpress" http.title:"Log In"
Devuelve solo las páginas de inicio de sesión de WordPress: la superficie de ataque exacta para CVE-2026-64638.
http.component:"wordpress" "wp-content" "?ver=7.0" -"?ver=7.0.3"
Señala instancias de WordPress 7.0.x sin el parche 7.0.3 mediante el fingerprinting de versiones de activos.
http.component:"wordpress" http.html:"wp-login.php"
Detecta sitios donde wp-login.php es accesible pero puede no ser la página actual: mayor cobertura.
http.component:"wordpress" -http.title:"Just a moment" -http.title:"Attention Required" -org:"Cloudflare"
Filtro agresivo que descarta la mayoría de los objetivos detrás de Cloudflare. Úsalo al escanear a gran escala con --active: Cloudflare limitará la tasa o bloqueará la solicitud de la sonda.
Consejo: exporta los resultados de Shodan con
shodan downloady canaliza los nombres de host directamente axss2shell_mass.py -i.
El 7 de agosto de 2026, pwn.ai reveló CVE-2026-64638 (XSS2Shell) — una vulnerabilidad crítica de cross-site scripting sin autenticación previa en el núcleo de WordPress que encadena hasta la ejecución remota de código en el servidor. [citation:pwn.ai blog]
El fallo explota una discrepancia entre los analizadores de strip_tags() de PHP y wp_kses_post() de WordPress:
strip_tags() utiliza < seguido inmediatamente de una letra para identificar etiquetas HTML. < area id=...> (con un espacio) se trata como texto — sobrevive.wp_kses_post() (KSES) reconoce < area como un elemento <area> válido — y <area> está en la lista blanca de KSES. [citation:pwn.ai blog]Un inicio de sesión fallido con un nombre de usuario especialmente diseñado < area id=ajaxurl href=/?rest_route=/&_method=GET&_jsonp=alert>... omite ambos saneadores, se renderiza como DOM vivo en la página de inicio de sesión, secuestra el script user-profile.js de WordPress mediante DOM clobbering y dispara alert() en el origen de WordPress — cero clics, cero autenticación, cero cookies requeridas. [citation:pwn.ai blog]
¿Escalado a un administrador con sesión iniciada? La misma primitiva roba contraseñas de aplicación mediante Same Origin Method Execution (SOME), sube un plugin malicioso y ejecuta PHP como www-data. [citation:pwn.ai blog] [citation:hadrian.io blog]
Afectado: WordPress 6.4 hasta 7.0.2 — parcheado en 7.0.3 con backports para 4.7+.
Impacto: ~500 millones de sitios web en el momento de la divulgación. [citation:pwn.ai blog]
Este es un kit de herramientas de solo detección. No weaponiza la vulnerabilidad: ofrece a investigadores de seguridad, cazadores de bug bounty y equipos azules todo lo necesario para:
"Una cadena de versión indica cuál debería ser el nivel de parche del código.
Solo el comportamiento del saneador de la página de inicio de sesión indica si el fallo se dispara."
Los hosts administrados aplican backports de parches de seguridad silenciosamente sin cambiar las cadenas de versión. Los plugins de endurecimiento del inicio de sesión reemplazan el mensaje de error por completo, eliminando el canal de reflexión incluso en versiones inseguras. Los escáneres basados solo en versión producen falsos positivos y falsos negativos. Este escáner envía una única sonda benigna y clasifica el comportamiento real del saneador.
git clone https://github.com/jakestone/xss2shell.git
cd xss2shell
pip install -r requirements.txt
# Passive — no probes sent to target, version + endpoint fingerprinting only
python3 xss2shell_mass.py -i domains.txt -o results
# Active — sends ONE benign failed-login per host (authorized assets only!)
python3 xss2shell_mass.py -i domains.txt -o results --active --workers 80
# Single target
python3 make_poc.py --target https://blog.example.com
# Batch from scanner output
python3 make_poc.py --from-results results.csv -o pocs/
Abre el .poc.html generado en tu navegador mientras grabas un video → si se dispara alert(), has capturado evidencia de XSS sin autenticación previa.
xss2shell_mass.py)usage: xss2shell_mass.py [-h] -i INPUT [-o OUTPUT]
[--active] [--workers WORKERS]
[--timeout TIMEOUT] [--quiet]
?ver= de activos, referencias a wp-content)user-profile.js encolado, versiones de activos del núcleo/?rest_route=/&_method=GET&_jsonp=<random> — ¿está abierta la vía JSONP?--active)Envía un único inicio de sesión fallido con el nombre de usuario < area id=<RANDOM> href=/x2s> y clasifica la respuesta HTML:
bypass — el elemento <area> real con nuestro marcador sobrevivió → desajuste strip_tags/KSES CONFIRMADOescaped — marcador presente pero codificado como entidad → parche o endurecimiento presentestripped — se muestra el error predeterminado de WP, etiquetas eliminadas → acevomod o parcheadoclosed — sin reflexión del nombre de usuario en absoluto → plugin de endurecimiento del inicio de sesión instaladomake_poc.py)usage: make_poc.py [-h] [--target TARGET] [--from-results FROM_RESULTS]
[-o OUTDIR]
Genera la página PoC publicada por pwn.ai para cada objetivo: el formulario HTML exacto que dispara alert() en un WordPress sin parchear. Se incluyen tres variantes de payload en los comentarios:
El motor de decisión del escáner combina la clasificación de versión (de la API stable-check de WordPress.org) con evidencia de comportamiento para producir 10 veredictos distintos:
Columnas del CSV: host, url, status, checker_status, wp_version, branch_status, evidence, http, ms, error
La columna checker_status se corresponde con el vocabulario del verificador público de pwn.ai (vulnerable / patched / not_wordpress / unreachable / inconclusive / error) para una correlación directa.
Si estás del lado defensor, estas son las señales forenses que deja esta vulnerabilidad:
# Primary signal: encoded '<' in the log parameter
POST /wp-login.php → log=%3C... (URL-encoded < in username field)
# Higher confidence: paired with REST pivoting
GET /?rest_route=/&_method=GET&_jsonp=... # JSONP callback
GET /wp-json/wp/v2/statuses/publish?_jsonp=... # WAF-bypass variant
# Escalation stage indicators
GET /wp-admin/authorize-application.php?success_url=<off-origin>
POST /wp-admin/update.php?action=upload-plugin
GET /wp-content/plugins/<random>/shell.php
Bloquea POST /wp-login.php cuando el parámetro log contenga %3C (< codificado en URL). Los nombres de usuario válidos de WordPress nunca contienen corchetes angulares. No limites la regla a etiquetas específicas: KSES permite tabulaciones, nuevas líneas y retornos de carro después de <, así como cualquier etiqueta de la lista blanca, por lo que una regla específica de etiquetas se evade trivialmente. [citation:hadrian.io blog]
El callback _jsonp= de la fase de escalada utiliza puntos para recorrer propiedades (p. ej., window.opener.approve.click). Marca las solicitudes REST con callbacks JSONP con puntos como indicadores sólidos de explotación. [citation:hadrian.io blog]
THIS TOOL IS DETECTION-ONLY. IT DOES NOT:
✗ Weaponize the JSONP callback beyond the public alert()
✗ Include admin-lure pages or Application Password capture
✗ Include REST abuse, plugin upload, or PHP shell code
✗ Execute more than one failed login per target per scan
YOU MUST:
✓ Only scan assets you own or have written authorization to test
✓ Only generate PoCs for your own browser on your own server
✓ Never send PoC links to site admins/users
✓ Never escalate past alert() without program written approval
✓ Follow the bug bounty program scope and rules
This toolkit exists for authorized security research, bug bounty
programs, and defensive detection engineering. Misuse is your
responsibility.
xss2shell/
├── README.md ← You are here
├── xss2shell_mass.py ← Behavior-first mass scanner (v1.1.0)
├── make_poc.py ← Evidence-grade PoC page generator
├── requirements.txt ← Python dependencies (just `requests`)
├── .gitignore ← Ignores scan outputs and cache
└── example/
├── domains.txt ← Example input file
└── example_output.csv ← Example scan output
P: ¿Por qué no comprobar simplemente la cadena de versión de WordPress?
R: Los hosts administrados (WP Engine, Kinsta, Pantheon, etc.) aplican con frecuencia backports de parches de seguridad sin cambiar la versión. Los plugins de endurecimiento del inicio de sesión reemplazan el mensaje de error por completo. Ambos casos producen falsos positivos en escáneres basados solo en versión y falsos negativos para versiones ocultas. Este escáner prueba el comportamiento real del saneador.
P: ¿Es peligrosa la sonda --active?
R: No. Envía exactamente un inicio de sesión fallido con un nombre de usuario marcador benigno. No intenta ejecutar JavaScript, no enumera nombres de usuario válidos y no dispara ningún exploit real. Es menos intrusiva que un intento de inicio de sesión estándar.
P: ¿Se puede usar esta herramienta para escanear sin autorización?
R: No. La sonda activa envía un POST HTTP a /wp-login.php, que es una solicitud al servidor objetivo. Úsala solo en activos que poseas o para los que tengas autorización escrita explícita para probar.
P: ¿Cuál es la diferencia entre vulnerable y confirmed_vulnerable?
R: vulnerable significa que la API de WordPress.org indica que la versión es insegura, pero no hemos confirmado el desajuste strip_tags/KSES a nivel de comportamiento. confirmed_vulnerable significa que enviamos una sonda y el elemento <area> sobrevivió a ambos saneadores — la cadena publicada puede dispararse.
P: ¿Puedo usarlo para los informes de mi programa de bug bounty?
R: ¡Sí! La columna checker_status se asigna directamente al vocabulario del verificador público de pwn.ai para una correlación fácil. Combina los resultados del escaneo con la evidencia en video del PoC de make_poc.py para informes completos.
P: ¿Detecta la cadena de RCE?
R: No. Este kit detecta el punto de entrada XSS sin autenticación previa. La cadena RCE completa requiere un administrador con sesión iniciada, Application Passwords habilitadas y permisos de subida de plugins — condiciones que este escáner no evalúa. El escáner se centra en lo que es observable externamente: la omisión del saneador.
Construido por 0xlipon • Solo detección • Solo para uso autorizado
| Recurso | Enlace |
|---|
| Divulgación original (pwn.ai) | pwn.ai/blog/xss2shell |
| Análisis técnico de Hadrian | hadrian.io/blog/wordpress-xss2shell |
| Aviso de WordPress (GHSA) | GHSA-52p2-r8wf-jcrf |
| Investigación sobre ataques SOME (2022) | pwn.ai/blog/bypass-csp-using-wordpress |
| Lanzamiento de WordPress 7.0.3 | wordpress.org/news/2026/08/wordpress-7-0-3-release |
| Bandera | Descripción |
|---|
-i, --input | Archivo con un host por línea (dominio simple o URL completa) |
-o, --output | Ruta base para los archivos de salida (genera .csv + .json) |
--active | Habilita la sonda de comportamiento: un inicio de sesión fallido por host |
--workers | Tamaño del pool de hilos (predeterminado: 50, máximo ~200 para conexiones buenas) |
--timeout | Timeout HTTP en segundos (predeterminado: 10) |
--quiet | Solo imprime confirmed_vulnerable, vulnerable y likely_vulnerable |
| Variante | Valor de href | Cuándo usarla |
|---|
| Predeterminada | /?rest_route=/&_method=GET&_jsonp=alert | WordPress estándar |
| Envelope | /?rest_route=/&_method=GET&_envelope=1&_jsonp=alert | REST devuelve 401 (lo envuelve en 200) |
| Pivote WAF | /wp-json/wp/v2/statuses/publish?_jsonp=alert&_method=GET | ?rest_route= bloqueado por el WAF |
| Veredicto | Condiciones |
|---|
confirmed_vulnerable 🔴 | La versión es insegura Y el marcador de la sonda sobrevivió como elemento <area> Y el gadget user-profile.js está presente |
vulnerable 🔴 | La versión es insegura según wordpress.org; sonda de comportamiento NO ejecutada (vuelve a ejecutarla con --active) |
likely_vulnerable 🟠 | El marcador de la sonda sobrevivió PERO user-profile.js no está encolado (falta el gadget de auto-disparo publicado) |
mitigated 🟣 | La versión es insegura PERO el marcador de la sonda fue escapado/eliminado/cerrado (backport silencioso o endurecimiento) |
likely_patched 🟢 | Versión oculta/desconocida PERO el marcador de la sonda fue escapado/eliminado |
patched 🟢 | La versión es latest o outdated (tiene backports de seguridad) |
not_wordpress ⚫ | No se detectó huella de WordPress |
unreachable ⚫ | Error de conexión (timeout, SSL, DNS) |
inconclusive 🟡 | Bloqueo del WAF, challenge de Cloudflare, versión oculta sin sonda o ausencia de la página de inicio de sesión |
error 🟡 | Fallo inesperado durante el escaneo |