Статья О стойкости, хэшировании и подборе паролей с помощью Python

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

Давайте рассмотрим небольшой пример. Вот пароль: FhjpEgfkYfkfgeFpjhf4558. По сути, это беспорядочный (нет) набор символов. Ну да не в этом дело. В сети множество калькуляторов для вычисления времени на подбор пароля методом прямого перебора, то есть брут-форса. Давайте посмотрим, сколько времени будет взламываться данный пароль.

0000.png

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

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

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

Python:
chrs_s = 'abcdefghijklmnopqrstuvwxyz'
chrs_upper = chrs_s.upper()
chrs_specials = '!\][/?.,~-=";:><@#$%&*()_+\' '
chrs_numerics = '1234567890'
chrs = ''.join([chrs_s, chrs_upper, chrs_specials, chrs_numerics])

Для того чтобы вычислить все последовательности данной строки, воспользуемся функцией product() модуля itertools. Данная функция возвращает декартово произведение входных итерируемых последовательностей. Ну и для того, чтобы вычислить декартово произведение последовательности с самим собой укажем необязательный аргумент repeat, в который будем последовательно передавать количество повторений из цикла.

Фух, я не математик и потому, данные формулировки достаточно сложны. Но, тем не менее, воспользоваться этим модулем я все же смогу. Давайте приступим.

Python:
for i in range(1, 13):
    for j in itertools.product(chrs, repeat=i):
        temp = ''.join(j)
        compare_hesh(pass_hesh, temp)

Создадим цикла от одного до… тут нужно указать, какой длины последовательность символов вы желаете получить по окончании всех повторений. Затем запускаем цикл, в котором используем для генерации функцию product(), в которую передаем полученную нами последовательность символов и в переменной темп объединяем полученные значения, которые передаем дальше.

Вот полный код функции для генерации последовательностей и передачи их в другую функцию для сравнения.

Python:
def chars_gen(pass_hesh):
    chrs_s = 'abcdefghijklmnopqrstuvwxyz'
    chrs_upper = chrs_s.upper()
    chrs_specials = '!\][/?.,~-=";:><@#$%&*()_+\' '
    chrs_numerics = '1234567890'
    chrs = ''.join([chrs_s, chrs_upper, chrs_specials, chrs_numerics])

    for i in range(1, 13):
        for j in itertools.product(chrs, repeat=i):
            temp = ''.join(j)
            compare_hesh(pass_hesh, temp)

Как видим, данная функция так же принимает значение хэша пароля, которое мы попросим ввести пользователя в начале программы, и передает его дальше в другую функцию.

Теперь создадим функцию def compare_hesh(input_pass_md5, word), которая будет принимать хэшированный пароль и сгенерированную последовательность символов.

Python:
def compare_hesh(input_pass_md5, word):
    enc_hash_word = hashlib.md5(word.encode('utf-8').strip()).hexdigest()
    if input_pass_md5 == enc_hash_word:
        print(f'Пароль найден: {word}')
        exit(0)

Здесь мы создаем переменную, которая использует библиотеку hashlib, в частности ее функцию md5. В данную функцию мы передаем полученную последовательность символов, после чего эта последовательность кодируется в кодировку utf-8, обрезаются все пробелы, если таковые присутствуют и переводим с помощью hexdigest в шестнадцатеричные значения. Ну, а дальше сравниваем хэш пароля, который у нас есть с полученным хэшем сгенерированной последовательности. И если данные значения совпадают, ура. Мы молодцы и пароль найден. О чем сразу же принтуем в терминал, не забывая при этом добавить к напечатанному сообщению и сам найденный пароль. Ну и дальше выходим из скрипта, чтобы исключить дальнейшую генерацию и сравнение.

Вот полный код скрипта:

Python:
import itertools

def chars_gen(pass_hesh):
    chrs_s = 'abcdefghijklmnopqrstuvwxyz'
    chrs_upper = chrs_s.upper()
    chrs_specials = '!\][/?.,~-=";:><@#$%&*()_+\' '
    chrs_numerics = '1234567890'
    chrs = ''.join([chrs_s, chrs_upper, chrs_specials, chrs_numerics])

    for i in range(1, 13):
        for j in itertools.product(chrs, repeat=i):
            temp = ''.join(j)
            compare_hesh(pass_hesh, temp)


def compare_hesh(input_pass_md5, word):
    enc_hash_word = hashlib.md5(word.encode('utf-8').strip()).hexdigest()
    print(enc_hash_word)
    if input_pass_md5 == enc_hash_word:
        print(f'Пароль найден: {word}')
        exit(0)


def main():
    input_pass_md5 = input('Введите хэшированный пароль: ')
    chars_gen(input_pass_md5)


if __name__ == "__main__":
    main()

По сути, это вот все можно использовать, но, только для генерации паролей. Для брутфорса данный скрипт будет работать очень медленно и найдет пароль точно годика через два-три.

Давайте немного модифицируем данный скрипт. Предположим, что у нас есть очень хороший словарь для перебора паролей. Ну и немного везения, надежды пользователя на авось и прочих, неучтенных, но тем не менее действующих факторов. В данном случае перебор будет работать быстрее. Вот только найдется ли наш пароль, вопрос открытый. Давайте изменим функцию def chars_gen(pass_hesh).

Python:
def chars_gen(pass_hesh):
    with open('0.txt') as file:
        src = file.readlines()
    for word in src:
        compare_hesh(pass_hesh, word.strip())

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

Python:
import hashlib


def chars_gen(pass_hesh):
    with open('0.txt') as file:
        src = file.readlines()
    for word in src:
        compare_hesh(pass_hesh, word.strip())


def compare_hesh(input_pass_md5, word):
    enc_hash_word = hashlib.md5(word.encode('utf-8').strip()).hexdigest()
    print(enc_hash_word)
    if input_pass_md5 == enc_hash_word:
        print(f'Пароль найден: {word}')
        exit(0)


def main():
    input_pass_md5 = input('Введите хэшированный пароль: ')
    chars_gen(input_pass_md5)


if __name__ == "__main__":
    main()

Проведем боевое испытание. Я предварительно хэшировал пароль: qwerty202020. И для того, чтобы понять, работает наш скрипт или нет добавил в словарь с паролями. Вот, собственно хэш данного пароля: b3e5a79c37c4ab275848a5cdb9092b22.

Запускаем программу. Вводим хэш и… достаточно быстро находим пароль.

0001.png


Понятное дело, если бы я данный пароль не добавил в словарь, он был бы не найден. Но, тем не менее, теперь вы понимаете, насколько уязвимы «стандартные» или лучше сказать «ленивые» пароли вроде 12345 или qwerty123, ну и прочей такой же ерунды. Она уже давно вся изучена и добавлена в словари.

Поэтому, будьте внимательны при генерации пароля. Делайте его как можно более устойчивым ко взлому и не попадайтесь на фишинговые атаки.

Спасибо за внимание. Надеюсь, что данная статья будет кому-нибудь полезна
 
  • Нравится
Реакции: And4R и El Chapo
Мы в соцсетях: