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 onerror=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>&lt;script&gt;alert(1)&lt;/script&gt;</a>

</body>

</html>

Console下输出:

document.getElementById('1').innerText

"<script>alert(1)</script>"

document.getElementById('1').innerHTML

"&lt;script&gt;alert(1)&lt;/script&gt;"

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'>&lt;script&gt;alert(1)&lt;/script&gt;</div>

<button class="btn1">text()测试</button></br>

<button class="btn2">html()测试</button>

</body>

</html>

点击btn1弹出:<script>alert(1)</script>

点击btn2弹出:&lt;script&gt;alert(1)&lt;/script&gt;

 

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 onerror=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