226 lines
10 KiB
Python
226 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import configparser
|
|
import os
|
|
import sys
|
|
import time
|
|
from lib.utils import *
|
|
from lib.core import *
|
|
from lib.reports import *
|
|
from lib.utils import *
|
|
from lib.utils.Queue import Queue
|
|
import time
|
|
|
|
class SkipTargetInterrupt(Exception):
|
|
pass
|
|
|
|
class Controller(object):
|
|
|
|
def __init__(self, script_path, arguments, output):
|
|
self.script_path = script_path
|
|
self.exit = False
|
|
self.arguments = arguments
|
|
self.output = output
|
|
self.blacklists = self.getBlacklists()
|
|
self.fuzzer = None
|
|
self.recursive = self.arguments.recursive
|
|
self.excludeStatusCodes = self.arguments.excludeStatusCodes
|
|
self.recursive = self.arguments.recursive
|
|
self.directories = Queue()
|
|
self.excludeSubdirs = (arguments.excludeSubdirs if arguments.excludeSubdirs is not None else [])
|
|
self.output.printHeader(PROGRAM_BANNER)
|
|
self.dictionary = FuzzerDictionary(self.arguments.wordlist, self.arguments.extensions,
|
|
self.arguments.lowercase)
|
|
self.printConfig()
|
|
try:
|
|
for url in self.arguments.urlList:
|
|
try:
|
|
self.currentUrl = url
|
|
self.reportManager = ReportManager()
|
|
self.requester = Requester(url, cookie=self.arguments.cookie,
|
|
useragent=self.arguments.useragent, maxPool=self.arguments.threadsCount,
|
|
maxRetries=self.arguments.maxRetries, timeout=self.arguments.timeout,
|
|
ip=self.arguments.ip, proxy=self.arguments.proxy,
|
|
redirect=self.arguments.redirect)
|
|
for key, value in arguments.headers.items():
|
|
self.requester.setHeader(key, value)
|
|
# Initialize directories Queue with start Path
|
|
self.basePath = self.requester.basePath
|
|
if self.arguments.scanSubdirs is not None:
|
|
for subdir in self.arguments.scanSubdirs:
|
|
self.directories.put(subdir)
|
|
else:
|
|
self.directories.put('')
|
|
self.setupReports(self.requester)
|
|
|
|
self.output.printTarget(self.currentUrl)
|
|
self.fuzzer = Fuzzer(self.requester, self.dictionary, testFailPath=self.arguments.testFailPath,
|
|
threads=self.arguments.threadsCount)
|
|
self.wait()
|
|
except SkipTargetInterrupt:
|
|
continue
|
|
except RequestException as e:
|
|
self.output.printError('Unexpected error:\n{0}'.format(e.args[0]['message']))
|
|
exit(0)
|
|
except KeyboardInterrupt as SystemExit:
|
|
self.output.printError('\nCanceled by the user')
|
|
exit(0)
|
|
finally:
|
|
self.reportManager.save()
|
|
self.reportManager.close()
|
|
self.output.printWarning('\nTask Completed')
|
|
|
|
def printConfig(self):
|
|
self.output.printConfig( ', '.join(self.arguments.extensions), str(self.arguments.threadsCount),
|
|
str(len(self.dictionary)))
|
|
|
|
def getBlacklists(self):
|
|
blacklists = {}
|
|
for status in [400, 403, 500]:
|
|
blacklistFileName = FileUtils.buildPath(self.script_path, 'db')
|
|
blacklistFileName = FileUtils.buildPath(blacklistFileName, '{0}_blacklist.txt'.format(status))
|
|
if not FileUtils.canRead(blacklistFileName):
|
|
continue
|
|
blacklists[status] = []
|
|
for line in FileUtils.getLines(blacklistFileName):
|
|
# Skip comments
|
|
if line.startswith('#'):
|
|
continue
|
|
blacklists[status].append(line)
|
|
return blacklists
|
|
|
|
def setupReports(self, requester):
|
|
if self.arguments.autoSave:
|
|
basePath = ('/' if requester.basePath is '' else requester.basePath)
|
|
basePath = basePath.replace(os.path.sep, '.')[1:-1]
|
|
fileName = ('{0}_'.format(basePath) if basePath is not '' else '')
|
|
fileName += time.strftime('%y-%m-%d_%H-%M-%S.txt')
|
|
directoryName = '{0}'.format(requester.host)
|
|
directoryPath = FileUtils.buildPath(self.script_path, 'reports', directoryName)
|
|
outputFile = FileUtils.buildPath(directoryPath, fileName)
|
|
if not FileUtils.exists(directoryPath):
|
|
FileUtils.createDirectory(directoryPath)
|
|
if not FileUtils.exists(directoryPath):
|
|
self.output.printError("Couldn't create reports folder {0}".format(directoryPath))
|
|
sys.exit(1)
|
|
if FileUtils.canWrite(directoryPath):
|
|
report = None
|
|
if self.arguments.autoSaveFormat == 'simple':
|
|
report = SimpleReport(requester.host, requester.port, requester.protocol, requester.basePath,
|
|
outputFile)
|
|
if self.arguments.autoSaveFormat == 'json':
|
|
report = JSONReport(requester.host, requester.port, requester.protocol, requester.basePath,
|
|
outputFile)
|
|
else:
|
|
report = PlainTextReport(requester.host, requester.port, requester.protocol, requester.basePath,
|
|
outputFile)
|
|
self.reportManager.addOutput(report)
|
|
else:
|
|
self.output.printError("Can't write reports to {0}".format(directoryPath))
|
|
sys.exit(1)
|
|
if self.arguments.simpleOutputFile is not None:
|
|
self.reportManager.addOutput(SimpleReport(requester.host, requester.port, requester.protocol,
|
|
requester.basePath, self.arguments.simpleOutputFile))
|
|
if self.arguments.plainTextOutputFile is not None:
|
|
self.reportManager.addOutput(PlainTextReport(requester.host, requester.port, requester.protocol,
|
|
requester.basePath, self.arguments.plainTextOutputFile))
|
|
if self.arguments.jsonOutputFile is not None:
|
|
self.reportManager.addOutput(JSONReport(requester.host, requester.port, requester.protocol,
|
|
requester.basePath, self.arguments.jsonOutputFile))
|
|
|
|
def handleInterrupt(self):
|
|
self.output.printWarning('CTRL+C detected: Pausing threads, please wait...')
|
|
self.fuzzer.pause()
|
|
try:
|
|
while True:
|
|
msg = "[e]xit / [c]ontinue"
|
|
if not self.directories.empty():
|
|
msg += " / [n]ext"
|
|
if len(self.arguments.urlList) > 1:
|
|
msg += " / [s]kip target"
|
|
self.output.printInLine(msg + ': ')
|
|
|
|
option = input()
|
|
if option.lower() == 'e':
|
|
self.exit = True
|
|
self.fuzzer.stop()
|
|
raise KeyboardInterrupt
|
|
elif option.lower() == 'c':
|
|
self.fuzzer.play()
|
|
return
|
|
elif not self.directories.empty() and option.lower() == 'n':
|
|
self.fuzzer.stop()
|
|
return
|
|
elif len(self.arguments.urlList) > 1 and option.lower() == 's':
|
|
raise SkipTargetInterrupt
|
|
else:
|
|
continue
|
|
except KeyboardInterrupt as SystemExit:
|
|
self.exit = True
|
|
raise KeyboardInterrupt
|
|
|
|
def processPaths(self):
|
|
try:
|
|
path = self.fuzzer.getPath()
|
|
while path is not None:
|
|
try:
|
|
if path.status is not 0:
|
|
if path.status not in self.excludeStatusCodes and (self.blacklists.get(path.status) is None
|
|
or path.path not in self.blacklists.get(path.status)):
|
|
self.output.printStatusReport(path.path, path.response)
|
|
self.addDirectory(path.path)
|
|
self.reportManager.addPath(self.currentDirectory + path.path, path.status, path.response)
|
|
self.index += 1
|
|
self.output.printLastPathEntry(path, self.index, len(self.dictionary))
|
|
path = self.fuzzer.getPath()
|
|
except (KeyboardInterrupt, SystemExit) as e:
|
|
self.handleInterrupt()
|
|
if self.exit:
|
|
raise e
|
|
else:
|
|
pass
|
|
except (KeyboardInterrupt, SystemExit) as e:
|
|
if self.exit:
|
|
raise e
|
|
self.handleInterrupt()
|
|
if self.exit:
|
|
raise e
|
|
else:
|
|
pass
|
|
self.fuzzer.wait()
|
|
|
|
def wait(self):
|
|
while not self.directories.empty():
|
|
self.index = 0
|
|
self.currentDirectory = self.directories.get()
|
|
self.output.printWarning('[{1}] Starting: {0}'.format(self.currentDirectory, time.strftime('%H:%M:%S')))
|
|
self.fuzzer.requester.basePath = '{0}{1}'.format(self.basePath, self.currentDirectory)
|
|
self.output.basePath = '{0}{1}'.format(self.basePath, self.currentDirectory)
|
|
self.fuzzer.start()
|
|
self.processPaths()
|
|
return
|
|
|
|
def addDirectory(self, path):
|
|
if self.recursive == False:
|
|
return False
|
|
if path.endswith('/'):
|
|
if path in [directory + '/' for directory in self.excludeSubdirs]:
|
|
return False
|
|
if self.currentDirectory == '':
|
|
self.directories.put(path)
|
|
else:
|
|
self.directories.put('{0}{1}'.format(self.currentDirectory, path))
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
MAYOR_VERSION = 0
|
|
MINOR_VERSION = 3
|
|
REVISION = 0
|
|
global PROGRAM_BANNER
|
|
PROGRAM_BANNER = \
|
|
r""" __
|
|
_|. _ _ _) _ _ _|_ v{0}.{1}.{2}
|
|
(_||| _) __)(_|| (_| )
|
|
""".format(MAYOR_VERSION, MINOR_VERSION, REVISION) |