Check isAdmin value + Docker Testing + README update

pull/25/head
Swissky 2018-07-29 18:58:46 +02:00
parent f53723a54a
commit 06bb761c87
12 changed files with 2157 additions and 4583 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
TODO.md
TODO/
*.pyc
engine/*.pyc

View File

@ -1,9 +1,9 @@
# Wordpresscan
A simple Wordpress scanner written in python based on the work of WPScan (Ruby version)
A simple Wordpress scanner written in python based on the work of WPScan (Ruby version), some features are inspired by WPSeku.
## Disclaimer
```
The author of this github is not responsible for misuse or for any damage that you may cause!
The authors of this github are not responsible for misuse or for any damage that you may cause!
You agree that you use this software at your own risk.
```
@ -17,14 +17,15 @@ cd Wordpresscan
```
Virtualenv
```
```bash
virtualenv .venv -p /usr/bin/python2.7
source .venv/bin/activate
pip install -r requirements.txt
```
Example 1 : Basic update and scan of a wordpress
```
## Examples
### Example 1 : Basic update and scan of a wordpress
```python
python main.py -u "http://localhost/wordpress" --update --random-agent
-u : Url of the WordPress
@ -33,13 +34,13 @@ python main.py -u "http://localhost/wordpress" --update --random-agent
--random-agent : Use a random user-agent for this session
```
Example 2 : Basic bruteforce (option --brute, option --nocheck)
### Example 2 : Basic bruteforce (option --brute, option --nocheck)
* bruteforce customs usernames
```
```python
python main.py -u "http://127.0.0.1/wordpress/" --brute --usernames "admin,guest" --passwords-list fuzz/wordlist.lst
```
* bruteforce with usernames list
```
```python
python main.py -u "http://127.0.0.1/wordpress/" --brute --users-list fuzz/wordlist.lst --passwords-list fuzz/wordlist.lst
```
* bruteforce detected users
@ -48,7 +49,7 @@ python main.py -u "http://127.0.0.1/wordpress/" --brute --passwords-list fuzz/wo
```
```
```python
╭─ 👻 swissky@crashlab: ~/Github/Wordpresscan master*
╰─$ python main.py -u "http://127.0.0.1/wordpress/" --brute --users-list fuzz/wordlist.lst --passwords-list fuzz/wordlist.lst --nocheck
_______________________________________________________________
@ -74,8 +75,8 @@ _______________________________________________________________
Bruteforcing - ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
```
Example 3 : Thinking is overrated, this is aggressive, mostly not advised!
```
### Example 3 : Thinking is overrated, this is aggressive, mostly not advised!
```python
python main.py -u "http://127.0.0.1/wordpress/" --fuzz
[i] Enumerating components from aggressive fuzzing ...
@ -91,6 +92,11 @@ python main.py -u "http://127.0.0.1/wordpress/" --fuzz
## Output example from a test environment
![alt tag](https://github.com/swisskyrepo/Wordpresscan/blob/master/screens/Version%204.4.7.png?raw=true)
## Deploy a test environment
```bash
docker-compose -f wordpress_compose.yml up -d
```
To enable `wp-json` api you need to change "Permalink" to anything but "simple" in the settings.
## Credits and Contributors
* Original idea and script from [WPScan Team](https://wpscan.org/)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -17,14 +17,14 @@ class Brute_Engine:
users_to_brute = usernames.split(',')
for user in users_to_brute:
user = user.replace(' ', '')
print notice("Bruteforcing " + user)
print(notice("Bruteforcing " + user))
self.bruteforcing_pass(wordpress, user, passwords_list)
# 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)
print(critical("Can't found %s file" % file_list))
exit()
# launch users & passwords bruteforce
self.bruteforcing_user(wordpress, users_list, passwords_list)
@ -34,10 +34,10 @@ class Brute_Engine:
else:
if len(wordpress.users) != 0:
if not os.path.isfile(passwords_list):
print critical("Can't found %s file" % passwords_list)
print(critical("Can't found %s file" % passwords_list))
exit()
print notice("Bruteforcing detected users: ")
print(notice("Bruteforcing detected users: "))
for user in wordpress.users:
print info("User found "+ user['slug'])
self.bruteforcing_pass(wordpress, user['slug'], passwords_list)
@ -48,7 +48,7 @@ class Brute_Engine:
description :
"""
def bruteforcing_user(self, wordpress, users_list, passwords_list):
print notice("Bruteforcing all users")
print(notice("Bruteforcing all users"))
with open(users_list) as data_file:
data = data_file.readlines()
@ -70,12 +70,12 @@ class Brute_Engine:
try:
html = requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text
except:
print critical('ConnectionError in thread, retry...')
print(critical('ConnectionError in thread, retry...'))
continue
break
# valid login -> the submited user is printed by WP
if '<div id="login_error">' in html and '<strong>%s</strong>' % user in html:
print info("User found "+ user)
print(info("User found "+ user))
users_found.append(user)
@ -84,7 +84,7 @@ class Brute_Engine:
description :
"""
def bruteforcing_pass(self, wordpress, user, passwords_list):
print info("Starting passwords bruteforce for " + user)
print(info("Starting passwords bruteforce for " + user))
with open(passwords_list) as data_file:
data = data_file.readlines()
@ -108,9 +108,20 @@ class Brute_Engine:
try:
html = requests.post(wordpress.url + "wp-login.php", data=data, verify=False).text
except:
print critical('ConnectionError in thread, retry...')
print(critical('ConnectionError in thread, retry...'))
continue
break
if not '<div id="login_error">' in html:
print warning("Password found for {} : {}{}".format(user,pwd, ' '*100))
print(warning("Password found for {} : {}{}".format(user,pwd, ' '*100)))
found[0] = True
self.xmlrpc_check_admin(user, pwd)
def xmlrpc_check_admin(self, username, password):
post = "<methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value><string>" + username + "</string></value></param><param><value><string>" + password + "</string></value></param></params></methodCall>"
req = requests.post("http://127.0.0.1:8000/xmlrpc.php", data=post)
regex = re.compile("isAdmin.*boolean.(\d)")
match = regex.findall(req.text)
if int(match[0]):
print(critical("User is an admin !"))

View File

@ -18,7 +18,7 @@ def notice(msg):
return "\n\033[1m[i] " + msg + "\033[0m"
def critical(msg):
return "\n\033[91m[!] " + msg + "\033[0m"
return "\033[91m[!] " + msg + "\033[0m"
def warning(msg):
return "\033[93m[i] " + msg + "\033[0m"

View File

@ -14,6 +14,7 @@ class Wordpress:
index = None
agent = False
users = {}
files = set()
def __init__(self, url, user_agent, nocheck, max_threads):
print info("URL: %s" % url)
@ -113,6 +114,7 @@ class Wordpress:
r = requests.get(self.url + 'readme.html', headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r):
self.files.add('readme.html')
# Basic version fingerprinting
regex = 'Version (.*)'
@ -130,6 +132,7 @@ class Wordpress:
def is_debug_log(self):
r = requests.get(self.url + 'debug.log', headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r) and not "404" in r.text :
self.files.add('debug.log')
print critical( "Debug log file found: %s" % (self.url + 'debug.log') )
@ -138,10 +141,26 @@ class Wordpress:
description : determine if there is any unsafe wp-config backup
"""
def is_backup_file(self):
backup = ['wp-config.php~', 'wp-config.php.save', '.wp-config.php.bck', 'wp-config.php.bck', '.wp-config.php.swp', 'wp-config.php.swp', 'wp-config.php.swo', 'wp-config.php_bak', 'wp-config.bak', 'wp-config.php.bak', 'wp-config.save', 'wp-config.old', 'wp-config.php.old', 'wp-config.php.orig', 'wp-config.orig', 'wp-config.php.original', 'wp-config.original', 'wp-config.txt', 'wp-config.php.txt', 'wp-config.backup', 'wp-config.php.backup', 'wp-config.copy', 'wp-config.php.copy', 'wp-config.tmp', 'wp-config.php.tmp', 'wp-config.zip', 'wp-config.php.zip', 'wp-config.db', 'wp-config.php.db', 'wp-config.dat','wp-config.php.dat', 'wp-config.tar.gz', 'wp-config.php.tar.gz', 'wp-config.back', 'wp-config.php.back', 'wp-config.test', 'wp-config.php.test']
backup = [
'wp-config.php~', 'wp-config.php.save', '.wp-config.php.bck',
'wp-config.php.bck', '.wp-config.php.swp', 'wp-config.php.swp',
'wp-config.php.swo', 'wp-config.php_bak', 'wp-config.bak',
'wp-config.php.bak', 'wp-config.save', 'wp-config.old',
'wp-config.php.old', 'wp-config.php.orig', 'wp-config.orig',
'wp-config.php.original', 'wp-config.original', 'wp-config.txt',
'wp-config.php.txt', 'wp-config.backup', 'wp-config.php.backup',
'wp-config.copy', 'wp-config.php.copy', 'wp-config.tmp',
'wp-config.php.tmp', 'wp-config.zip', 'wp-config.php.zip',
'wp-config.db', 'wp-config.php.db', 'wp-config.dat',
'wp-config.php.dat', 'wp-config.tar.gz', 'wp-config.php.tar.gz',
'wp-config.back', 'wp-config.php.back', 'wp-config.test',
'wp-config.php.test', "wp-config.php.1","wp-config.php.2",
"wp-config.php.3", "wp-config.php._inc", "wp-config_inc"]
for b in backup:
r = requests.get(self.url + b, headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r) and not "404" in r.text :
self.files.add(b)
print critical("A wp-config.php backup file has been found in: %s" % (self.url + b) )
@ -151,7 +170,8 @@ class Wordpress:
"""
def is_xml_rpc(self):
r = requests.get(self.url + "xmlrpc.php", headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r) and "404" in r.text :
if r.status_code == 405 :
self.files.add("xmlrpc.php")
print info("XML-RPC Interface available under: %s " % (self.url+"xmlrpc.php") )
@ -166,6 +186,7 @@ class Wordpress:
for directory, name in zip(directories,dir_name):
r = requests.get(self.url + directory, headers={"User-Agent":self.agent}, verify=False)
if "Index of" in r.text:
self.files.add(directory)
print warning("%s directory has directory listing enabled : %s" % (name, self.url + directory))
@ -176,6 +197,7 @@ class Wordpress:
def is_robots_text(self):
r = requests.get(self.url + "robots.txt", headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r) and not "404" in r.text :
self.files.add("robots.txt")
print info("robots.txt available under: %s " % (self.url+"robots.txt") )
lines = r.text.split('\n')
for l in lines:
@ -191,6 +213,7 @@ class Wordpress:
for f in files:
r = requests.get(self.url + f, headers={"User-Agent":self.agent}, verify=False)
if "200" in str(r) and not "404" in r.text :
self.files.add(f)
print info("%s available under: %s " % (f, self.url+f) )
"""
@ -233,4 +256,5 @@ class Wordpress:
print "Themes : %s" % self.themes
print "Agent : %s" % self.agent
print "Users : %s" % self.users
print "Files : %s" % self.files
print "---------------------------"

View File

@ -0,0 +1,28 @@
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8001:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
db_data:
#docker-compose up -d

View File

@ -33,9 +33,10 @@ if __name__ == "__main__":
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('--usernames', action ='store', dest='usernames', default='', help="Usernames to bruteforce separated with a ','")
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")
parser.add_argument('--debug', action ='store_const', const='debug', dest='debug', default=False, help="Enable a debugging flag")
results = parser.parse_args()
# Check wordpress url
@ -63,5 +64,9 @@ if __name__ == "__main__":
# Load plugins for more functions
Load_Plugins(wp)
# Debug
if results.debug:
wp.to_string()
else:
parser.print_help()