
Análise defensiva, detalhamento do patch e scanner de detecção para CVE-2026-14378 (Plugin WordPress DevKit Pro <= 2.3.0).
CVE-2026-14378 é um Bypass de Autenticação Não Autenticado crítico (CVSS 9.8) no plugin DevKit Pro para WordPress, afetando todas as versões até e incluindo a 2.3.0.
O plugin inclui um mecanismo de troca de usuário para desenvolvedores. Quando um administrador "troca" para outra conta de usuário, ele armazena o ID do administrador em um cookie chamado original_user_id. A falha: o plugin renderiza um formulário HTML de "voltar" em qualquer página sempre que esse cookie estiver presente — inclusive para visitantes não autenticados que definem o cookie manualmente. Pior ainda, a etapa de verificação de nonce verifica a capacidade de administrador do usuário do cookie, não a sessão do solicitante — então o servidor entrega alegremente um cookie de sessão de administrador autenticado para qualquer um que envie o POST correto.
Resultado final: zero credenciais necessárias para assumir completamente o admin do WordPress.
| Atributo | Detalhes |
|---|---|
| ID do CVE | CVE-2026-14378 |
| Classe da Vulnerabilidade | Autenticação Imprópria (CWE-287) |
| Pontuação CVSS v3.1 | 9.8 (Crítica) |
| Vetor CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Software Afetado | Plugin WordPress DevKit Pro (dplugins) |
| Versões Vulneráveis | <= 2.3.0 |
| Versão Corrigida | 2.3.1 |
| Data de Divulgação | 02 Out 2026 |
O DevKit Pro inclui um auxiliar de desenvolvedor que permite aos administradores do site "trocar" para outras contas de usuário para testar permissões. Quando o administrador usa a troca:
Set-Cookie: original_user_id=1isset($_COOKIE['original_user_id'])wp_footer() com um formulário POST oculto contendo um nonce novoadmin-post.php?action=revert_switchwp_set_auth_cookie($user_id) para restaurar a sessão originalO manipulador do 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 -->';
}
}
O manipulador POST que processa o envio do formulário:
// 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 à pergunta: "O usuário #1 possui a capacidade manage_options?"
A resposta para o usuário #1 (o primeiro administrador do WordPress criado) é sempre true.
Deveria estar perguntando: "A pessoa que está fazendo esta requisição HTTP possui a capacidade manage_options?"
A verificação correta é current_user_can('manage_options'), que retornaria false para um visitante não autenticado.
Todo este passo a passo foi executado e verificado contra uma instância WordPress ativa rodando em um contêiner Podman local (http://localhost:8080) com o DevKit Pro 2.3.0 ativo.
Para reproduzir isso localmente, você precisa de:
Configuração rápida de laboratório com 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
Após o WordPress ser inicializado (http://localhost:8080/wp-admin/install.php), instale e ative o DevKit Pro 2.3.0 através do menu de plugins.