New Facebook Photo Hacks
February 11th, 2009 at 10:14 UTC by Joseph Bonneau
Last March, Facebook caught some flak when some hacks circulated showing how to access private photos of any user. These were enabled by egregiously lazy design: viewing somebody’s private photos simply required determining their user ID (which shows up in search results) and then manually fetching a URL of the form:
www.facebook.com/photo.php?pid=1&view=all&subj=[uid]&id=[uid]
This hack was live for a few weeks in February, exposing some photos of Facebook CEO Mark Zuckerberg and (reportedly) Paris Hilton, before the media picked it up in March and Facebook upgraded the site.
Instead of using properly formatted PHP queries as capabilities to view photos, Faceook now verifies the requesting user against the ACL for each photo request. What could possibly go wrong? Well, as I discovered this week, the photos themselves are served from a separate content-delivery domain, leading to some problems which highlight the difficulty of building access control into an enormous, globally distributed website like Facebook.
Here’s an example “public link” of a photo of me in my office:
http://www.facebook.com/photo.php?pid=34947682&id=210132
Posting this link shouldn’t be a privacy problem, as you shouldn’t be able to see a photo by following this link unless you’re in my network of friends. Facebook promotes this view by telling you at the bottom of the page that you can safely send this link out to friends, and in fact such links are posted all over the web if you search for them. Access rights are enforced on the Facebook page, so knowing this link doesn’t reveal the photo. But, unfortunately, the actual photo file is embedded in the page with the address:
http://photos-c.ak.fbcdn.net/photos-ak-sf2p/v646/41/83/210132/n210132_34947682_4899.jpg
Presumably, ‘fbcdn.net’ stands for ‘Facebook Content Delivery Network,’ and the image is hosted here using a high-performance photo server which doesn’t have to do all the session management overhead of the larger site. Keep in mind that, as reported in October, Facebook is hosting 10+ billion photos, by some measures more than any other site on the web. If the URL of a photo were temporary and difficult to guess from the public address, this scheme might be okay. The photo server will in fact respond to a request from wget without any cookies at all. It has to, because it is in a different domain than the main Facebook site, and browsers are specifically designed to prevent transferring state between domains.
Unfortunately the link is neither temporary nor difficult to guess. The links appear to work indefinitely, based on trying some months-old ones floating around the web. Worse, most of the apparent randomness in the URL is not needed to access the photo. The following link is just as valid as the one posted above:
http://photos-c.ak.fbcdn.net/photos-ak-sf2p/210132/n210132_34947682_4899.jpg
All we need is the actual filename of the photo, and I’ve reverse-engineered the filename format as:
[photo-size][uid]_[pid]_[PIN].jpg
Photo-size is just a character in the set {t, s, n} representing the resolution of the image, uid is the user ID of the user who uploaded the photo, pid is a photo ID, and PIN is a four-digit random number. I’m calling it a PIN because it was chosen to be four decimal digits, which can only be assumed to have been done in a foolish analogy to bank card security. It’s easy to learn everything but the PIN given a public link to the photo. Brute-forcing the PIN is also fairly easy: it’s a space of 9000, which can be searched in about 45 minutes using one script. This is also easily parallelisable, given that we can query any of the mirrored photo servers in the set {photos-a.ak.fbcdn.net, photos-b.ak.fbcdn.net … photos-z.ak.fbcdn.net} we can get this down to under 2 minutes.
This is still a lot of work for one photo, but it gets better. Incrementing the photo ID by one reliably gives the next photo that was uploaded as part of the same album. Looking at the next few photos in sequence from the one posted above, the sequence of PINs is {4899,5210,5535,5857,6193,6524,6853}, giving deltas of {311,325,322,336,331,329}. These are almost certainly created by timestamping as the photos are received. So, given the public link to one photo, and doing one brute-force, we can pretty easily get the rest of the album with 10-20 queries per photo. I’ve coded this up and it works splendidly–the photo servers don’t appear to do any rate-limiting or blocking.
How to fix this problem? Obviously Facebook could check the session cookies for every photo request, but we’ll assume this is impractical given the current setup. If concede that using the knowledge of an opaque URL as a capability to view a photo is all we have to work with, then there is no reason not to increase the length of the PIN portion to be a cryptographically-strong 20 digits–it doesn’t need to ever be written or stored by a human. Of course, these must be generated randomly as photos are uploaded. It would also be prudent to have the PINs expire after an hour or so, as they aren’t meant to provide a permanent link, and may end up cached in all sorts of places. Finally, multiple requests with invalid PINs should lead to IP blocking to prevent crawling.
This is a smaller hole than the one from last year, as we need to find a public photo link first. As far as I can see, there’s no predictable pattern of photo IDs for given user IDs, so we can’t access photos for our arbitrary choice of user. Still, it is a privacy violation as Facebook promotes the view that public links won’t allow access to photos, when they actually do. Above all, it is an inexcusably sloppy design, especially given the bad press Facebook received for the original problems.
Entry filed under: Security engineering, Social networks
65 comments Add your own
1. Alex | February 11th, 2009 at 11:26 UTC
Note the giveaway “ak.” – fbcdn is indeed a CDN, but it’s actually Akamai Technologies’s CDN.
1 192.168.1.1 (192.168.1.1) 1.578 ms 1.871 ms 0.726 ms
2 ar0.rbsov.bogons.net (193.178.223.245) 26.429 ms 29.046 ms 29.954 ms
3 cr0-Vl1455.thdo.bogons.net (194.39.143.161) 28.916 ms 26.863 ms 26.478 ms
4 cr0-G1-1.rbsov.bogons.net (193.178.223.218) 26.052 ms 25.778 ms 24.490 ms
5 linxnap.netarch.akamai.com (195.66.224.168) 27.059 ms 28.777 ms 31.115 ms
6 a92-122-208-200.deploy.akamaitechnologies.com (92.122.208.200) 30.019 ms 28.898 ms 26.221 ms
Presumably they just offload all the images into the akamai instances and hope. To do better, they might need to move to something like Akamai Edge Computing, where they push app servers out to the edge – so they could serve images as if they were directly served from facebook.com, but at the edge.
2. Joseph Bonneau | February 11th, 2009 at 12:13 UTC
Good observation–I suspected the use of a separate domain name for the photos is to enable the use of a third-party CDN. Curiously, some of my photos on the site are hosted within the facebook.com domain, seemingly those which I uploaded from within the USA:
http://photos-a.ll.facebook.com/photos-ll-sf2p/v74/41/83/210132/n210132_32194048_4702.jpg
Interestingly, it is much pickier about the URL, requiring the directory structure before the filename to be correct and making this attack more difficult (though it’s not clear this is meant as a security feature or is just an accident).
Perhaps we are observing growing pains as Facebook uses Akamai and other third-party CDN’s to host it’s photos for Europe, where it doesn’t have it’s own server farms?
3. Alex | February 11th, 2009 at 13:15 UTC
LL = Limelight Networks, surely?
1 192.168.1.1 (192.168.1.1) 1.487 ms 0.727 ms 0.728 ms
2 ar0.rbsov.bogons.net (193.178.223.245) 25.536 ms 27.323 ms 27.866 ms
3 cr0-Vl1455.thdo.bogons.net (194.39.143.161) 37.859 ms 34.327 ms 30.837 ms
4 tge2-3.fr4.lon.llnw.net (195.66.226.133) 23.448 ms 32.701 ms 25.258 ms
5 ve5.fr3.lon.llnw.net (69.28.171.137) 25.289 ms 37.723 ms 41.875 ms
6 tge7-2.fr3.lga.llnw.net (69.28.171.125) 97.004 ms 93.573 ms 93.150 ms
7 cdn-208-111-128-6.lga.llnw.net (208.111.128.6) 94.873 ms 125.412 ms 93.998 ms
Interesting that they’re using multiple CDNs.
4. Boyana | February 20th, 2009 at 00:06 UTC
If that IS a picture of you then youre CUTE
Thats all
5. Juicy Theodore | February 22nd, 2009 at 20:58 UTC
I agree with Boyana. More pix!
6. M | February 22nd, 2009 at 21:34 UTC
AFAIK you can use your own domains with Akamai. I know of at least one such website
7. R | February 22nd, 2009 at 22:07 UTC
Your second link “http://photos-c.ak.fbcdn.net/n210132_34947682_4899.jpg” is actually pointing to http://photos-c.ak.fbcdn.net/photos-ak-sf2p/v646/41/83/210132/n210132_34947682_4899.jpg
If you try to go to http://photos-c.ak.fbcdn.net/n210132_34947682_4899.jpg in your address bar it says “access denied”.
8. Alex Polvi | February 23rd, 2009 at 00:32 UTC
any idea who this is?
http://photos-a.ak.fbcdn.net/photos-ak-fooo/
or
http://photos-b.ak.fbcdn.net/photos-ak-bar/
or
http://photos-c.ak.fbcdn.net/photos-ak-asdfasdf/
weird.
9. jd | February 23rd, 2009 at 04:13 UTC
SmugMug ran into an issue like this not long ago. It was actually pretty easy to access random pictures (you could do it by hand, didn’t even need a script), even for albums that were considered “private”. They ignored the problem until the bloggers discovered the loophole, finally generating enough attention (and negative publicity) so that SmugMug had to fix it.
10. Chanux | February 23rd, 2009 at 05:19 UTC
@Alex Polvi
http://photos-c.ak.fbcdn.net/photos-ak-qwerty/
Actually http://photos-c.ak.fbcdn.net/photos-ak-anything/ gets the same weird photo
11. Mark | February 23rd, 2009 at 18:24 UTC
The simple fix of course is to replace the PIN with something useful, like a md5 or sha1 hash.
12. Size? | February 23rd, 2009 at 20:01 UTC
How is the photosize easily obtained from the original URL? I mean, certainly photos come in some standard sizes, but they don’t need to. One can crop something and upload the crop. So wouldn’t your brute force need to also guess the photo size for all PINs?
13. php | February 23rd, 2009 at 20:15 UTC
Interesting!
“This is a smaller hole than the one from last year”. There will always be a work around to a fix from facebook.
14. Baggins | February 23rd, 2009 at 21:05 UTC
@12: the photosize is the letter at the start of the filename, it is one of t, s, or n.
15. I. C. London | February 23rd, 2009 at 22:00 UTC
Thanks for the tip! Could be good to check out photos of the babes!
16. v4lkyrius | February 23rd, 2009 at 22:14 UTC
The script: http://code.google.com/p/fbcdngen/
Enjoy.
17. v4lkyrius | February 23rd, 2009 at 22:38 UTC
For the script, search Google Code for “fbcdngen”.
(Sorry if this is a double-post.)
18. 831 | February 23rd, 2009 at 22:40 UTC
They should learn from myspace photos. The file names consist of a letter denoting the size, and a hash of an unknown combination of variables, which probably include photoID, UID, albumID, timestamp, etc.
The only way to get the URL to the photo is to visit the album, which is protected by privacy settings. And if the actual .jpg URL is shared, there is no personally identifiable or easily generated information in the filename
19. Richard Wagnerius | March 3rd, 2009 at 05:25 UTC
v4lkyrius,
is there a way to use this with Windows?
20. v4lkyrius | March 4th, 2009 at 17:58 UTC
Richard Wagnerius,
You could always use Cygwin.
Porting to Python would also be trivial — I might do this in the next couple of days… (I was going to update the Google Code page with a more practical example soon anyway.)
21. brunetton | March 11th, 2009 at 11:14 UTC
Apparently things changed for PIN.
Just uploaded a serie of photos today, the first is :
http://photos-a.ak.fbcdn.net/photos-ak-snc/n688128920_1416990_508624.jpg
and for the other :
n688128920_1416990_508624
n688128920_1416991_5698379
n688128920_1416992_419399
n688128920_1416993_6452287
n688128920_1416994_1876604
n688128920_1416995_5456914
n688128920_1416996_3920588
n688128920_1416997_1648475
n688128920_1416998_3840230
n688128920_1416999_1262169
n688128920_1417000_7373886
n688128920_1417001_2291228
n688128920_1417002_4354333
n688128920_1417003_3309571
n688128920_1417004_8117456
n688128920_1417005_5151293
n688128920_1417006_4690735
n688128920_1417007_5531666
n688128920_1417008_1689620
n688128920_1417009_6540206
Photos have been uploaded at «the same time», I mean in the same group of photos using «upload all» in the java applet.
I don’t see any timestamp here, it looks like 7 decimal random digits.
I’ll really slow the process for brute force
Hopefully, we always can check babe’s photos that have been uploaded before that filename change.
22. v4lkyrius | March 11th, 2009 at 22:12 UTC
brunetton,
In your example, the total number of digits is 22, as opposed to just 18 in the article. So, brute forcing your photos would theoretically take 10,000 times as much effort.
However, in practice, “UID” is still constant, and “PID” are still sequential, so brute-force attacks (given a single known URL to begin with) remain entirely plausible.
23. brunetton | March 12th, 2009 at 10:17 UTC
v4lkyrius, indeed it remains possible, but it’ll be 1000x slower (minimum) :
4 digits (with a small delta) –> 7 random digits
It was just what I wanted to point
24. Nancy | March 28th, 2009 at 15:24 UTC
Sorry not a programmer but need to know if you do consider Facebook privacy secure. I am searching answers to advise if this is the right tech tool to use for teaching professionals who wish to communicate with other teaching professionals on a variety of topics (teacher unions advise against it).
25. rocker | April 17th, 2009 at 10:05 UTC
i’m trying your example v4lkyrius, my url_list now is 14GB and i’ve restricted the search from PID_MIN (00000000) to PID_MAX (01999999)… and it is still running!!
26. AkiRoss | April 21st, 2009 at 11:38 UTC
@11
actually, you can’t use an hash as PIN, because hash is (should) be irreversible, while the server needs a quick way to associate a number to a filename. If hash were a feasible solution, it would be so easy to use just an MD5 for every file like [size][md5].jpg.
I.e. given an md5 of the image, you can’t go back to the image/filename quickly. It would require a database to associate every hash to every file in one single enormous table.
In addition, hashes aren’t so quick to generate. This is an high traffic server, i guess there are performance reason because they didn’t use an hash from the beginning.
27. G | April 23rd, 2009 at 08:02 UTC
is this still working??
I see that the UID currently is more than 6 digits… so is this still working?
28. G | April 23rd, 2009 at 09:50 UTC
@v4lkyrius
hello v4lkyrius
is your code still working?
I am not able to work on cygwin xargs
it says command not found
please help
29. dude | April 26th, 2009 at 09:37 UTC
man that 6 digits thing sucks
cant find anything no more
any ideas
30. jack martin | May 30th, 2009 at 14:50 UTC
so if I had some picture ids from a past friend whose albums are set to private how would I pull those up now?
what would I put into those links to change it to the pic I want to see?
thanks for help
31. Tim | June 2nd, 2009 at 01:40 UTC
The script still “works” with the longer UID and random 7-digit PIN, it’s just not practical anymore given the crazy size of the url_list.txt you are going to get. If it could be modified to create the URL, do the wget and get whatever is there to be gotten, and then move on to the next URL without creating that massive text file, that would be great. Could just leave it running forever.
32. Jay | June 11th, 2009 at 02:55 UTC
Poking around with this today. A few things: Facebook has moved to the larger numbers for new uploads, but this method is still practical for older files.
However, when I try to use it, wget winds up eating up all my RAM and bringing my system to a grinding halt quickly. Is there a way to avoid this?
33. jagga jatt | June 17th, 2009 at 13:41 UTC
Is there a way to determine a PHOTO-ID for an unknown gallery given you have acess to one or more urls to different galleries?
34. Voolex | August 7th, 2009 at 07:24 UTC
Or simply, you can guess a friends password…. Most of them are using the ddmmyy format… Works every time…
35. giraa2 | September 4th, 2009 at 04:54 UTC
There is a proven method available for accessing photos from private profiles, it is described in:
http://privacystalker.blogspot.com/
It uses brute force attack approach! It works for me!!
It was written in spanish but you can always use google translator or babylon…
36. Anthony | September 6th, 2009 at 16:19 UTC
I don’t care how long the PIN is; it’s still technically a security issue. If they’re using a 3rd party CDN with no intelligence aside from wget, that means that a “friend” can grab the true URL of a photo, post it somewhere, and that’s that.
People are all caught up on the idea that a photo is secure, even on a CDN, as long as the URL is “unguessable”. But isn’t anyone concerned about an unguessable URL being acquired legitimately, but then posted somewhere it shouldn’t be, at which point there is no ACL to check permissions?
37. Interweb Troll | September 8th, 2009 at 04:36 UTC
Hey, could you figure out who uploaded the photo which has a filename ending in 13523613_3602037.jpg? I think it’s from an album, not a profile picture, hence it has a 5-string ID but I was only given two of the strings. I don’t know the user ID of the person (that’s what I’m trying to figure out). Any advice would be appreciated. Thanks.
38. Sasha | November 9th, 2009 at 17:27 UTC
Is there a way to figure out the sequence of the pin? For example, one of my pins (as you identify it) is .1273.
If someone has that pin along with the [photo-size][uid]_[pid]_[pin], can they now view my other photos? Thanks!
39. Rohan Tarun | December 4th, 2009 at 03:07 UTC
I believe the script needs to be updated… as for there for the digits have been increased and extra numbers have been included…
please kindly lend us the updated version
40. Jat | December 27th, 2009 at 01:44 UTC
This doesn’t work anymore does it? =[
41. Bridget | December 30th, 2009 at 22:31 UTC
For the fbcdngen script, how large is the text file supposed to be when you run with the old 4-digit pins? I had to stop mine because it was approaching 400 gigabytes (after I left the computer for a few hours) and was going to fill up my entire hard drive if I let it run much longer. I don’t know if it was looping infinitely or if those really are all individual values — if the latter, how does anyone have the hard drive space for the 7-digit pins..?
42. Ugly51 | January 24th, 2010 at 01:22 UTC
Reference the post regarding the link:
http://photos-a.ak.fbcdn.net/photos-ak-anything/
I was in the middle of experimenting with this URL, when my connection dropped out while the page was loading, leaving me with something quite interesting in my address bar. I had:
http://www.zwunzi.com/and a huge random sting]
I think this may be of interest. Are Facebook hosting phots on Zwunzi.com?
The Zwunzi URL is not in my browser history, almost as though it never happened. Otherwise I would have posted the actual link here.
43. anonymous17 | January 29th, 2010 at 04:21 UTC
too bad ugly51 that seems like a virus not any kind of photo hosting on the part of facebook.
a quick google for zwunzi will tell.
44. Arkhonx | March 3rd, 2010 at 19:57 UTC
Is it possible to use this method to retrieve a recently deleted photo? The album’s open and stuff. I basically have everything but the pin and the server. They seem to be different from picture to picture although they’re in the same album.
Thanks in advance!
45. giorgos4 | April 29th, 2010 at 23:10 UTC
and what we can do when the name of the photo is something like this: http://sphotos.ak.fbcdn.net/photos-ak-sf2p/v645/21/115/18878_1354857355512_1353807097_30988186_8292763_n.jpg it has a lot of digits..can i find the facebook source of this photo?
46. T | May 3rd, 2010 at 20:59 UTC
http://www.facebook.com/profile.php?id=1353807097#!/album.php?profile=1&id=1353807097
47. Mike | May 17th, 2010 at 20:08 UTC
Hey Joseph (and others),
Take note of the direct link to images you’re ready to delete off of Facebook, delete them and check a few days later.
If they’re still there, send them a message through the non-copyright infringement notice page with a list of those direct links.
So far, I’ve been told it works.
If enough people do it, then Facebook will have to address their privacy concerns before people quit and move to some of the newer sites opening up.
48. 5th Street | June 14th, 2010 at 16:37 UTC
It didn’t work! Help!!
All I have is the user’s URL, I can’t even view the pictures… I tried copy pasting, that didn’t work either!
49. 5th Street | June 14th, 2010 at 16:38 UTC
It didn’t work! Help!!
All I have is the user’s URL.. I can’t view the pictures.. I tried copy and paste, that didn’t work either!!
50. 5th Street | June 14th, 2010 at 16:47 UTC
It says “Content Not Found” WTH??!!
51. Jhon Doe | July 28th, 2010 at 04:35 UTC
Viewing private albums is actually easy if you stop trying to get your target friendship and focus on his/her friends. Why don’t we view our target profile logged as a friend he/she already has accepted? This idea is full explained in
http://www.giraa2.com/2010/05/acceso-total-un-facebook-privado.html
(in spanish, but you can use google translator)
52. sally george | August 3rd, 2010 at 15:17 UTC
hi ,
please i want to know how can i search by photo in facebook?? or in tagged or my space or any site like that???
i want to know if there is some one use my photos in a fake profile
please i need your help
53. anna | August 4th, 2010 at 10:26 UTC
I use http://accessexists.com at work
54. sudhir | December 13th, 2010 at 12:34 UTC
Hi, I tried but your trick could not be understood by me.. Is there any possibility of allowing me to view this user’s private photo album???
http://www.facebook.com/profile.php?id=100000177503270
thanks in advance
55. Arnoucheka | December 25th, 2010 at 16:45 UTC
photo
56. Arnoucheka | December 25th, 2010 at 16:49 UTC
is there any possibility of allowing me to view this user s private photo album???
57. Arnoucheka | December 25th, 2010 at 16:52 UTC
is there any possibility of allowing me to view this user s private photo album
58. Charizze | January 24th, 2011 at 02:38 UTC
i clicked the link by accident and downloaded some file , is that bad ?
59. haze | February 8th, 2011 at 05:18 UTC
im confuse! can someone teach me, how to get the photo Id and the pin which has the 7 digit numbers. thank you
60. Web Developer | February 12th, 2011 at 09:54 UTC
Unless you know who you are looking for it is useless.
61. berga | July 11th, 2011 at 12:58 UTC
Hi,
It seems that fbcdn filenames format has changed, and now looks like that :
ID1_ID2_ID3_ID4_ID5_SIZE.jpg
ID3 seems to be uploader’s ID, SIZE is in the same set as before.
Did anyone worked on reversing this new format ?
62. mancini02 | August 5th, 2011 at 13:12 UTC
I’m not much of a computer programmer but can you apply this technique to give me access to
http://www.facebook.com/profile.php?id=141400985
sophia’s photos?
63. Gare | September 24th, 2011 at 17:19 UTC
Hi,
I think my fiancee is hiding something from me. In other words, sleeping with another guy. Her profile weblink is:
http://facebook.com/nmsisg
her id=1032585243
Massiel Galan
I need help from someone that can help me view this profile. I know she is hiding something from me… I just need to verify.
Please help! I am lost…
Gare
64. farid | October 7th, 2011 at 04:11 UTC
i need to see http://www.facebook.com/aida.riazi1 ’s photos
help plz?
65. meriaduz | January 6th, 2012 at 02:06 UTC
With the actual links on fbcdn, the photos have now more IDs.
[ID1]_[PID]_[UID]_[ID4]_[ID5]_[SIZE].jpg
PID = photo id;
UID = user who uploads the photo;
SIZE = the size in the picture, could be q, n, o and merely t, s…
…but what about the others ID?!?!
It looks like they want to make harder the way of getting those IDs, but we can get access to the pictures if knowing the URL.
Leave a Comment
Some HTML allowed:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Subscribe to the comments via RSS Feed