
Exploit de prueba de concepto para CVE-2026-64638: XSS reflejado en el inicio de sesión de WordPress encadenado con DOM clobbering para lograr la apropiación de la cuenta de administrador y la ejecución remota de código.
Software: WordPress Core ≤ 7.0.2 (todas las versiones anteriores a 7.0.3)
CVSS: 8.9 (Alta)
CWE: CWE-79 — Neutralización incorrecta de la entrada durante la generación de la página web
Autenticación requerida: Ninguna (Pre-Auth)
Interacción del usuario: Activa (el administrador necesita hacer clic en 1 enlace)
Impacto: XSS → Toma de control de la cuenta → Ejecución remota de código
WordPress es el sistema de gestión de contenidos más popular del mundo, y representa más del 40% de todos los sitios web en internet. Cada sitio WordPress tiene una página de inicio de sesión en /wp-login.php — este es un endpoint público al que cualquiera puede acceder sin autenticación.
Cuando un usuario introduce un nombre de usuario incorrecto, WordPress muestra un mensaje de error que contiene exactamente el nombre de usuario que el usuario acaba de escribir: "El nombre de usuario X no está registrado en este sitio." El problema radica en que el valor del nombre de usuario se coloca directamente en la respuesta HTML sin pasar por ninguna función de escape — un atacante solo necesita introducir HTML/JavaScript en lugar de un nombre de usuario real, y el código se ejecutará en el navegador.
Esta es una vulnerabilidad de XSS Reflejado — el payload está contenido en la solicitud y el servidor lo refleja idénticamente en el HTML. Lo que la hace peligrosa es que la vulnerabilidad reside en la página de inicio de sesión — un lugar al que los administradores acceden con frecuencia, donde las cookies de sesión del administrador pueden ser robadas.
El equipo de investigación descubrió además que este XSS puede encadenarse con una vulnerabilidad de DOM clobbering en el emoji-loader de WordPress, lo que permite cargar JavaScript desde un servidor externo. A partir de ahí, un atacante puede crear una nueva cuenta de administrador → instalar un plugin que contenga un webshell → ejecutar código PHP en el servidor. Esta cadena de explotación se conoce como XSS2Shell.
| Atributo | Valor |
|---|---|
| ID CVE | CVE-2026-64638 |
| Puntuación CVSS | 8.9 (Alta) |
| Software | WordPress Core ≤ 7.0.2 |
| Autenticación | No requerida (Pre-Auth) |
| Interacción del usuario | Requiere 1 clic (el administrador hace clic en el enlace) |
| Complejidad del ataque | Alta |
| Parcheado | WordPress 7.0.3 (08/06/2026) |
| Reportado por | equipo pwn.ai vía HackerOne |
| Informe HackerOne | #3877102 |
WordPress carga el soporte de emojis en cada página (incluida la página de inicio de sesión) mediante el archivo emoji-loader.js. Este script lee la configuración de un elemento con id="wp-emoji-settings":
// Before patch (vulnerable)
const settings = JSON.parse(
document.getElementById('wp-emoji-settings').textContent
);
document.getElementById() devuelve el primer elemento del DOM con un id coincidente. Si un atacante inyecta un <div id="wp-emoji-settings"> antes de la etiqueta de script original, getElementById leerá el contenido del atacante en lugar de la configuración real. Esta técnica se denomina DOM clobbering — sobrescribir el comportamiento de JavaScript mediante la inyección de elementos HTML.
La configuración de emojis contiene una URL para cargar un archivo JavaScript (concatemoji). El atacante controla esta URL → carga un archivo JS desde un servidor externo → ejecuta código arbitrario dentro del contexto del navegador.
Una vez que se logra la ejecución de JavaScript dentro del contexto del administrador, el atacante tiene privilegios completos de administrador de WordPress:
/wp-admin/user-new.php con la sesión del administrador/wp-admin/plugin-install.phpCualquiera de los 3 métodos anteriores permite ejecutar código PHP en el servidor — es decir, RCE.
A partir del commit de corrección 0d6d42e en wordpress-develop, identifiqué 3 ubicaciones en el archivo wp-includes/user.php donde el nombre de usuario/correo electrónico se coloca directamente en el mensaje de error:
# View diff between vulnerable and patched versions
git diff 7.0.2..7.0.3 -- src/wp-includes/user.php
Ubicación 1 — Línea 189 (el nombre de usuario no existe):
Antes :
// BEFORE (vulnerable):
sprintf(
__( 'The username <strong>%s</strong> is not registered...' ),
$username // ← no escaping
)

Después :
// fixed:
sprintf(
__( 'The username <strong>%s</strong> is not registered...' ),
esc_html( $username ) // ← escaped
)
Ubicación 2 — Línea 216 (contraseña incorrecta):
Antes :

// BEFORE:
'<strong>' . $username . '</strong>' // ← no escaping
Después :
// AFTER:
'<strong>' . esc_html( $username ) . '</strong>'
Ubicación 3 — Línea 299 (contraseña incorrecta para el correo electrónico):
Antes :

// BEFORE:
'<strong>' . $email . '</strong>' // ← no escaping
Después :
// AFTER:
'<strong>' . esc_html( $email ) . '</strong>'
Flujo de datos desde la solicitud POST hasta el mensaje de error:
$_POST['log'] ← user input from login form
↓
wp_signon() [user.php:51]
$credentials['user_login'] = wp_unslash($_POST['log']) ← only removes backslashes
↓
wp_authenticate($username, $password) [pluggable.php:689]
$username = sanitize_user($username) ← strips HTML tags, but has a bypass
↓
wp_authenticate_username_password() [user.php:153]
get_user_by('login', $username) ← user not found
↓
sprintf('The username <strong>%s</strong>...', $username) ← XSS!
↓
WP_Error → login_header() → wp_admin_notice()
wp_kses_post(...) ← filters HTML but allows <div>, <a>, through
↓
HTML response → browser render → JavaScript execute
WordPress tiene 2 capas de filtrado antes de que el nombre de usuario llegue al HTML:
Capa 1: sanitize_user() — Llama a strip_tags() para eliminar las etiquetas HTML. Sin embargo, strip_tags() de PHP tiene limitaciones conocidas: el formato de etiquetas no estándar puede evadir el filtro.
Capa 2: wp_kses_post() — Permite pasar un subconjunto seguro de HTML, incluidos <div>, <a>, `` con ciertos atributos (pero elimina los manejadores de eventos como onerror, onload). Fundamentalmente: wp_kses_post permite <div id="wp-emoji-settings"> — precisamente el elemento necesario para el DOM clobbering.
El equipo de pwn.ai encontró una forma de evadir ambas capas para inyectar un payload útil. Los detalles técnicos específicos no han sido publicados públicamente.