通过命名空间混淆实现突变XSS - DOMPurify < 2.0.17 bypass
字数 1955 2025-08-20 18:18:05
DOMPurify < 2.0.17 命名空间混淆绕过分析
1. 漏洞概述
本漏洞利用HTML规范中的命名空间特性和表单元素解析规则,通过精心构造的HTML标记绕过DOMPurify的过滤机制,最终实现跨站脚本攻击(XSS)。该漏洞影响DOMPurify 2.0.17之前的版本。
2. 关键概念
2.1 DOMPurify工作原理
DOMPurify的工作流程:
- 将输入HTML解析为DOM树
- 遍历DOM树,删除所有不在白名单中的元素和属性
- 将过滤后的DOM树序列化为HTML字符串
- 返回安全的HTML字符串
典型使用方式:
div.innerHTML = DOMPurify.sanitize(htmlMarkup);
2.2 HTML解析与序列化的不一致性
HTML规范明确指出:序列化DOM树后再解析,不保证返回原始DOM结构。这种不一致性是mXSS(突变XSS)的根本原因。
2.3 表单元素的特殊解析规则
HTML规范中表单元素(<form>)的特殊性:
- 表单元素不能嵌套
- 解析器使用"表单元素指针"跟踪当前表单
- 结束标签
</form>会将表单元素指针设为null
特殊嵌套示例:
<form id="outer"><div></form><form id="inner"><input>
解析后会产生嵌套的表单结构,但序列化后再解析会消除嵌套。
2.4 HTML命名空间与外部内容
HTML解析器涉及三种命名空间:
- HTML命名空间 (
http://www.w3.org/1999/xhtml) - SVG命名空间 (
http://www.w3.org/2000/svg) - MathML命名空间 (
http://www.w3.org/1998/Math/MathML)
命名空间切换规则:
- 遇到
<svg>切换到SVG命名空间 - 遇到
<math>切换到MathML命名空间
2.5 集成点(Integration Points)
MathML文本集成点:
<math>,<mi>,<mo>,<mn>,<ms>,<mtext>
HTML集成点:
<math annotation-xml>(当encoding属性为text/html或application/xhtml+xml)<svg foreignObject>,<svg desc>,<svg title>
2.6 特殊元素mglyph和malignmark
在MathML文本集成点中,只有<mglyph>和<malignmark>作为直接子元素时保持MathML命名空间,其他元素默认切换到HTML命名空间。
3. 漏洞利用分析
3.1 利用Payload
<form><math><mtext></form><form><mglyph><style></math>
3.2 解析过程
初始解析:
- 第一个
<form>打开,设置表单元素指针 <math>切换到MathML命名空间<mtext>是MathML文本集成点</form>关闭表单,指针设为null- 第二个
<form>打开 <mglyph>作为<form>的子元素,在HTML命名空间<style>在HTML命名空间,内容作为文本处理
过滤后DOM树:
- html:form
- mathml:math
- mathml:mtext
- html:form
- html:mglyph
- html:style
- 文本内容: "</math>"
序列化后HTML:
<form><math><mtext><form><mglyph><style></math></style></mglyph></form></mtext></math></form>
重新解析时:
- 第一个
<form>打开 <math>切换到MathML命名空间<mtext>是MathML文本集成点- 尝试打开第二个
<form>,但由于在<mtext>内,表单元素指针不为null,第二个<form>被忽略 <mglyph>现在是<mtext>的直接子元素,保持在MathML命名空间<style>在MathML命名空间,内容不被视为文本</math>关闭MathML命名空间- ``在HTML命名空间创建,执行XSS
3.3 关键点
- 表单嵌套的解析差异:初始解析允许特殊形式的嵌套,但重新解析时会消除嵌套
- mglyph的命名空间变化:初始在HTML命名空间,重新解析后在MathML命名空间
- style元素的解析差异:HTML命名空间的style内容作为文本,MathML命名空间的style内容作为标记解析
4. 相关变种
另一个利用相同原理的Payload:
<math><mtext><table><mglyph><style><math><table id="</table>">
5. 防御建议
-
避免序列化-解析往返:使用DOMPurify的
RETURN_DOM或RETURN_DOM_FRAGMENT选项// 推荐用法 const cleanDOM = DOMPurify.sanitize(html, {RETURN_DOM: true}); -
升级DOMPurify:确保使用2.0.17或更高版本
-
理解HTML解析复杂性:开发者应充分了解HTML解析的特殊情况,特别是命名空间和元素嵌套规则
6. 总结
该漏洞展示了HTML解析器复杂性与安全过滤器的交互问题,通过:
- 滥用表单元素的解析特性
- 利用MathML命名空间中的特殊元素行为
- 依赖序列化-解析过程的不一致性
实现了对DOMPurify的绕过,强调了在安全过滤中直接操作DOM而非序列化HTML的重要性。