从两次DOMPurify绕过探索绕过xss过滤器之法
字数 2092 2025-08-30 06:50:11

DOMPurify绕过技术深度解析:从命名空间混淆到DOM Clobbering

前言

在Web安全领域,XSS(跨站脚本)攻击是最常见的安全威胁之一。虽然使用成熟的XSS过滤器如DOMPurify是防御XSS的有效手段,但理解其绕过技术对于安全研究人员和开发者都至关重要。本文将深入探讨两种主要的DOMPurify绕过技术:命名空间混淆/MXSS和DOM Clobbering。

核心概念

1. 命名空间(Namespace)混淆

HTML文档中存在三种命名空间,元素在不同命名空间中的解析方式各不相同:

  1. HTML命名空间:标准HTML元素
  2. SVG命名空间:SVG相关元素
  3. MathML命名空间:数学标记元素

关键点:

  • <style>在HTML中被当作文本,但在SVG或MathML中被当作HTML
  • 命名空间混淆常发生在MXSS(突变XSS)后,浏览器第二次解析时命名空间发生改变

2. MXSS(突变XSS)

MXSS使过滤器在清理用户payload时变得困难,因为相同的解析器连续解析两次payload会产生不同结果。

简单示例

</form><form id="inner">

第一次解析时位置会被纠正,第二次解析时因form不能嵌套而删除form,导致DOM树突变。

3. <caption>的突变机制

<caption>元素有特殊的解析行为:

  • 解析器找到<caption>开始标记时,会从开放元素堆栈中弹出元素,直到弹出<caption>元素
  • 后续内容会被弹出<table>,不考虑标签的命名空间

突变示例

<style>本来是svg命名空间的
<a id= 被当作html
</style> 被当作属性

突变后:

  • <a id=成为文本
  • </style>闭合<style>
  • 恶意``暴露

4. 节点扁平化(Node Flattening)

当嵌套标签达到上限(512层)时,浏览器会进行扁平化处理:

  • 将第513层的内容提取出来
  • 被扁平化元素的命名空间保持不变
  • 扁平化后的<a>标签不会被弹出(正常情况下嵌套的<a>会被弹出)

DOM Clobbering技术

基本原理

DOM Clobbering利用HTML元素污染JavaScript命名空间:

  1. 通过idname属性可以直接在JavaScript中访问元素
    <div id="test"></div>
    <script>console.log(test); // 输出DOM元素</script>
    
  2. 特殊标签(<embed>, <form>, ``, <object>)的name属性也可以直接访问

关键特性

  1. 无法覆盖已存在的全局变量:如window.name

  2. 特殊标签返回值

    • <base><a>返回URL值
    • 会隐式触发toString()调用
  3. 多层级污染

    • 多个同名id时,Chrome返回HTMLCollection
    • 可用于污染form.attributes等属性
  4. iframe污染

    <iframe name="test" src="..."></iframe>
    <script>
    setTimeout(() => {
      console.log(test); // iframe的window对象
    }, 100);
    </script>
    
  5. document污染

    • ``, <form>, <embed>等元素可以污染document属性
    • 甚至可以污染document.cookie

DOMPurify绕过实例分析

1. DOMPurify 3.1.0绕过

技术组合

  • <caption>突变
  • 节点扁平化

Payload结构

<table><caption></caption></table>

绕过过程

  1. 使用<table>抑制<caption>的弹出
  2. 通过扁平化将<caption>弹出
  3. 下次解析时将恶意代码(``)弹入HTML命名空间
  4. DOMPurify不会将</style>识别为标签,从而绕过过滤

2. DOMPurify 3.1.1绕过

新增防御

  • 深度嵌套检测(MAX_NESTING_DEPTH)
  • 超过深度限制强制删除节点

绕过技术

  1. DOM Clobbering劫持parentNode

    • 利用HTMLFormElement特性:浏览器自动将带有name属性的子元素挂载到父form对象
    • 导致f.parentNode返回undefined,破坏深度计数
  2. 组合<form>突变

    • 第一次解析后嵌套一层<form>
    • 再次劫持parentNode重新计数
    • 达到255*3=765层,触发扁平化

完整绕过流程

  1. DOMPurify首次解析,修正<form>结构并嵌套一层<form>
  2. 通过DOM Clobbering两次重新计数
  3. 触发扁平化,弹出<caption>
  4. 浏览器第二次解析,将恶意代码弹入HTML命名空间

防御措施与后续修复

DOMPurify的后续修复主要包括:

  1. 拦截DOM Clobbering攻击
  2. 增加正则表达式匹配MXSS模式
    • 注意:过度依赖正则可能引入新的问题

总结

