According to Facebook, Cursor-based pagination is the most efficient method of paging and should always be used where possible – a cursor refers to a random string of characters which mark a specific item in a list of data. Using certain access tokens with access to specific data it was possible to translate the cursor to an object ID that the session user doesn’t have access. In short, the cursors were leaking data.
In the paging documentation, https://developers.facebook.com/docs/graph-api/using-graph-api/v2.2#paging
When reading an edge that supports cursor pagination, you will see the following JSON response:
... Endpoint data is here
What isn’t explicitly stated in the documentation is that these cursors aren’t as random as they appear. These are in fact base64 encoded object IDs.
echo MTAxNTExOTQ1MjAwNzI5NDE= | base64 --decode
echo NDMyNzQyODI3OTQw | base64 --decode
I first arrived at this when inspecting this bug Tagged Places shouldn’t show paging params if no user_tagged_places granted
I was told they were aware of paging bug, nevertheless I decided to dig and see how far and what data I was able to get before they fixed it. Here are some major areas that I found
- Get the Ad Account and Group ID for any user
- Get Ad Account Group for any user
- Paging Cursors leaking data in GraphQL
The final one I believe was the kicker
GraphQL is an internal API Facebook has been using on their mobile applications for the past two years https://www.youtube.com/watch?v=9sc8Pyc51uU. I’ve been trying to find bugs in it for the last year D: D: D:
Using GraphQL, certain endpoints and the fact that cursors were leaking data I was able to determine
Uploaded Albums of any user (private “Only Me” included)
Execute a call to
Notice that the end cursor 533270280057 is the ID of an album set only to Friends for user 13608786
So by using the start_cursor, end_cursor and before params I can page through the data to identify IDs that are not available under privacy set to Public
Uploaded Videos of any user (private “Only Me” included)
A similar approach can be taken with any user’s uploaded videos
List of Video IDs
- 10100949657825037 <- Privacy Friends Only
- 10101702399754177 <- Privacy Only me
Time creation of all Timeline Stories of any user (private “Only Me” included)
Here I wasn’t able to deduce the IDs, as the cursor worked on the updated time of the post. Which turned out to be just as good
Say I (13608786) just updated by status privately (Only me) at 1419882632 unix time.
Using the endpoint from the querying user.
From previous bug reports, we know this is just base64 encoded
Decoding the start_cursor
echo MTQxOTg4MjYzMjox | base64 --decode
1419882632 matches the unix time from earlier.
Using the methods from Uploaded Videos and Uploaded Albums, and throwing it into a Python script, I can make a graph of any user’s activity based on updated time regardless of privacy level set.
- When does user X post the most/least?
- Which days?
- Which week?
Dec 29, 2014 4:01pm – Report Sent
Dec 30, 2014 4:53pm – Escalation by Facebook
Jan 30, 2015 2:27pm – Patched and bounty awarded of $7500 by Facebook