Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
Improved reliability of Thread Pool / Added many more enumeration types / list parsing capability / Ctrl+C to skip over enumeration phases /
  • Loading branch information
frizb authored Jun 21, 2017
1 parent 09e4d96 commit 264bcb9
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 79 deletions.
201 changes: 150 additions & 51 deletions Vanquish2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
# TODO: Import data into MSF database and generate pretty table reports of servers and ports
# TODO: Append the exact command that is used to the output text files for easy refernce in documentation
# TODO: Create a suggest only mode that dumps a list of commands to try rather than running anything
# TODO: Fix TCP / UDP / Multi Nmap scan merging - When multiple scans have the same port info,we get duplicate entries
# TODO: Do not enum closed or filtered ports
# TODO: Command replacer for lists of users, passwords and directory lists
# TODO: Add color and -color -colour flags to disable it
# TODO: Expand user enumeration
# Starts Fast moves to Through
# 1. NMAP Scan
# 2. Service Enumeration Scan
#
# TODO:
# 3. Finds relavant exploits and copies to a subfolder
# TODO: still havent finished the following features
# 3. Word list creation 1st pass
# Banner Grabx
# HTTP Enum
Expand All @@ -34,9 +34,10 @@
"""
Main application logic and automation functions
"""
from parser import ParserError

__version__ = '0.8'
__lastupdated__ = 'June 17, 2017'
__version__ = '0.9'
__lastupdated__ = 'June 18, 2017'
__nmap_folder__ = 'Nmap'

###
Expand All @@ -51,12 +52,16 @@
import argparse
import random
from pprint import pformat
from pprint import pprint
from shutil import copyfile
import json
import subprocess
from subprocess import call
import multiprocessing
import threading
import xml.etree.ElementTree as ET
from multiprocessing.dummy import Pool as ThreadPool
from subprocess import Popen, PIPE, STDOUT


# PROGRESS BAR - Thank you! clint.textui.progress
Expand Down Expand Up @@ -150,17 +155,23 @@ def bar(it, label='', width=32, hide=None, empty_char=BAR_EMPTY_CHAR,
bar.show(i + 1)

class logger:
DEBUG = False;
VERBOSE = False;
DEBUG = False
VERBOSE = False
DEBUG_FILE = None
VERBOSE_FILE = None

@staticmethod
def debug(msg):
if logger.DEBUG == True:
if logger.DEBUG_FILE is not None:
logger.DEBUG_FILE.write(msg+'\n')
elif logger.DEBUG == True:
print("[!] "+msg)

@staticmethod
def verbose(msg):
if logger.VERBOSE == True:
if logger.VERBOSE_FILE is not None:
logger.VERBOSE_FILE.write(msg + '\n')
elif logger.VERBOSE == True:
print("[*] "+msg)

class Vanquish:
Expand All @@ -183,12 +194,14 @@ def __init__(self, argv):
self.parser.add_argument("-reportFile", metavar='report', type=str, default="report.txt",
help='filename used for the report (default: %(default)s)')
self.parser.add_argument("-noResume", action='store_true', help='do not resume a previous session')
self.parser.add_argument("-range", metavar='IPs', type=str, nargs="+", default="",
help='a range to scan ex: 10.10.10.0/24')
self.parser.add_argument("-threadPool", metavar='threads', type=int, default="8",
help='Thread Pool Size (default: %(default)s)')
self.parser.add_argument("-phase", metavar='phase', type=str, default='', help='only execute a specific phase')
self.parser.add_argument("-noExploitSearch", action='store_true', help='disable searchspolit exploit searching')
self.parser.add_argument("-logging", action='store_true', help='enable verbose and debug data logging to files')
self.parser.add_argument("-verbose", action='store_true', help='display verbose details during the scan')
self.parser.add_argument("-debug", action='store_true', help='display debug details during the scan')

self.args = self.parser.parse_args()
self.hosts = self.args.hostFile

Expand All @@ -209,6 +222,21 @@ def __init__(self, argv):
#current enumeration phase command que
self.phase_commands = []

#Current Thread Pool command contents
self.thread_pool_commands = []
self.thread_pool_errors = []

# write errors to error log rather than display them on screen
self.command_error_log = open("commanderrorlog.txt", 'w')
if self.args.logging:
self.debug_log = open("debuglog.txt", 'w')
self.verbose_log = open("verboselog.txt", 'w')
logger.DEBUG_FILE= self.debug_log
logger.VERBOSE_FILE= self.verbose_log

sys.stderr = self.command_error_log
self.devnull = open(os.devnull, 'w')

# Scan the hosts using Nmap
# Create a thread pool and run multiple nmap sessions in parallel
def upfront_scan_hosts(self, hosts, command_label):
Expand All @@ -233,16 +261,11 @@ def upfront_scan_hosts(self, hosts, command_label):
logger.debug("scan_hosts() - command : " + command)

#results = pool.map(self.execute_scan, self.phase_commands)
for _ in bar(pool.imap_unordered(self.execute_scan, self.phase_commands), expected_size=len(self.phase_commands)):
for _ in bar(pool.imap_unordered(self.execute_command, self.phase_commands), expected_size=len(self.phase_commands)):
pass
pool.close()
pool.join()

def execute_scan(self, command):
logger.debug("execute_scan() - " + command)
stream = os.popen(command)
logger.debug("execute_scan() - COMPLETED! - " + command)

# Parse Nmap XML - Reads all the Nmap xml files in the Nmap folder
def parse_nmap_xml(self):
print "[+] Reading Nmap XML Output Files..."
Expand Down Expand Up @@ -312,18 +335,35 @@ def merge_two_dicts( x, y):

# find exploits from exploit db and copy them to service folder
# TODO: Copy results to service folders - update nmap_dict with other web app etc products and versions...
def exploit_search(self):
def exploit_search(self, command_label):
if self.args.noExploitSearch: return False
logger.debug("exploit_search()")
# Check nmap_dict
self.phase_commands = []
for host in self.nmap_dict:
for service in self.nmap_dict[host]['ports']:
if service.get('product', '') is not '' and service.get('version','') is not '':
version_digits = ' '.join(str(x) for x in re.findall(r'\d+',service.get('version','')))
stream = os.popen('searchsploit --json '+service.get('product', '')+" "+version_digits)
json_results = stream.read()
stream.close()
print json_results
version_digits = ' '.join(str(x) for x in re.findall(r'\d+', service.get('version', '')))
command_keys = {
'output': self.get_enumeration_path(host, service['name'],service['portid'], command_label),
'target': service.get('product', '')}
base, filename = os.path.split(command_keys['output']) # Resume file already exists
if not self.args.noResume and self.find_files(base, filename + ".*").__len__() > 0:
logger.verbose("exploit_search() -Exploit Search file already exists: "
+ command_keys['output'])
else:
self.execute_command(self.prepare_command(command_label, command_keys))
with open(command_keys['output']+".json") as data_file:
try:
data = json.load(data_file)
except:
continue
if len(data['RESULTS']) == 0:
os.remove(command_keys['output']+".json")
else: # copy exploits to exploit folder
exploits_path = os.path.join(base, "exploits")
if not os.path.exists(exploits_path): os.makedirs(exploits_path)
for exploit in data['RESULTS']:
exploit_base, exploit_filename = os.path.split(exploit['Path'])
copyfile(exploit['Path'], os.path.join(exploits_path,exploit_filename))

# Enumerate a phase
# phases are defined in attackplan.ini
Expand All @@ -333,6 +373,7 @@ def exploit_search(self):
def enumerate(self,phase_name):
logger.debug("Enumerate - "+ phase_name)
self.phase_commands = []
self.thread_pool_errors = []
for host in self.nmap_dict:
logger.debug("enumerate() - Host: " + host)
for service in self.nmap_dict[host]['ports']:
Expand All @@ -341,36 +382,55 @@ def enumerate(self,phase_name):
if not ('closed' in service['state'] or 'filtered' in service['state']) \
and ( service['name'].find(known_service) <> -1 or service['portid'] in ports.split(',')):
if self.plan.has_option(phase_name,known_service):
for command in self.plan.get(phase_name,known_service).split(','):
if command is not '':
for command_label in self.plan.get(phase_name,known_service).split(','):
if command_label is not '':
command_keys = {
'output': self.get_enumeration_path(host, service['name'],service['portid'], command),
'output': self.get_enumeration_path(host, service['name'],service['portid'], command_label),
'target': host,
'domain': self.args.domain,
'service': service['name'],
'port':service['portid']
'port':service['portid'],
}
base, filename = os.path.split(command_keys['output']) # Resume file already exists
if not self.args.noResume and self.find_files(base,filename+".*").__len__()>0:
logger.verbose("enumerate() - RESUME - output file already exists: "
+ command_keys['output'])
else:
self.phase_commands.append(self.prepare_command(command,command_keys))
logger.verbose("enumerate() - command : " + command)
command = self.prepare_command(command_label, command_keys)
#TODO: Check for dictionary tags / list tags
contains_list = False
for section in self.config.sections():
if "List" in section:
if command.find("<"+section+">") <> -1: # include entire list from section
contains_list = True
for item in self.config.items(section):
new_command = command
new_command = new_command.replace("<" + section + ">",self.config.get(section, item))
self.phase_commands.append(new_command)
else:
for item in self.config.items(section):
command = command.replace("<" + item[0] + ">",item[1])
if contains_list == False: self.phase_commands.append(command)
logger.verbose("enumerate() - command : " + command_label)
else:
logger.debug("\tenumerate() - NO command section found for phase: " + phase_name +
" service name: "+known_service )
pool = ThreadPool(self.args.threadPool)
#results = pool.map(self.execute_enumeration, self.phase_commands)
for _ in bar(pool.imap_unordered(self.execute_enumeration, self.phase_commands), expected_size=len(self.phase_commands)):
for _ in bar(pool.imap_unordered(self.execute_command, self.phase_commands), expected_size=len(self.phase_commands)):
pass
pool.close()
pool.join()

def execute_enumeration(self,enumerate_command):
logger.debug("execute_enumeration() - " + enumerate_command)
stream = os.popen(enumerate_command)
logger.debug("execute_enumeration() - COMPLETED! - " + enumerate_command)
def execute_command(self, command):
logger.debug("execute_enumeration() - " + command)
self.thread_pool_commands.append(command)
process = Popen(command, shell=True, stdin=PIPE, stderr=self.command_error_log, stdout=self.devnull)
process.stdin.close()
#if process.wait() != 0:
#logger.debug("execute_enumeration() - ERRORS EXECUTING: - " + command)
#self.thread_pool_errors.append(command)
logger.debug("execute_enumeration() - COMPLETED! - " + command)
self.thread_pool_commands.remove(command)

def get_enumeration_path(self, host, service, port, command):
ip_path = os.path.join(self.args.outputFolder, host.strip().replace(".","_"))
Expand Down Expand Up @@ -450,13 +510,14 @@ def banner_block():
# Entry point for command-line execution
##################################################################################

@property
def main(self):
start_time = time.time()
#sys.stderr = open("errorlog.txt", 'w')
print("[+] Configuration file: " + str(self.args.configFile))
print("[+] Attack plan file: " + str(self.args.attackPlanFile))
print("[+] Output Path: " + str(self.args.outputFolder))
print("[+] Host File: " + str(self.args.hostFile.name))
print("Configuration file: " + str(self.args.configFile))
print("Attack plan file: " + str(self.args.attackPlanFile))
print("Output Path: " + str(self.args.outputFolder))
print("Host File: " + str(self.args.hostFile.name))
logger.debug("DEBUG MODE ENABLED!")
logger.verbose("VERBOSE MODE ENABLED!")

Expand All @@ -473,38 +534,76 @@ def main(self):
print "[+] Starting upfront Nmap Scan..."
for scan_command in self.plan.get("Scans Start", "Order").split(","):
print "[+] Starting Scan Type: " + scan_command
self.upfront_scan_hosts(self.hosts, scan_command)
try:
if self.args.phase == '': self.upfront_scan_hosts(self.hosts, scan_command)
except KeyboardInterrupt:
logger.verbose("Keyboard Interrupt Detected... skipping "+scan_command)
print "\t[X] Keyboard Interrupt Detected... skipping "+scan_command
continue
except ValueError as err:
bar(self.phase_commands,expected_size=len(self.phase_commands))
if len(self.thread_pool_errors) > 0:
logger.verbose("[X] Phase completed but encountered the following errors: \n"
+ pformat(self.thread_pool_errors) + pformat(self.thread_pool_commands) )
print "[X] Phase completed but encountered the following errors: \n" \
+ pformat(self.thread_pool_errors) + pformat(self.thread_pool_commands)
continue

print "[+] Starting background Nmap Scan..."

# TODO background thread with long term comprehensive scan - restart enumeration it has finished -
# Start background Nmap port scans ... these will take time and will run concurrently with enumeration
#for scan_command in self.plan.get("Scans Start", "Order").split(","):
# self.upfront_scan_hosts(self.hosts, scan_command)
#thread = threading.Thread(target=self.background_scan_hosts, args=())
#thread.daemon = True # Daemonize thread
#thread.start() # Start the execution
#TODO background thread with long term comprehensive scan - restart enumeration it has finished -
# ensure resume is turned on

self.write_report_file(self.nmap_dict)
# Begin Enumeration Phases
print "[+] Starting enumeration..."
for phase in self.plan.get("Enumeration Plan","Order").split(","):
self.parse_nmap_xml()
self.write_report_file(self.nmap_dict)
# print "[+] Starting upfront Nmap Scan..."
# TODO Search for exploits
#self.exploit_search()
print "[+] Starting Phase: " + phase
self.enumerate(phase)

try:
if self.args.phase == phase or self.args.phase == '': self.enumerate(phase)
except KeyboardInterrupt:
logger.verbose("[X] Keyboard Interrupt Detected... exiting phase:: "+phase)
logger.verbose("[X] Thread Pool at Interrupt: \n" + pformat(self.thread_pool_commands))
print "[X] Keyboard Interrupt Detected... exiting phase: "+phase
print "[X] Thread Pool at Interrupt:"
pprint(self.thread_pool_commands)
continue
except ValueError as err:
bar(self.phase_commands, expected_size=len(self.phase_commands))
if len(self.thread_pool_errors) > 0:
logger.verbose("[X] Phase completed but encountered the following errors: \n"
+ pformat(self.thread_pool_errors) + pformat(self.thread_pool_commands) )
print "[X] Phase completed but encountered the following errors: \n" \
+ pformat(self.thread_pool_errors) + pformat(self.thread_pool_commands)
continue

try:
self.write_report_file(self.nmap_dict)
print "[+] Searching for matching exploits..."
self.exploit_search("SearchSploit JSON")
except:
bar(self.phase_commands, expected_size=len(self.phase_commands))

print "[+] Elapsed Time: " + time.strftime('%H:%M:%S', time.gmtime(time.time() - start_time))
logger.verbose("Goodbye!")
self.command_error_log.close()
if self.args.logging:
self.debug_log.close()
self.verbose_log.close()
return 0


def main(argv=None):
vanquish = Vanquish(argv if argv else sys.argv[1:])
return vanquish.main()
return vanquish.main


if __name__ == "__main__":
sys.exit(main())
Loading

0 comments on commit 264bcb9

Please sign in to comment.