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.
Nice find and post .
Nice !!
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.