译者: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参数提供的网址。
当你看到这样的行为时,想到的第一个漏洞是什么?
你猜对了(也许没猜对),这是一个开放的重定向(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
服务器返回的响应如下:
正如你所看到的,我可以通过使用data协议来实现使用JavaScript代码(window.location)的重定向。
浏览器差异分析
有人可能会想,为什么我不直接弹个窗,并向uber提交了一个XSS漏洞。
这是因为JavaScript代码没有运行在auth.uber.com域上。然后,服务器返回的301响应使用Location头执行重定向,页面的origin发生改变,在这种情况下,origin为空。
还有一点很重要,那就是上述重定向技术只能在Firefox浏览器中使用,在Chrome中不起作用。
Chrome会阻止此请求,原因有二:
Chrome不支持使用Location头重定向到data协议
Chrome浏览器与Firefox解析页面的方式不同,不会容忍data协议中的语法错误(data:accounts.uber.com;html/text – 想起来了吗?)
使用Location头重定向到data协议时,Chrome提示以下错误消息:
复制粘贴到网址栏:
所以,本writeup的第一个提示:在某些情况下,不同的浏览器的行为是不同的,所以如果你的payload在某个浏览器中不起作用,并不表示其他浏览器也不会执行它。
在实现了开放重定向之后(此类漏洞并不在uber赏金计划的范围之内),我暂时放弃了这个攻击向量,去忙其他事情了。
漏洞深入挖掘
几个星期过去了,我又无聊了,所以决定再回过头看看这个漏洞。
然后,当我用上述链接登录到我的uber帐户时,我注意到之前我错过了一些东西,当您尝试访问没有活动会话的URL时,重定向过程是不同的。
完成登录过程后,服务器响应如下所示:
我怎么会错过这个行为?可能是因为我在一个活动的会话中做了大部分重复工作,结果总是导致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);
我得到了这个弹窗:
在页面上快速查看浏览一下源代码,一切看起来似乎都很正常…
但是为什么没有弹窗呢?
查看我的请求历史记录后,我注意到了这个:
内容安全策略CSP…就是这个阻止弹窗了?但是CSP头在哪里?并未出现在这个响应中:
在历史记录中快速搜索一下,得到如下结果:
我做了一个快速测试,以确认这确实是导致没有弹窗的原因。通过从响应中删除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后,我得出了一些结论:
对我们很重要的一部分内容如下:
我不能使用内联脚本,因为会有随机的nonce值(每个请求都会变),所以不要使用:
<script>alert(1);<script>
我唯一的机会是找到一个由CSP批准的域名,同时能够将我的输入作为javascript返回。
但是你有什么机会找到这样的东西?显然机会很多。
https://en.wikipedia.org/wiki/JSONP
经过几分钟Google后,我发现了这个:
最终的链接为:
https://app-lon02.marketo.com/index.php/form/getKnownLead?callback=alert(document.domain);//
完美!
快速组装新的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
登录然后……
但是只有在用户未登录的情况下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,能够学到很多东西。
永不放弃;)