html中alert的用法_【渗透实战】通过HTML命名空间混淆绕过DOMPurify实现XSS

cb33fe1537ea3ed86d007e91ebdb9c55.png 点击上方 蓝字 关注我们 1概述 本篇文章主要介绍了如何绕过主流的HTML杀毒程序库DOMPurify。 DOMPurify是一款使用JavaScript编写的的HTML过滤库,可以处理来自用户的不受信任的HTML片段,删除可能导致跨站脚本攻击(XSS)的所有元素及属性。 具体的绕过方法如下所示:
<form><math><mtext>form><form><mglyph><style>math><img src onerror=alert(1)>
以上代码没有任何多余的元素,为了理解具体的绕过原理,我们需要先了解HTML规范的一些特性。 2DOMPurify用法

我们首先了解下DOMPurify通常是如何使用的。假设我们需要将html Markup中一个不受信任的HTML赋值给某个div,我们可以通过以下代码使用DOMPurify对该内容进行过滤,在赋值给div:

div.innerHTML = DOMPurify.sanitize(htmlMarkup)

在解析、系列化以及处理DOM树的过程中,上诉代码实际上会执行以下操作:

  1. htmlMarkup被解析为DOM树

  2. DOMPurify过滤DOM树(简而言之,这个过程中DOMPurify会遍历DOM树中的所有元素以及属性,并删除不在允许列表中的所有节点)

  3. DOM树被序列化回HTML标记

  4. 赋值给innerHTML之后,浏览器将再次解析HTML标记

  5. 解析的DOM树被附加到文档的DOM树中

来看个简单的示例。假设我们的初始标记为AB。在第一步中,它会被解析到以下树中: a616be598d793eebb5955e1ab32d2a34.png 然后,DOMPurify对其进行过滤后,剩下以下DOM树: 11e23c4e4a87ef1223d3365985c7e676.png 将其系列化后得到以下内容:
A<img src="1">B

这就是DOMPurify.sanitize返回的结果。然后浏览器在赋值给innerHTML时会再次解析该标记。

DOM树与DOMPurify处理过的树相同,然后将其附加到目标文档中。

简而言之,按顺序,以上过程可以总结成:解析->序列化->解析。直观上,大家可能会觉得系列化DOM树,并再次解析后,应该会返回初始的DOM树,但事实并非如此。

HTML规范中,关于系列化HTML片段的警告信息表明:如果使用HTML解析器进行解析,则此算法[序列化HTML]的输出可能不会返回初始的树结构。HTML解析器本身也有可能输出不往返序列化和重新解析步骤的树结构,尽管这种情况通常不符合要求。

值得注意的是,序列号-重新解析的往返操作并不一定能返回初始的DOM树,这也是造成mutation XSS(突变XSS) 的根本原因。通常这类情况是由于某些解析器、序列化器错误所导致的结果,但有两种特殊情况符合上诉警告信息所描述的场景。

3嵌套FORM元素

其中一种情况涉及FORM元素,它是HTML里非常特殊的一个元素,因为它本身不能嵌套。HTML规范中,明确指出FORM元素不可以是某个FORM元素的后继。

efac6ad12e274043b46c22145b9782c9.png

可以在任何浏览器中使用以下标记进行验证:

<form id=form1>INSIDE_FORM1<form id=form2>INSIDE_FORM2

该片段会生成以下DOM树:

0769cdd9d7857cb1e6588e7dc37b0e3b.png

第2个form在DOM树中会被完全忽略,就像从未存在过一样。

接下来是比较有趣的部分。如果我们继续阅读HTML规范,会发现它实际上给出了一个示例,该示例通过带有错误嵌套标签的标记成功创建了一个嵌套式表单。如下所示:

<form id="outer"><div>form><form id="inner"><input>

结果会生成以下包含一个嵌套表单元素的DOM树:

106005670ae9385aa364a36a4e1defd6.png

这并不是特定浏览器的bug,它直接来自HTML规范,并且在解析HTML的算法中也有描述。大意如下所示:

  • 当我们打开标签时,解析器需要记录该标签是由表单元素指针打开的。如果指针不为空,则无法创建表单元素。

  • 当我们结束标签时,表单元素指针始终设置为null。

因此,回到以下片段:

<form id="outer"><div>form><form id="inner"><input>

首先,表单元素指针设置为id =“outer”,然后开始解析div,碰到闭合标签后,表单元素指针会被设置为null。由于指针为null,因此可以创建下一个id=“ inner”的表单。并且由于我们当前位于div内,因此可以成功创建一个嵌套式表单。

现在,如果我们尝试序列化生成的DOM树,将获得以下标记:

<form id="outer"><div><form id="inner"><input>form>div>form>

值得注意的是,该标记将不包含任何错误嵌套的标签。当再次解析标记时,将创建以下DOM树:

ffee7591b1fae766daabe521a90fdff4.png

综上,我们证明了经过序列化、重解析后并不一定能返回原始的DOM树。更有趣的是,这基本上是一种符合规范的突变。

当我发现这一点后,便意识到可以通过某种方式滥用这种特性以绕过HTML过滤器。思考了很久后,偶然间发现HTML规范中的另一个异常点。在继续讨论该问题前,我们先来了解下HTML规范中的潘多拉魔盒:外部内容(foreign content)。

4外部内容

外部内容就好比一把瑞士军刀,可以用来破坏解析器及过滤器。该方法曾被我用来绕过DOMPurify以及Ruby过滤库。

HTML解析器可以使用三个命名空间元素创建DOM树: 

  • HTML命名空间(http://www.w3.org/1999/xhtml) 

  • SVG命名空间(http://www.w3.org/2000/svg) 

  • MathML命名空间(http://www.w3.org/1998/Math/MathML) 

默认情况下,所有元素都位于HTML命名空间中。但是,如果解析器遇到或元素,则它将分别“切换”到SVG和MathML命名空间。这两个命名空间都会产生外部内容。

在外部内容中,标记的解析过程与普通的HTML不同。尤其是在解析


思考以下标记:

<style><a>ABCstyle><svg><style><a>ABC

它将被解析成以下DOM树:

loading.gif

备注:从现在开始,本文DOM树中的所有元素都将包含一个命名空间。因此html style意味着它是HTML命名空间中的


最终的DOM树证实我的观点:html style仅包含文本内容,而svg style会像普通元素一样被解析。 


继续分析,我们可能会猜想:如果我们位于或内部,则所有元素也都会位于非HTML命名空间中。但事实并非如此,HTML规范中包含MathML文本集成点和HTML集成点,这些元素的子元素都具有HTML命名空间(但某些情况除外)。 


思考以下示例:

<math><style>style><mtext><style>style>

它将被解析成以下DOM树

loading.gif

请注意观察,在MathML命名空间中,style元素是如何成为math的直接子元素的,而mtext中的style元素则位于HTML命名空间中。这是因为mtext是MathML文本集成点,并使用解析器切换命名空间。


MathML文本集成点包括:


  • math mi

  • math mo

  • math mn

  • math ms


HTML集成点包括:


  • math annotation-xml(如果其具有encoding属性,并且属性值等于text/html或者application/xhtml+xml)

  • svg foreignObject

  • svg desc

  • svg title

我之前一直以为MathML文本集成点或HTML集成点的所有子级,默认情况下都具有HTML命名空间,但事实并非如此。HTML规范指出,默认情况下,MathML文本集成点的子级都位于HTML命名空间中。但有两种例外情况:mglyph和malignmark。只有当它们是MathML文本集成点的直接子级时,才会发生这种情况。


我们可以通过以下标记进行检查:

loading.gif

注意,作为mtext的直接子元素的mglyph在MathML命名空间中,而作为html元素的子元素的mglyph在HTML命名空间中。


假设我们想确定一个“当前元素”的命名空间。此时我已经制定了一些经验法则:


  • 非满足以下条件,否则当前元素将位于其父元素的命名空间中

  • 如果当前元素为或,且其父元素在HTML命名空间中,则当前元素分别位于SVG或MathML命名空间中。 

  • 如果当前元素的父元素是HTML集成点,则当前元素位于HTML命名空间中除非它是或。 

  • 如果当前元素的父元素是MathML集成点,则当前元素位于HTML命名空间中,除非它是,,或。

  • 如果当前元素是

    ,,
    ,,
    ,, ,,
    ,,,,,
  • ,,,,,

    或,并定义了color,face或size属性,那么堆栈上的所有元素将关闭,直到看到MathML文本集成点、HTML集成点或HTML命名空间中的元素为止。然后,当前元素也会位于HTML命名空间中。

当我在HTML规范中找到mglyph时,马上就意识到这是滥用html form突变以绕过过滤器的方法。
5
DOMPurify绕过


因此,让我们回到绕过DOMPurify的有效载荷上:
有效载荷利用了错误嵌套的html form元素,并且包含mglyph元素,它将生成以下DOM树: loading.gif 该DOM树是无害的,所有元素都在DOMPurify的允许列表中。值得注意的是,mglyph位于HTML命名空间中,而看起来像XSS有效载荷的代码片段只是html style中的一个文本。由于存在一个嵌套的html form,所以我们可以确定此DOM树将在重新解析时发生突变。


因此,这里DOMPurify不会执行任何操作,将返回序列化的HTML:



该片段具有嵌套的Form标签。因此,当其赋值给innerHTML时,将被解析成以下DOM树: loading.gif 所以现在没有创建第二个html表单,mglyph为mtext的直接子元素,这意味着它位于MathML命名空间中。因此,style同样位于MathML命名空间中,且其内容不会被视为文本来解析。然后, math>会闭合元素,现在会在HTML名称空间中创建img,从而导致XSS。


6总结


以上绕过方式可以总结成以下几点:


DOMPurify的典型使用方法致使HTML标记被解析两次

HTML规范中存在特殊情况,可以创建嵌套form元素。但是,在重新解析时, 第二个form会被忽略

mglyph和malignm ark 是HTML规范中的特殊元素,即使默认情况下其他所有标签都位于HTML命名空间中,但如果它们是MathML文本集成点的直接子元素,那么它们也将位于MathML命名空间中。

结合以上几点,我们可以创建一个具有两个form元素和mglyph元素的标记,这些标记最初位于HTML命名空间中, 但是在重新解析时位于MathML命名空间中,从而使后续的style标签的解析方式有所不同,导致XSS。


end



loading.gif


好文!必须在看
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值