Pages

Monday, May 25, 2015

Stealing Private Photo Albums from Google - Same Origin Method Execution


It has been a long time since I updated this blog since I focused on company blogs and Black Hat presentations for the last couple of years. It is time to kick in with details about the vulnerable Google instance I was demonstrating during my Black Hat EU presentation – Same Origin Method Execution. Using that instance and a payload of only alphanumeric characters and a dot I was able to hijack web functionality and thereby delete or steal private photo albums from Google Plus. A white paper is currently in review stages and will be released next week in my next post! 



Same Origin Policy and OAuth overview:

From time to time and for numerous reasons web applications interact with external third-party services. Some of these reasons may be importing information (e.g. import contact list), notifying events, getting delegated access to resources such providing a "Login with" systems and etc. 


Since an external service often (if not always) means a different security context, interacting with it will require overcoming Same Origin Policy (SOP). One of the most common interfaces for such third-party interaction these days is using OAuthentication dialogs. OAuth dialogs are hosted on third-party resource providers and are designed to ask the resource owner (the user) to allow/deny access from the website to the user's (resource owner) private resources. Once an answer is given the dialog will redirect (call) the user back to a callback page hosted on the website which asked for access, this page is called the “callback endpoint”. The purpose of the callback endpoint is to receive a token from the third-party and notify the requesting website. Although, since no best practice was created for building callback endpoints, the technique web developers use to save the token and notify the initiating page (call it back) is based entirely on developers’ habit and experience. Therefore many times callback endpoints end up prone to “Same Origin Method Execution”.


The vulnerable instance:

How many times have you noticed a "Login with", import contacts or friend lists from third-party service? 



As in many other websites Google Plus provides an option to import external contacts information from third-party resource providers such as Yahoo and Microsoft, Prior to the responsible disclosure the authorization process occurred as follows:

  1. Google Plus opened a pop-up for the OAuth dialog asking secure delegated access to the user’s external contact-list on Yahoo/Microsoft, to avoid losing the currently open document content.  
  2. After the user allows/denies the access, the dialog window document is redirected to a Google Plus callback endpoint consisting a callback URL parameter.
  3. The endpoint page uses the value provided by the parameter to notify the opener and deliver the access token (JSONP style).
Callback endpoint URL:

https://plus.google.com/c/u/0/auth?src=connectedaccounts&callback=callback_function_name&dest=microsoft2&[...snip...]
Accessing the callback endpoint URL directly responded with the following markup:

<html><body><script type="text/javascript">
window.opener.callback_function_name({"status":0,"token":"ItHumYWI[...snip..]","oauthstate":"1234","tokenid":"ToKeN1234","tokenexp":"0","gid":"401223423..","siteid":6,"displayname":"Ben Hayak","profileurl":"http://profile-url.com"});
</script></body></html>



Is that Cross-site scripting?

Of course the first thing to try here was Cross-site Scripting, although fortunately ;) in similarity to most callback endpoint implementations only a specific set of characters were allowed as a callback value specifically alphanumeric a dot and underscore [A-Za-z0-9_\.]. We can however use tricks like document.write, eval, etc, though as aforementioned in the markup above no user controlled input got inside the parenthesis so XSS is out of the question.


“NULL means no!?”

When accessing the callback endpoint directly the window.opener property is null, so it appears that the instance is not vulnerable. However there is a technique to control the opener window. The first idea that came to mind was to create a bug chain, by finding another page in google plus that will force opening a window with an arbitrary URL and combine that with the callback endpoint. Although finding such a page is a little too rare and will weaken the attack.


Window Objects in memory:

So let’s try different approach, I wanted to entirely control a window and set its document to whatever page in Google Plus and then tell the Google Plus callback endpoint “This is your opener window deal with it”. That’s exactly what you can acheive in “Same Origin Method Execution” all you need is a page that opens another window and a set of redirection and you can designated any window as the opener and start hijacking methods! The key element that makes this a successful approach is the nature of user agents. When a window document is being redirected its window object remains at the original allocated memory space, therefore any other window/s that holds a reference to it as “opener” can still use the reference to access the opener window even after the document changed/redirected.

If window A opens window B, the user agent will create a reference to window A as window.opener property of window B, when window A’s document would change (redirect) the reference won’t be cleared/deleted and window B can still use the opener property to reference it.

So the answer is pretty clear, all there’s left is to make a malicious html that will open another window via “window.open(‘/step1.html’)” , then redirect the first window to a designated document on Google Plus, and redirected the pop-up window to the callback endpoint. This way you can set any page as the opener of the callback endpoint.



Stealing Private Photo Albums with SOME:

I covered how to control an opener and abuse a vulnerable callback endpoint to execute any DOM/Javascript function at the context of the opener window. All there’s left is to choose a page containing an interesting web functionality and use only alphanumeric and a dot to assemble a payload that will hijack this web functionality (Similar to the impact of CSRF but we don’t mind CSRFTokens or other protective mechanisms).

The target document I used:

Google Plus can automatically backup private cell photos to Google servers in case G+ is installed as a mobile application on your phone. Fortunately Google created a service called “Google Picker” which allows users to share their photos, videos and documents stored in Google servers with third-party websites.

Now that I've designated a target document (Google Picker) I setup the attack page as follows:
  1. Set a page that opens two pop-up windows to control their opener.
  2. Redirect the page to Google Plus instance of Picker.
  3. Wait for the DOM to complete loading.
  4. Redirect the first pop-up to the vulnerable callback endpoint with a callback parameter set to: document.activeElement.parentElement.parentElement.parentElement.previousSibling.lastChild.click
  5. Redirect the second pop-up to the vulnerable callback endpoint with a callback parameter set to: pickerApp.V.Fa.PA which made the service send all the selected photos to my own domain.
  6. Send the malicious setup to a chosen victim (In my case Google Bug bounty responsible disclosure :)
The exploit resulted in hijacking the following JavaScript functions:
  1. window.opener.document.activeElement.parentElement.parentElement.parentElement.previousSibling.lastChild.click({JSONDATA});
  2. window.opener.pickerApp.V.Fa.PA({JSONDATA});


This flow could make any user send every single photo from his/her private album to my server.

BlackHat EU Video:




Additional use cases: 
I exploited the same instance to delete Google Plus timeline posts. 



Note: The issue is now fixed as it has been reported to Google bug bounty program.



Hope you enjoyed reading!





5 comments:

  1. Gr8,everything very clear expect one- @BenHayak can you explain,the Callback endpoint containing the token would be only valid for you,if its visited by the victim it will give error rather than the markup mentioned,how did you bypass that?

    ReplyDelete
  2. Thank you for your interest. For any session, the callback endpoint's markup led to the execution of a controlled callback function based on the "callback" parameter. Taking this into account, in case of another user's browser or a session validation, google generated similar markup with only slightly different argument (i.e. JSON data). Since SOME attack allows you to hijack functions that will ignore the arguments (i.e. submit,click,etc) the varied value didn't affect the vulnerability and thus the instance was vulnerable executed a method for every user session. For the sake of clarity this instance was perfectly exploited and it was acknowledge by the Google bug bounty program.

    Regards.

    ReplyDelete
  3. BEN ROCKS.. ITS MY DREAM TO GO IN GOOGLE HALL OF FAME ONCE.. I WILL SOON

    ReplyDelete