Earlier this week, I reported finding a cross-site scripting vulnerability on a facebook.com page. Last night, I asked a friend with contacts at Facebook to let their developers know directly, and the company responded quickly. I confirmed just after midnight that the hole is now patched, which means I will now share technical details.
The problem was a fairly typical XSS issue. In poking around various pages related to application permissions, I noticed that several URI parameters appeared in the source of the page, but Facebook did a good job of filtering out characters which could allow cross-site scripting. Further experimentation revealed that specifying various parameters on one page led to various error messages.
This specific page was
www.facebook.com/connect/prompt_permissions.php, a pop-up that can appear when an application requests extended permissions, such as read access to a user’s stream. A typical use of this page came by issuing a GET request with several parameters:
api_key (the API key of the requesting application),
next (the next URI to load),
channel_url (the cross-domain receiver file for communicating with Facebook),
locale (language), and
ext_perm (the specific extended permission requested).
For instance, if an application with API key
d41d8cd98f00b204e9800998ecf8427e wanted to access a user’s stream, it may issue a GET request to this URI:
Note that the extended permission parameter is simply the text
read_stream. When I tried setting it to a number, say
ext_perm=1, I received a page with this error message:
The application cannot ask you for permission 1
Sure enough, this error message was not filtered. I could then easily craft an XSS link. The trick only had two requirements: the user had to be logged into Facebook, and the API key had to match an application that the user had authorized. Since finding the API key of any third-party application is fairly trivial, one could easily target widely installed applications in an actual attack.
To demonstrate the possibilities of an XSS link, I set
ext_perm=%3Cscript%3Ealert(document.getElementById(↵ and saw this output:
Those experienced with Facebook code will recognize what can be accomplished with
post_form_id. Facebook uses this code to sign AJAX requests for all sorts of operations when someone uses pages on facebook.com, hence the list of activities I gave in my last post.
Of course, to perform such activities, an attacker would need the user’s Facebook ID, which does not occur in the source code of
prompt_permissions.php. But since we’re injecting code into a facebook.com page, browser security no longer prevents script access to iframes or XHR objects that reference other facebook.com pages, since none of them happen cross-domain. In fact, by setting
ext_perm=%3Ciframe+src%3D%22http%3A%2F2Fwww.facebook.com↵, one would see their profile image URI, which contains their Facebook ID.
Facebook did act swiftly to correct this problem, as they’ve done with previous cases, and I commend them for their response. However, I would once again note that many Facebook applications, including widely used ones, have this same type of vulnerability. An application cannot be exploited to the same degree as a facebook.com page, but it does allow a hacker to access profile information, send notifications, and publish stories on a user’s wall. Facebook’s recently announced privacy changes should eventually help limit profile access via hijacked applications, but many security issues still remain.