
Análisis defensivo, desglose del parche y escáner de detección para CVE-2026-14378 (WordPress DevKit Pro Plugin <= 2.3.0).
CVE-2026-14378 es una Omisión de autenticación no autenticada crítica (CVSS 9.8) en el plugin DevKit Pro para WordPress, que afecta a todas las versiones hasta la 2.3.0 inclusive.
El plugin incluye un mecanismo de cambio de usuario para desarrolladores. Cuando un administrador "cambia" a otra cuenta de usuario, almacena el ID del administrador en una cookie llamada original_user_id. El fallo: el plugin renderiza un formulario HTML de "volver atrás" en cualquier página siempre que esa cookie esté presente — incluso para visitantes no autenticados que establecen la cookie manualmente. Peor aún, el paso de verificación del nonce comprueba la capacidad de administrador del usuario de la cookie, no la sesión del solicitante — por lo que el servidor entrega sin problema una cookie de sesión de administrador autenticado a cualquiera que envíe el POST correcto.
Resultado neto: no se requieren credenciales para tomar el control total del administrador de WordPress.
| Atributo | Detalles |
|---|---|
| ID de CVE | CVE-2026-14378 |
| Clase de vulnerabilidad | Autenticación incorrecta (CWE-287) |
| Puntuación CVSS v3.1 | 9.8 (Crítica) |
| Vector CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Software afectado | Plugin de WordPress DevKit Pro (dplugins) |
| Versiones vulnerables | <= 2.3.0 |
| Versión parcheada | 2.3.1 |
| Fecha de divulgación | 02 Oct 2026 |
DevKit Pro incluye una ayuda para desarrolladores que permite a los administradores del sitio "cambiar" a otras cuentas de usuario para probar permisos. Cuando el administrador usa el cambio:
Set-Cookie: original_user_id=1isset($_COOKIE['original_user_id'])wp_footer() con un formulario POST oculto que contiene un nonce nuevoadmin-post.php?action=revert_switchwp_set_auth_cookie($user_id) para restaurar la sesión originalEl manejador del hook wp_footer:
// DevKit Pro <= 2.3.0 — render_switch_back_bar()
public function render_switch_back_bar() {
// FLAW: Only checks if cookie exists — no session validation!
if ( isset( $_COOKIE['original_user_id'] ) ) {
$user_id = (int) $_COOKIE['original_user_id'];
$nonce = wp_create_nonce( 'devkit_revert_switch_' . $user_id );
echo '<div id="devkit-pro-switch-back" class="devkit-switch-bar" style="display:none;">';
echo ' <form id="devkit-revert-form" action="' . admin_url('admin-post.php') . '" method="POST">';
echo ' <input type="hidden" name="action" value="revert_switch" />';
echo ' <input type="hidden" name="_wpnonce" value="' . $nonce . '" />';
echo ' <input type="hidden" name="target_user_id" value="' . $user_id . '" />';
echo ' </form>';
echo '</div>';
echo '<!-- DevKit Pro 2.3.0 Switch Component Active -->';
}
}
El manejador POST que procesa el envío del formulario:
// DevKit Pro <= 2.3.0 — handle_revert_switch()
public function handle_revert_switch() {
$user_id = (int) $_POST['target_user_id'];
$nonce = sanitize_text_field( $_POST['_wpnonce'] );
if ( ! $this->verify_nonce_and_capability( $user_id, $nonce ) ) {
wp_die( 'Unauthorized' );
}
wp_set_current_user( $user_id );
wp_set_auth_cookie( $user_id ); // <-- Grants authenticated session to caller
wp_redirect( admin_url() );
exit;
}
private function verify_nonce_and_capability( $user_id, $nonce ) {
if ( ! wp_verify_nonce( $nonce, 'devkit_revert_switch_' . $user_id ) ) {
return false;
}
// CRITICAL FLAW: Checks the cookie user's capability, not the caller's!
return user_can( $user_id, 'manage_options' );
}
user_can( $user_id, 'manage_options' ) responde a la pregunta: "¿Tiene el usuario #1 la capacidad manage_options?"
La respuesta para el usuario #1 (el primer administrador de WordPress creado) es siempre true.
Debería estar preguntando: "¿Tiene la persona que realiza esta solicitud HTTP la capacidad manage_options?"
La comprobación correcta es current_user_can('manage_options'), que devolvería false para un visitante no autenticado.
Todo este recorrido fue realizado y verificado contra una instancia de WordPress en vivo ejecutándose en un contenedor Podman local (http://localhost:8080) con DevKit Pro 2.3.0 activo.
Para reproducir esto localmente, necesitas:
Configuración rápida del laboratorio con Podman:
# Start MariaDB
podman run -d --name wp-db \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
mariadb:10.6
# Start WordPress
podman run -d --name wp-app \
-p 8080:80 \
--link wp-db:mysql \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_NAME=wordpress \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=wppass \
wordpress:latest
Después de que WordPress se inicialice (http://localhost:8080/wp-admin/install.php), instala y activa DevKit Pro 2.3.0 a través del menú de plugins.