My XSRF exploit of build.phonegap.com

PhoneGap Build is a nice service from Adobe. You upload your PhoneGap application and it builds Android, iOS packages on the cloud. PhoneGap Build also provides an API for it’s service. I used the API to create pgbuild to automate the upload of the PhoneGap application source and download the built packages.

The exploit I found has to do with the JSONP API (scroll to the end of the api page). Using the exploit, one can read a logged in user’s registered apps, initiate a rebuild of the apps etc.

The exploit itself is fairly trivial, if you understand how browsers work. If you just want to know the exploit scroll to the very end. The goal of this post is to provide a high level overview of how browser security and xsrf-style exploits work. Let’s start with Same Origin Policy.

Same Origin Policy

Browsers are very anal (for good reasons) about what resources a web page can access. A web page served from foo.com can only access resources from foo.com. This principle is called Same-Origin Policy. The policy is applied to JavaScript, HTML, file:// protocol and each has it’s exceptions. For HTML, the img tag, for example, can have url’s that point to an arbitrary website. The script tag and style tag can have the src attribute set to content from arbitrary websites.

Cookies

Whenever the browser makes a request, it sends across the cookies that have been set for that domain. This is true even for the cross-domain img and script tags. For example, let’s assume that you were logged into your bank. Without logging out, if you navigate to evil.com which has img src=”http://bank.com” somewhere inside it, your browser will make a request to bank.com with the cookies of bank.com set. The bank might provide personal information about you (since cookies have been provided) and dutifully serve the web page. It’s as if you had typed bank.com in the url bar of the browser (smart browsers will set the HTTP accept header when requesting an image and smart servers will check that header, but that is another story). Thankfully, the response from bank.com is most likely some HTML and the browser will fail to load it as an image. Your bank content is thus not introspectable by evil.com. The same goes for the script tag. Since HTML cannot be executed as a script, the browser just ignores the response.

Same-Origin policy is restrictive

One of the main reasons tags like img, script are excused from same-origin policy is for ease of development of the web. It’s nice to be able to link to external images and to reuse scripts from another domain that one owns. However, the same-origin policy is restrictive for sharing “data” between domains. Data is usually offered by web services as XML or JSON. Unfortunately, the popular way for web pages to fetch data – XHR (aka AJAX), also follows the same origin policy.

JSONP – Sharing data across domains

JSONP (JSON with padding) takes advantage of the fact that script tags can point to different domains. A web page author issues a cross-domain API call that returns JSON data as part of the script tag. For example,

<script src="service.com/api/get_posts_as_json"> </script>

The result of the above call is JSON which not valid JavaScript. The browser will ignore the above just like it ignored HTML in the previous bank example. We need to somehow make the output of above a valid JavaScript. With JSONP, one writes:

function processPosts(result) { ... }

<script src="service.com/api/get_posts_as_json?callback=processPosts"></script>

service.com sees the callback parameter and responds with processPosts(json_result) which is valid JavaScript. All we have to do is to provide an implementation of processPosts function somewhere in our web page.

The exploit

First thing to notice about the build.phonegap.com’s JSONP API is that it uses build.phonegap.com as it’s domain. This is interesting because the main website is also hosted on build.phonegap.com. This means that if a user is logged into the build.phonegap.com, a evil web page can issue JSONP API calls with the user’s credentials.

Usually, “APIs” are hosted in a domain separate from the website. For example, api.service.com for API access and www.service.com for the website. This means that api.service.com can safely have JSONP support since the cookies from www.service.com and api.service.com won’t mix.

If APIs and the websites are hosted in the same domain distinguished only by path, then one needs to be more careful. For example, http://build.phonegap.com/api provides API access and http://build.phonegap.com/apps provides the website. Cookies do have a ‘path’ attribute which can be used to tell the browser that different paths in same domain don’t mix. However, http://build.phonegap.com/apps set the cookies in the ‘/’ path. This meant that cookies will be provided for http://build.phonegap.com/api too.

Do you see the exploit now? All I had to do was to use the JSONP api to access user information. In case you were wondering, exploits where evil websites steal credentials of a user or impersonate the unwitting user are called Cross-Site Request Forgery attacks (CSRF or XSRF).

Sample exploit code

Once you are logged onto build.phonegap.com, navigate to the links below:
Example 1 – List Your Apps
Example 2 – Rebuild Your Apps (WARNING: Don’t Click unless you know what you are doing)

Possible fixes

Since I don’t have access to build.phonegap.com code, I can only guess a few possible fixes

  • Strip out cookies when processing JSONP and rely on the HTTP auth header or the API token.
  • Make the website set cookies with path as /apps.
  • Remove JSONP support and add support for CORS.

Move api access to a separate domain.

Current status

I informed Adobe before this blog post and they quickly fixed the problem (I haven’t checked how).

Update

Adobe has acknowledged me on their web site, thanks Adobe!

twitter