Конкурс Брутим архивы ZIP/RAR используя python.

Bdpk

Bdpk

Member
06.11.2016
8
47
logo.png


Статья для участия в Конкурсе программистов.

Доброго времени суток, мы начинаем!

0. Предисловие

Всегда наступает такой момент, когда пользователь забывает пароль от архива, в котором спрятал свои секреты. Для самых забывчивых мы сегодня сделаем некоторую программу, функционал которой -- подбор пароля для архивов с расширением zip/rar.

Писать будем на python3(дальше просто python), хотя заранее знаем, что писать локальную брутилку на этом языке не рационально. Однако иногда он используется как черновик, вот и почеркаем немного, чтобы проверить, можно ли такое реализовать. (да, можно :) )

1. Подготовка


Нам понадобится:
  • текстовый редактор или IDE;
  • интерпретатор python;
  • минимальное знание python; ( В противном переходим к чтению серии статей от @al04e )
  • желательно, наличие unix;
2. Стартуем

Мы пишем на python, поэтому не будем засорять основное окружение ненужными пакетами и воспользуемся инструментом :
virtualenv Codeby

Сгенерировалось окружение, активируем его!
source bin/activate

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

Если приходилось писать на этом языке, то наверняка знакома следующая , если нет, рекомендую ознакомиться:
Python:
if __name__ == "__main__":
    pass
Начало положено! Программа работает с двумя сущностями: архив и словарь, поэтому напишем пару строк, чтобы обработать входящие данные:
Python:
import argparse
parser = argparse.ArgumentParser( '--file ' + '--dict ')
parser.add_argument('-f', '--file', dest='archive', required=True, type=str, help='Archive file')
parser.add_argument('-d', '--dict', dest='dictionary', required=True, type=str, help="Dictionary file")
args = parser.parse_args()
Импортировали модуль для разбора входящих данных.
Создали объект парсер и указали какие аргументы надо парсить, обращаю внимание, что в поле аргумента dest хранится имя нашей сущности, напоследок мы запарсили, все что пришло к нам. Аргумент required=True означает, что программа не будет работать без указания этого параметра, т.е это обязательный флаг для программы.


launch.PNG


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

Сэкономлю ваше время и расскажу, что к чему тут, у нас есть:
1544816255876.png

Поэтому нам следует изначально определить входного файла.
Нам стало известно:
  • RAR = RAR!
  • ZIP = PK
Сразу возникает следующая идея, открыть файл как бинарный и забрать первые два бита.
Python:
def cutMagicNumbers(archive):
    with open(archive, 'rb') as file:
        currentType = file.read(2).decode()
    return launcher(currentType)

cutMagicNumbers(args.archive)(args.archive, args.dictionary)
Заодно вернем полученную сигнатуру другой функции, которая определит расширение архива.

Python:
def launcher(extension):
    return {'PK': prepareBruteZip,
            'Ra': prepareBruteRar,
            }.get(extension, 'Not Found')
Теперь по порядку. В момент вызова функции cutMagicNumbers(args.archive) мы передали архив, открыли его и прочитали первые два бита. Затем вернули результат функции launcher(extension), которая знает как сопоставляется сигнатура и архив. В теле функции launcher определен словарь со значениями, сигнатурой - ключом и функцией.
Используя метод get, мы возвращаем ссылку на функцию(prepareBruteZip или prepareBruteRar) в функцию cutMagicNumbers, которая в свою очередь возвращает в cutMagicNumbers(args.archive)(args.archive, args.dictionary) и полученное значение – функция с двумя аргументами (args.archive, args.dictionary).

Стек вызовов выглядит так:

call_stack.PNG



Осталось определить функции, которые будут отвечать за подготовку и перебор.

Начнем с Zip. Импортируем модуль zipfile, он включен в стандартную библиотеку python.

