技术社区
安全培训
技术社群
积分商城
先知平台
漏洞库
历史记录
清空历史记录
相关的动态
相关的文章
相关的用户
相关的圈子
相关的话题
注册
登录
从两次DOMPurify绕过探索绕过xss过滤器之法
le0n
发表于 四川
WEB安全
701浏览 · 2025-06-10 06:33
返回文档
前言
抛开转义的绕过不谈,在网站中注入用户html代码的需求是长期存在的,所以使用安全的xss过滤器非常必要。从安全考虑,使用自写的过滤器总是十分危险的,而市面上已经有比较成熟的过滤器比如DOMPurify(当然在其配置错误时也会存在安全问题,这并非我们今天的重点)。这篇文章主要介绍命名空间、MXSS、Dom cloberring的基础概念和用法,最终会解释两次DOMPurify绕过的原理。通过这篇文章,我们能洞见绕过xss过滤器的常见手段,从而在相关题目、场景下有相应思路。
命名空间混淆
存在三种命名空间,元素在它们中解析的方式各不相同。
<html>
HTML namespace
<svg>
SVG namespace
<math>
MathML namespace
比如
<style>
中的内容在html中被当作text,但是在svg或MathML中被当作html,命名空间混淆其实就是在mxss突变后(浏览器第二次解析)命名空间发生改变。
这个Dom浏览器的网址在这里,十分好用的一个工具,甚至内置了DOMPurify各个版本,允许自定义配置:
https://yeswehack.github.io/Dom-Explorer/
命名空间集成点
html集成点允许在其他命名空间插入html:
比如:
在svg中通过
<title>
标签插入了一个table
MXSS
mxss即突变xss让过滤器在清理用户payload时十分困难。比如对于DOMPurify是专门用来做清理的库,所以它的解析行为和浏览器一致,(如果误用beautifulsoup这样的库就会因解析不一致问题而无法做到安全地过滤)。
但mxss的payload被相同的解析器连续解析两次,却会出现不一样的结果。
一个简单例子
第一次解析时
</form><form id="inner">
位置反了,被解析器换过来,而第二次解析时又因为
form
不能嵌套而删除form,解析器十分想纠正dom树,但同时也造成了突变。
<caption>的突变
<caption>
可以用来做类似弹出的操作
根据
标题插入模式
定义,如果解析器找到
<caption>
开始标记,则需要从
打开元素堆栈
中
弹出元素
,直到弹出
<caption>
元素。
这个过程有些难理解,详细原理可以看
in caption insertion mode
。大概是从
<caption>
开始,后面被弹出的元素在下面叠加,直到下一个
</caption>
,然后再后面的内容直接被弹出了
<table>
。
而这种弹出与扁平化就完全相反了,
它不会考虑弹出标签的命名空间
,因为它们只是开放元素堆栈的一部分。
<style>
本来是svg命名空间的,
<a id=
被当作html,
</style><img src=x onerror=alert()>
被当作属性,但被弹入html命名空间后
<a id=
成了text,
</style>
直接闭合了
<style>
,使恶意的
<img>
露出来。
但是这还只发生了一次突变,还是会被过滤器轻松识破,但如果嵌套一层其他突变,在这次突变后caption还没发生突变,恶意结点就能继续伪装起来。
Node flattening嵌套结点扁平化
当我们嵌套上限数量(512)的标签时,浏览器不会继续解析,而会进行flattening。
其实就是把第513层的内容拿出来,避免超过512层,但有趣的是,拿出的元素的命名空间竟然没有改变。
同时,如果a标签肯定是不允许嵌套的,直接嵌套
<a>
会被弹出:
但是被扁平化的a不会再被弹出。
DOM clobbering
原理
你在 HTML 里面设定一个有 id 的元素之后,在 JavaScript 里面就可以直接访问到它:
直接用
btn
也可以,因为当前的 scope 找不到就会往上找,一路找到
window
。
除了 id 可以直接用
window
访问到以外,
<embed>
,
<form>
,
<img>
跟
<object>
这四个标签用 name 也可以存取到:
DOM clobbering也是攻击xss过滤器的一个重要特性,
利用这个特性我们可以覆盖一些值来影响过滤器,但是
想攻击的变量已经存在的话,用 DOM 是覆盖不掉的,比如说
window.name
全局变量一直存在,无法覆盖,或者以下情况:
但是如果过滤器中有这样赋值:
注意到window.conf并非一开始已存在的全局变量,我们使用DOM clobbering覆盖掉就会使其为非预期的初值。
同时我们也要注意,之前__toString后的值都是
object,十分不干净,在某些需要用到其值时无法利用
有两个特殊的标签
<base>
跟
<a>
:会返回url值
还有这里的情况也隐式地触发了toString(),ctf题目常有污染此url使bot访问指定页面的考法
多层级DOM Clobbering
HTMLCollection
有多个同名id时,chrome传出
htmlcollection(在 Firefox 上面并不会回传
HTMLCollection
),此时多出了一个层级:
form
这样就污染了form.attributes,或者说是过滤器中的el.attributes(它本来应该访问到form的属性map的,但Dom cloberring的优先级竟然比它高),常用来干扰xss过滤器对属性的过滤(biosctf2025#QoutesApp)。
在这种情况下使用form还有一个优点就是它本身就可以自动加载js:
如果我们把
<form>
跟
HTMLCollection
结合在一起,就能够达成三层:
iframe
当你建了一个 iframe 并且给它一个 name 的时候,用这个 name 就可以拿到 iframe 里面的
window
,所以可以像这样:
这边之所以会需要 setTimeout 是因为 iframe 并不是同步加载的,所以需要一些时间才能正确抓到 iframe 里面的东西。
污染 document
有几个元素搭配name能影响document,而且优先级很高,如:
<img>
<form>
<embed>
,甚至document.cookie都能被他们污染:
在 CTF 中,有时能和 prototype pollution 一起使用:
document.cookie会指向<img name=cookie>,并且这里调用其_toString方法,toString被我们污染了,所以返回token=1。
实例:两个版本DOMPurify的绕过
第一个版本主要是基于mxss进行绕过,而第二个版本则是通过Dom cloberring绕过了fix增加的检测,可以说是非常好的例子了。
DOMPurify3.1.0
用到上面提到的caption突变和嵌套结点扁平化突变:
<table><caption></caption></table>
我们先加上
<table>
来抑制
<caption>
的弹出,然后再通过扁平化把
<caption>
弹出,再下次解析时就能和上边一样把
<img src=x onerror=alert()>
弹入html命名空间。而DOMPurify不会把
</style><img src=x onerror=alert()>
识别成标签,所以就不会对它进行过滤。
第一次解析
第二次解析
DOMPurify3.1.1
看出增加了deep-nesting深度探测
超过MAX_NESTING_DEPTH就强制删除currentNode
看完整代码,while遍历每一层标签,执行用户自定义的hook,然后sanitize这个标签,设置顶层的__depth为1,以后都通过parentNode来获取上一次标签,再通过__depth属性来记录层数,如果超过最大层数就移除标签。那如果我们劫持parentNode属性呢?
类似于DOM Clobbering,我们用HTMLFormElement 的特性:浏览器会自动将
<input>
、
<select>
、
<textarea>
等带有
name
属性的元素挂载到父级
<form>
对象上。
我们发现f.parentNode成了undefined,直接导致了重复计数。
但是不够,差两层
(255*2=510<512)
,同时没有其他标签有这种特性了(
于是用我们之前提到的
<form>
的突变,让第一次解析后再嵌套一层
<form>
,然后又劫持一次parentNode就能再重新计数一次,达到
255*3=765
层
第一次DOMPurify解析,修正
<form>
的结构,嵌套一层
<form>
重新计数两次,达到flattening,扁平化
<caption>
标签,第二次浏览器解析弹出栈中的
<style><a title="</svg></style><img src onerror=alert(1)>"></a></style>
到html中解析成恶意代码,从而绕过DOMPurify。
DOMPurify的后续fix
主要是拦截Dom cloberring和增加了许多正则匹配(用正则来过滤mxss有着未雨绸缪的感觉),但是使用正则也会带来许多问题,可以详细看后续发现的绕过。
1
人收藏
1
人喜欢
转载
分享
0
条评论
某人
表情
可输入
255
字
评论
发布投稿
热门文章
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
近期热点
一周
月份
季度
1
从零掌握java内存马大全(基于LearnJavaMemshellFromZero复现重组)
2
突破网络限制,Merlin Agent助你轻松搭建跳板网络!
3
从白帽角度浅谈SRC业务威胁情报挖掘与实战
4
基于规则的流量加解密工具-CloudX
5
从0到1大模型MCP自动化漏洞挖掘实践
暂无相关信息
暂无相关信息
优秀作者
1
T0daySeeker
贡献值:38700
2
一天
贡献值:24800
3
Yale
贡献值:21000
4
1674701160110592
贡献值:18000
5
1174735059082055
贡献值:16000
6
Loora1N
贡献值:13000
7
bkbqwq
贡献值:12800
8
手术刀
贡献值:11000
9
lufei
贡献值:11000
10
xsran
贡献值:10600
目录
前言
命名空间混淆
命名空间集成点
MXSS
一个简单例子
<caption>的突变
Node flattening嵌套结点扁平化
DOM clobbering
原理
多层级DOM Clobbering
HTMLCollection
form
iframe
污染 document
实例:两个版本DOMPurify的绕过
DOMPurify3.1.0
DOMPurify3.1.1
DOMPurify的后续fix
转载
标题
作者:
你好
http://www.a.com/asdsabdas
文章
转载
自
复制到剪贴板