此仓库包含一个本地 Docker 实验室,用于复现和验证 CVE-2026-56011,这是一个影响 MapPress Maps for WordPress 的未认证跨站脚本(XSS)漏洞。
MapPress Maps for WordPress 是一个用于在 WordPress 页面和文章中渲染地图的插件。漏洞行为影响可通过 mappress=embed 请求参数访问的 iframe 地图渲染路径。
此实验室比较两个 MapPress 版本:
| Service | MapPress 版本 | 目的 | URL |
|---|---|---|---|
| vuln | 2.97.3 | 易受攻击的比较目标 | http://localhost:8081 |
| patched | 2.97.4 | 已修补的比较目标 | http://localhost:8082 |
此本地实验室演示的验证路径如下:```text Unauthenticated browser request → GET /?mappress=embed → request supplies a crafted name value → vulnerable target renders name into an unquoted id attribute → injected onclick handler becomes a standalone HTML attribute → clicking the rendered MapPress component triggers alert(1) → patched target keeps the payload inside a quoted and escaped id attribute → clicking the rendered component does not trigger alert(1)
易受攻击的目标使用此手动浏览器 URL:```text
http://localhost:8081/?mappress=embed&name=cve56011%20onclick%3Dalert%281%29&width=400px&height=300px&zoom=5¢er=0%2C0
预期的易受攻击结果:```text Click on the rendered MapPress component → alert(1) pops up
已经修补的目标对MapPress 2.97.4使用了相同的payload:```text
http://localhost:8082/?mappress=embed&name=cve56011%20onclick%3Dalert%281%29&width=400px&height=300px&zoom=5¢er=0%2C0
预期的修补结果:```text Click on the rendered MapPress component → no alert appears
本实验室故意仅使用手动浏览器验证。它不包含 PoC 脚本、浏览器自动化、凭证窃取、外部回调、恶意软件、持久性、后渗透活动或针对外部系统的攻击。
## 已验证的事实
| 声明 | 证据 | 本实验室验证方法 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| CVE-2026-56011 影响 WordPress 的 MapPress Maps 插件。 | 公开漏洞公告表明受影响的 WordPress 插件是 MapPress Maps for WordPress。 | 查看参考文献部分,并检查两个 Docker 目标中安装的插件。 |
| 本实验室中的易受攻击比较版本是 MapPress 2.97.3。 | `vuln` 服务使用 `MAPPRESS_VERSION: 2.97.3` 构建该插件。 | 检查 `docker-compose.yml` 和 `vuln/Dockerfile`。 |
| 本实验室中的修补后比较版本是 MapPress 2.97.4。 | `patched` 服务使用 `MAPPRESS_VERSION: 2.97.4` 构建该插件。 | 检查 `docker-compose.yml` 和 `patched/Dockerfile`。 |
| MapPress 2.97.4 引入了相关的 iframe 转义修复。 | 2.97.4 的官方插件更新日志显示 `Added: escape in iframe`。 | 查看官方 WordPress 插件更新日志,并比较易受攻击版本和修补版本的源代码。 |
| 易受攻击的源代码将地图名称渲染到 Web 组件的 `id` 属性中,但未加引号。 | 在 2.97.3 中,`mappress_map.php` 渲染 `<mappress-map id={$name} ...>`。 | 比较 2.97.3 和 2.97.4 的源代码。 |
| 修补后的源代码对 `id` 值进行了引号包围和转义。 | 在 2.97.4 中,`mappress_map.php` 使用 `esc_attr($name)` 渲染 `id="`。 | 比较 2.97.3 和 2.97.4 之间的补丁差异。 |
| iframe 路径无需认证即可访问。 | 当 `$_GET['mappress']` 存在时,MapPress 注册 `template_redirect`。 | 在不登录的情况下从浏览器请求 `/?mappress=embed...`。 |
| iframe 路径从请求中读取地图属性。 | `template_redirect()` 将 `$_GET` 映射为地图参数并调用 iframe 渲染器。 | 检查 `mappress.php` 并重现手动 URL。 |
| 易受攻击的目标允许通过 `name` 进行属性注入。 | 精心构造的 name 值可以突破未加引号的 `id` 属性,变成 `onclick=alert(1)`。 | 打开易受攻击的手动 URL 并点击渲染的 MapPress 组件。 |
| 修补后的目标阻止了测试的属性注入行为。 | 修补后的输出将完整的载荷保留在引号包围的 `id` 属性内。 | 打开修补后的手动 URL 并点击渲染的 MapPress 组件。 |
## 假设与未知因素
本实验室使用 MapPress 2.97.3 作为易受攻击的比较目标,因为公开公告指出包括 2.97.3 在内的版本均受影响,且源代码差异显示该版本中存在未加引号的易受攻击属性渲染。
本实验室使用 MapPress 2.97.4 作为修补后的比较目标,因为公开公告将 2.97.4 确定为修复版本,官方更新日志表明 iframe 路径中已添加转义。
测试的易受攻击行为是未经认证的 iframe 渲染路径:```text
GET /?mappress=embed&name=<crafted-value>
本实验侧重于手动在浏览器中执行无害的警报负载:```text name=cve56011 onclick=alert(1)
该实验室不试图证明存储型传递链。某些公开公告将该漏洞归类为存储型XSS。本仓库聚焦于源确认的iframe渲染接收器以及可通过未认证的`mappress=embed`路由在本地复现的脆弱版与修补版行为。
该实验室不演示:
* 存储型载荷持久性,
* WordPress账户泄露,
* 管理员会话窃取,
* nonce窃取,
* 外部回调,
* 盲XSS收集,
* 凭证窃取,
* 数据库转储,
* 恶意软件,
* 持久性,
* 或针对非实验室系统的攻击。
手动浏览器验证证明了与安全相关的渲染差异:```text
MapPress 2.97.3:
crafted name value becomes executable onclick attribute
MapPress 2.97.4:
crafted name value remains inside the quoted id attribute
CVE-2026-56011 的根本原因是当 MapPress 在 iframe 地图输出路径中渲染 web component 时,对地图 name 值的输出编码不正确。
漏洞代码路径接受来自请求的地图渲染属性,并最终渲染一个自定义 HTML 元素:```html <mappress-map ...>
在 MapPress 2.97.3 中,地图名称被直接插入到 `id` 属性中,没有使用引号,也没有进行属性上下文转义:```php
return "<div></div>\r\n<mappress-map id={$name} {$atts}>\r\n$pois\r\n</mappress-map>\r\n";
这是不安全的,因为该值被用于HTML属性上下文中。如果攻击者控制 name,包含空格的值可以终止预期的 id 值并引入一个新属性。
该漏洞行为可总结为:```text Attacker sends unauthenticated iframe request → name = cve56011 onclick=alert(1) → MapPress sanitizes the value as text → sanitized text is still unsafe for an unquoted HTML attribute → renderer outputs id=cve56011 onclick=alert(1) → onclick becomes a standalone event handler attribute → user clicks the rendered component → JavaScript executes
关键点在于,通用的文本清理(sanitization)并不等同于正确的输出转义(escaping)。
易受攻击的代码对地图名称使用了 `sanitize_text_field()`,但这并不能使该值在未加引号的HTML属性中变得安全。空格在HTML属性中仍然有意义,因为它们分隔了一个属性和下一个属性。
因此,安全问题在于:```text
User-controlled input
+ unquoted HTML attribute context
+ missing esc_attr()
= attribute injection and XSS
补丁版本更改了渲染方式,对id值进行引用和转义处理:```php
return "
补丁后的行为可以总结如下:```text
Attacker sends the same crafted name value
→ MapPress renders id="cve56011 onclick=alert(1)"
→ onclick remains text inside the id value
→ no standalone event handler attribute is created
→ clicking the component does not execute alert(1)
安全教训是:```text Sanitize on input if needed, but always escape on output for the exact output context. For HTML attributes in WordPress, use esc_attr() and quote attribute values.
## 源代码分析
通过比较 MapPress 2.97.3 和 MapPress 2.97.4 确认了源代码级别的问题。
主要的渲染汇点在:```text
mappress_map.php
易受攻击的版本将 name 值作为未加引号的 id 属性渲染:```php
$name = (isset($vars['name']) ? $vars['name'] : 'noname');
return "
补丁版本引用了属性并对值进行了转义:```php
$name = (isset($vars['name']) ? $vars['name'] : 'noname');
return "<div></div>\r\n<mappress-map id=\"" . esc_attr($name) . "\" {$atts}>\r\n$pois\r\n</mappress-map>\r\n";
当请求包含 mappress 查询参数时,注册 iframe 路由:```php
if (isset($_GET['mappress']))
add_action('template_redirect', array(CLASS, 'template_redirect'));
iframe 请求处理器将查询参数映射为 map 参数:```php
$args = array_map(function($arg) {
if ($arg == 'true')
return true;
if ($arg == 'false')
return false;
return $arg;
}, $_GET);
然后,处理程序创建或加载一个映射对象,并使用请求参数更新它:```php $map = new Mappress_Map(); $map->update($args); $map->layout = 'left'; echo self::get_iframe($map); die();
iframe 助手渲染地图内容:```php
$content = $map->display(null, true);
display() 路径会保留提供的非空 name 值:```php
if (empty($this->name)) {
$this->name = (defined('DOING_AJAX') && DOING_AJAX) ? "mapp" . uniqid() : "mapp$div";
$div++;
}
这意味着请求提供的 `name` 值能够到达存在漏洞的渲染接收器。