Python:
def prepareBruteZip(archive, dictionary):
'''
file  == 2.0
type(pwd) == byte
'''
    zArchive = zipfile.ZipFile(archive)
    with open(dictionary, 'r') as wordlist:
        for word in wordlist.readlines():
            password = word.strip('\n').encode('ascii')  # unicode → byte
            brute(zArchive, password)
Прокомментирую некоторые моменты. Мне не удалось подобрать пароль к архиву ниже 2.0, поэтому первое условие – версия архива >= 2.0. Чувствуется, что модуль писался под python2, поэтому предварительно приходится преобразовывать к таблице ascii.

Python:
def brute(archive, password):
    try:
        archive.extractall(pwd=password)
        print('[+] Password is {}'.format(password))
    except:
        pass
Следом определим функцию подбора brute(). Она до жути простая. Замалчивает об ошибках возникающих при переборе, и если удалось открыть, печатает результат.


Перейдем к RAR. Импортируем модуль rarfile, его уже придется скачать.
pip3 install rarfile


Python:
def prepareBruteRar(archive, dictionary):
'''
type(pwd) == str
requirements installed unrar
'''
    rArchive = rarfile.RarFile(archive)
    with open(dictionary, 'r') as wordlist:
        for word in wordlist.readlines():
            password = word.strip('\n')  # str
            brute(rArchive, password)
С архивом rar все туманнее и запутаннее. При подборе архив ожидает, что вы передадите пароль в виде строки, а не байтов. Но интересен тот факт, что модуль rarfile требует установленной программы unrar. “что? ”- подумаете вы. Я так же подумал и пошел в сорцы, и лицезрел следующий текст.

# Basic logic:
# - Parse archive structure with Python.
# - Extract non-compressed files with Python
# - Extract compressed files with unrar.
# - Optionally write compressed data to temp file to speed up unrar,
# otherwise it needs to scan whole archive on each execution.

Разработчик ставил перед собой цель научить python запускать unrar, иначе говоря сделать обертку. Комичная ситуация, но на Windows unrar я так и не нашел.

Функция brute() для rar идентична, поэтому мы используем подготовительные функции.


3. Послесловие

Мы написали простой код, который подбирает пароль для архива. Настало время бенчмарков!

benchmark.PNG


Что и ожидалось от rar архива, ведь все время программа проводит в ожидании и просто вызывает unrar

На этом все! Не забывайте свои пароли!

Спасибо за внимание!


Python:
import zipfile
import rarfile
import argparse


def cutMagicNumbers(archive):
    with open(archive, 'rb') as file:
        currentType = file.read(2).decode()
    return launcher(currentType)


def launcher(extension):
    return {'Ra': prepareBruteRar,
            'PK': prepareBruteZip,
            }.get(extension, 'Not Found')


def prepareBruteZip(archive, dictionary):
    '''
    Ограничения file <ZipName> == 2.0
    type(pwd) == byte
    '''

    zArchive = zipfile.ZipFile(archive)
    with open(dictionary, 'r') as wordlist:
        for word in wordlist.readlines():
            password = word.strip('\n').encode('ascii') 
            brute(zArchive, password)


def brute(archive, password):
    try:
        archive.extractall(pwd=password)
        print('[+] Password is {}'.format(password))
    except:
        pass


def prepareBruteRar(archive, dictionary):
    '''
    type(pwd) == str
    requirements installed unrar
    '''
    rArchive = rarfile.RarFile(archive)
    with open(dictionary, 'r') as wordlist:
        for word in wordlist.readlines():
            password = word.strip('\n')
            brute(rArchive, password)

if __name__ == "__main__":

    parser = argparse.ArgumentParser(
        '--file <archive>' + '--dict <dictionary>')

    parser.add_argument('-f', '--file',  dest='archive', required=True,
                        type=str, help='Archive file')
    parser.add_argument('-d', '--dict',  dest='dictionary', required=True,
                        type=str, help="Dictionary file")
    args = parser.parse_args()

    cutMagicNumbers(args.archive)(args.archive, args.dictionary)
 
Мы в соцсетях: