◈ 渗透任务
HTML 净化器绕过测试
任务中
你的身份
「暗影安全公司」渗透测试工程师
目标网站
RichEditor 富文本编辑平台 (editor.richeditor.example.com)
委托方
RichEditor 使用了 HTML 净化器(DOMPurify),但可能存在 mXSS 绕过
任务目标
利用浏览器解析差异构造 mXSS payload,绕过净化器
- 构造在净化后仍可执行的 payload
- 绕过 DOMPurify 触发 alert(1)
📖 教程:Mutation XSS
什么是 Mutation XSS?
Mutation XSS(mXSS)是一种特殊的 XSS 类型。攻击者的 payload 经过 HTML 净化器处理后看起来是安全的,但当浏览器将净化后的输出渲染为 DOM 时,HTML 结构发生变异(mutation),使恶意代码复活。
核心矛盾:
- 净化器用自己的解析器解析输入,得到一个"安全"的 DOM
- 净化器序列化这个 DOM 为 HTML 字符串输出
- 浏览器用自己的解析器重新解析这个 HTML
- 两次解析的结果不同 → 恶意代码复活
HTML 命名空间
HTML 文档中存在三个命名空间,它们对标签的解析规则不同:
1. HTML 命名空间(默认)
<div>...</div>
// 标准 HTML 标签,按 HTML 规则解析
2. SVG 命名空间
<svg>
<style>...</style> // SVG 中的 style 不同于 HTML 中的 style
<foreignObject>
<div>...</div> // foreignObject 中可以嵌入 HTML
</foreignObject>
</svg>
3. MathML 命名空间
<math>
<mtext>...</mtext> // mtext 可以包含 HTML 标签
<mglyph> // mglyph 是自闭合标签
</math>
变异的关键:mtext 和 mglyph
<mtext> 在 MathML 中允许包含流式内容(HTML 标签)。但 <mglyph> 是自闭合的,不同浏览器的处理方式不同。
// 净化器可能这样解析:
<math>
<mtext>
<table>
<mglyph> // 净化器认为 mglyph 是自闭合的
<style>...</style>
</table>
</mtext>
</math>
// 浏览器可能这样解析(变异后):
<math>
<mtext>
<table>
<mglyph>
<style>... // mglyph 吞掉了后续内容
</mglyph> // 浏览器强制闭合 mglyph
</table>
</mtext>
</math>
DOMPurify 历史绕过案例
// CVE-2020-26881 - DOMPurify < 2.0.17
<math><mtext><table><mglyph><style><!--
</style><img title="--><img src onerror=alert(1)>">
// 解析过程:
// 1. 净化器在 MathML 命名空间中解析
// 2. <style> 的内容是 <!-- ,被视为注释开始
// 3. 净化器输出安全的 HTML
// 4. 浏览器重新解析时,<img title="..."> 中的 --> 关闭了注释
// 5. onerror 事件处理器被注入
其他 Mutation 向量
noscript 在 JS 禁用/启用时的差异
// JS 启用时:<noscript> 内容不解析
<noscript><img src=x onerror=alert(1)></noscript>
// 但某些净化器会解析 noscript 内容
textarea/title 的 RCDATA 解析
// <textarea> 和 <title> 是 RCDATA 元素
// 它们的内容中 < 不会开始新标签
// 但实体编码会被解码
<textarea><img src=x onerror=alert(1)></textarea>
// 不会执行
<textarea></textarea><img src=x onerror=alert(1)>
// 可能通过变异绕过
防御 Mutation XSS
- 使用经过充分审计的净化库(如最新版 DOMPurify)
- 开启净化器的
FORCE_BODY和命名空间感知选项 - 定期更新净化库,跟踪 mXSS 绕过案例
- 配合 CSP 防止内联脚本执行
🎯 练习:Mutation XSS
目标:下面的"净化器"移除了
<script> 标签、事件处理器和 javascript: 协议。
请利用 HTML 命名空间解析差异,使净化后的输出在浏览器中变异为恶意代码。
在上方输入框中输入 payload,点击"提交"查看净化器处理结果...
📄 查看漏洞源码 (PHP)
<?php
$input = $_GET['input'];
// 模拟"安全"的 HTML 净化器
function sanitize($html) {
// 移除 <script> 标签
$html = preg_replace("/<script\b[^>]*>.*?<\/script>/is", "", $html);
// 移除 on* 事件处理器
$html = preg_replace("/\bon\w+\s*=/i", "BLOCKED=", $html);
// 移除 javascript: 协议
$html = preg_replace("/javascript\s*:/i", "BLOCKED:", $html);
// 允许通过某些"安全"标签(但忽略了命名空间解析差异)
return $html;
}
$output = sanitize($input);
echo $output;
?>
🔒 净化器规则:
<script>标签 — 已移除on*事件处理器 — 已替换为 BLOCKEDjavascript:协议 — 已替换为 BLOCKED- HTML 标签 — 保留(包括 math, svg, style 等)
提示:思考净化器的 HTML 解析器和浏览器的解析器有什么不同。