通过分析这两次DOMPurify绕过,我们可以得出以下XSS过滤器绕过的通用思路:

  1. 利用解析差异

    • 命名空间混淆
    • MXSS突变
    • 浏览器与过滤器的解析不一致
  2. 污染关键属性

    • DOM Clobbering污染过滤器使用的变量
    • 劫持parentNode等关键属性
  3. 组合多种技术

    • 如同时使用节点扁平化和DOM Clobbering
    • 分层绕过不同防御机制

理解这些技术不仅有助于发现和修复安全漏洞,也能帮助开发者更安全地设计和使用XSS过滤器。

DOMPurify绕过技术深度解析:从命名空间混淆到DOM Clobbering 前言 在Web安全领域,XSS(跨站脚本)攻击是最常见的安全威胁之一。虽然使用成熟的XSS过滤器如DOMPurify是防御XSS的有效手段,但理解其绕过技术对于安全研究人员和开发者都至关重要。本文将深入探讨两种主要的DOMPurify绕过技术:命名空间混淆/MXSS和DOM Clobbering。 核心概念 1. 命名空间(Namespace)混淆 HTML文档中存在三种命名空间,元素在不同命名空间中的解析方式各不相同: HTML命名空间 :标准HTML元素 SVG命名空间 :SVG相关元素 MathML命名空间 :数学标记元素 关键点: <style> 在HTML中被当作文本,但在SVG或MathML中被当作HTML 命名空间混淆常发生在MXSS(突变XSS)后,浏览器第二次解析时命名空间发生改变 2. MXSS(突变XSS) MXSS使过滤器在清理用户payload时变得困难,因为相同的解析器连续解析两次payload会产生不同结果。 简单示例 : 第一次解析时位置会被纠正,第二次解析时因 form 不能嵌套而删除form,导致DOM树突变。 3. <caption> 的突变机制 <caption> 元素有特殊的解析行为: 解析器找到 <caption> 开始标记时,会从开放元素堆栈中弹出元素,直到弹出 <caption> 元素 后续内容会被弹出 <table> ,不考虑标签的命名空间 突变示例 : 突变后: <a id= 成为文本 </style> 闭合 <style> 恶意 `` 暴露 4. 节点扁平化(Node Flattening) 当嵌套标签达到上限(512层)时,浏览器会进行扁平化处理: 将第513层的内容提取出来 被扁平化元素的命名空间保持不变 扁平化后的 <a> 标签不会被弹出(正常情况下嵌套的 <a> 会被弹出) DOM Clobbering技术 基本原理 DOM Clobbering利用HTML元素污染JavaScript命名空间: 通过 id 或 name 属性可以直接在JavaScript中访问元素 特殊标签( <embed> , <form> , ``, <object> )的 name 属性也可以直接访问 关键特性 无法覆盖已存在的全局变量 :如 window.name 特殊标签返回值 : <base> 和 <a> 返回URL值 会隐式触发 toString() 调用 多层级污染 : 多个同名id时,Chrome返回HTMLCollection 可用于污染 form.attributes 等属性 iframe污染 : document污染 : ``, <form> , <embed> 等元素可以污染document属性 甚至可以污染 document.cookie DOMPurify绕过实例分析 1. DOMPurify 3.1.0绕过 技术组合 : <caption> 突变 节点扁平化 Payload结构 : 绕过过程 : 使用 <table> 抑制 <caption> 的弹出 通过扁平化将 <caption> 弹出 下次解析时将恶意代码( `` )弹入HTML命名空间 DOMPurify不会将 </style> 识别为标签,从而绕过过滤 2. DOMPurify 3.1.1绕过 新增防御 : 深度嵌套检测(MAX_ NESTING_ DEPTH) 超过深度限制强制删除节点 绕过技术 : DOM Clobbering劫持parentNode : 利用HTMLFormElement特性:浏览器自动将带有name属性的子元素挂载到父form对象 导致 f.parentNode 返回undefined,破坏深度计数 组合 <form> 突变 : 第一次解析后嵌套一层 <form> 再次劫持parentNode重新计数 达到255* 3=765层,触发扁平化 完整绕过流程 : DOMPurify首次解析,修正 <form> 结构并嵌套一层 <form> 通过DOM Clobbering两次重新计数 触发扁平化,弹出 <caption> 浏览器第二次解析,将恶意代码弹入HTML命名空间 防御措施与后续修复 DOMPurify的后续修复主要包括: 拦截DOM Clobbering攻击 增加正则表达式匹配MXSS模式 注意:过度依赖正则可能引入新的问题 总结 通过分析这两次DOMPurify绕过,我们可以得出以下XSS过滤器绕过的通用思路: 利用解析差异 : 命名空间混淆 MXSS突变 浏览器与过滤器的解析不一致 污染关键属性 : DOM Clobbering污染过滤器使用的变量 劫持parentNode等关键属性 组合多种技术 : 如同时使用节点扁平化和DOM Clobbering 分层绕过不同防御机制 理解这些技术不仅有助于发现和修复安全漏洞,也能帮助开发者更安全地设计和使用XSS过滤器。