PyTux

Trips of a curious penguin.

Hello, time traveler! You are reading an article that is almost ten years old. The world has changed, and so have I and my opinions. There is a good chance what's below is not current, correct, or secure anymore, and maybe it never was. This page is preserved because I am an archivist at heart, but you have been warned.

A bug worth 4200$

tl;dr I found a vulnerability in Facebook that allowed anyone to see the “strength” of all his friends’ friendships via a FQL query authenticated with an iOS app access token. The reporting experience has been smooth and not too slow, and the bounty generous.

Facebook Bug Bounty

Early this year Facebook started showing “last active” times above chats in the iOS app but not on the website (they now have rolled out it also in the website) and I started wondering if I could get that info also from my browser and maybe wrap it up in a extension or something (turns out visiting m.facebook.com would have been enough, but emh…). So, I started Burp Proxy with SSL interception, trusted the Burp CA on my iPad and started inspecting the Facebook iOS app calls.

They are mainly FQL multiqueries, and one in particular caught my eye (queries parameter URLDecoded for readability):

GET /method/fql.multiquery?sdk=ios&queries={"top_friends":"SELECT uid, online_presence, is_pushable, has_messenger, last_active FROM user WHERE uid in (SELECT uid2 FROM friend WHERE uid1=me() order by communication_rank desc LIMIT 15)","online":"SELECT uid, is_pushable, has_messenger FROM user WHERE online_presence ='active' AND uid IN (SELECT uid2 FROM friend WHERE uid1=me())","favorites":"SELECT uid, online_presence, is_pushable, has_messenger, last_active FROM user WHERE uid in (SELECT favorite_id FROM messaging_favorite WHERE uid=me())","favoriteRanking":"SELECT favorite_id, ordering FROM messaging_favorite WHERE uid=me()"}&sdk_version=2&access_token=REDACTED&format=json&locale=it_IT

These are a bunch of queries against the user table based on JOINs on the messaging_favorite and friend tables. This friend table is interesting: it holds all the friendships with their details, for example communication_rank. By fiddling around a bit with it I guessed that it is a rank of the “strength” of uid1’s friendship with uid2, probably the thing that decides who’s shown in your chat sidebar even when he’s offline.

The docs tell us that “[the access token owner is] the only user that this table can be queried for, the friends of friends cannot be retrieved”. Hmm. Should we trust the docs? Turns out, NO! ;)

Authenticating with our iOS app access token we can issue queries like

SELECT uid2, communication_rank FROM friend WHERE uid1=1289695510 ORDER BY communication_rank DESC

for an arbitrary friend’s uid1 (the above is the one of my favorite guinea pig, Anna) instead of just for ours. That queries return output like

{ "uid2": "1234567890", "communication_rank": "2.4456558227539" },
{ "uid2": "4242424242", "communication_rank": "1.68115234375" },
{ "uid2": "1337133713", "communication_rank": "1.602783203125" },
...

that tells us with which users the target contacts most, maybe the most interesting and private bit of information after message logs. We don’t even need to be friends of that users!

Reporting and patching

The first time I reported the issue through their form it got dismissed, probably also because I didn’t explain it very well. After offering a real world example they confirmed and quickly patched it.

The report netted me a generous 4200$ bounty (delivered as a cool prepaid card, that is worth the withdrawal fee) and a mention on their thanks page (a great, great CV builder), plus some pleasant compliments.

White Hat Bounty

Facebook offers a great example of how to run a Bug Bounty Program: assure the researcher that he’s being heard, offer him a direct contact (you get ticket-bound Reply-Tos) by skilled people, reward him generously (even if, believe me or not, this is the most optional point), publicly thank him and finally let him feel that his work is appreciated.

Vulnerability timeline

1 gen 2013 Vulnerability discovered and bug report filed
7 gen 2013 Test accounts POC sent
18 gen 2013 First dismissing reply received
19 gen 2013 Better explanation and real-users POC sent
28 gen 2013 Vulnerability confirmed and acknowledged
~ 30 gen 2013 Vulnerability fixed
1 feb 2013 Bounty awarded
18 mar 2013 Bounty paid
26 apr 2013 This public disclosure