DOM Clobbering 拓展xss攻击面

前言:

这篇文章在很多博客上看到,觉得很有意思,学习一波;

正文:

其实通过细读陆队和一些其他的文章,Dom clobbering xss的本质就是利用id或者name去对全局变量的劫持覆盖;在目标站点的script代码中如果有相应的引用;就会造成漏洞;

先来看一下如何进行劫持;简单看一下图:

oZtNlR.png

通过这个图不难理解,我们成功劫持掉了cookie;document.cookie已经成功被我们的标签给覆盖了;但是如果这里写入的是<img id=cookie>则无法success;具体的原因可以看下图:

oZU8z9.png

这里显然易见我们可以通过id和name在document和window对象下创建一个新的对象;但是值得注意的是使用id的时候是无法被document来获取到的;需要用document.getElementById来进行获取所以这也是为什么当标签设置为<img id=cookie>的时候无法success的原因;

oZUOoT.png

劫持函数

oZDoSU.png

可以很清楚的看到利用form和input进行覆盖函数,导致在后续的代码中无法识别函数;

利用

要想利用这个漏洞还是需要进一步的进行深入,因为当前测试的结果返回的仅仅是一个对象,无法返回string;当然我们可以测试一下强制类型进行转换;

oZwuZt.png

如果进行强制类型转换,那么会回显为字符类型的object HTMLImageElement;用此去进行xss攻击显然是不现实的;

oZ0N1e.png

所以现在的想法就是如何去将其内容控制为返回的字符串;

那么就需要去进行遍历,遍历到合适的标签让我们可以返回相应的string;

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

用此代码去进行遍历,最后回显的结果为两种标签对象:

oZD8zD.png

对应的就是area和a标签;利用这两种标签对象就可进行返回String;从而可进行xss;看一个测试图:

oZ6IE9.png

证明是在a标签下可以利用href进行强制类型转换;至于area,换个标签就可;也可以实现弹窗;是会返回其href属性的value;

两层利用:

上面的实验仅仅是建立在一层的基础之上,那么如果是两层甚至多层又如何触发xss?简单分析一下;

两层的一般形式就是x.y这种,需要去更内部去触发;

这里两层可以引入name属性去进行承接;

HTMLCollection

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

oZRM8I.png

就可实现双层的Dom clobbering点利用;area标签是一样的原理;

HTML Relationships

在不用HTMLCollection的情况下也可采用html的层级关系来实现层级访问;整体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

这也是为什么第四张图劫持函数可以成功的原因;

用此构建成x.y;结合之前的返回string;就可以构造出相应的payload;

1
2
3
4
<form id=x>
<button id=y>down</button>
<script>
console.log(x.y);//<button id="y">down</button>

这种方法无法拿到其value;也算是有点欠佳的方法;尽管有些标签可以返回value;但是也是需要借助value属性才可返回,比如:

om30u6.png

所以其利用方式也是很局限的;实际中还是最好利用两层同id去造成HtmlCollection然后利用name承接,进行a href的利用较好;

三层利用

三层利用的关系就需要我们结合上面两种方式;

1
2
3
4
<div id="x">
</div>
<form id="x" name="y"><output id=z>down</output>
</form>

理解一下也不难,先利用同id去造成HtmlCollection去获取第二层,然后在利用form和output来进行第三层的获取;

omYVrq.png

不过缺点和之前一样,因为套了form->output所以也是无法直接拿到value;

三层以上访问

三层以上的访问可以通过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>

这种方式可以直接拿到value;

如果不想通过setTimeout进行延时的话,还可以通过<style>标签或者<script>等进行网络请求造成延时,使得iframe加载完毕;因为在iframe没有记载完毕的时候他的document和加载完之后的document是不一样的;

1
2
3
4
5
6
<iframe name=a srcdoc="
<iframe srcdoc='<a id=c name=d href=cid:Clobbered>test</a><a id=c>' name=b>"></iframe>
<style>@import 'http://example.com';</style>
<script>
alert(a.b.c.d)
</script>

任意属性的测试

这里可以用以下的脚本进行fuzz;fuzz一下看看有没有字符串类型的属性可以让我们使用;

1
2
3
4
5
6
7
8
9
10
11
12
13
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 {
props.push(html[i]+':'+prop);
}catch(e){}
}
}
}
console.log([...new Set(props)].join('\n'));

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

1
2
3
4
5
6
<a id=x href="http://Clobbered-username:Clobbered-Password@a">
<script>
alert(x.username)//Clobbered-username
alert(x.password)//Clobbered-password
</script>

除此之外,还有其他的一些标签结合属性名称,比如a:title,也都挺有趣的;

1
2
3
4
5
<a id=x title="x:alert(1)">
<script type="text/javascript">
console.log(x.y);//'x:alert(1)'
</script>