def parseConfig(self): config = DefaultConfigParser() configPath = FileUtils.buildPath(self.script_path, "default.conf") config.read(configPath) # General self.threadsCount = config.safe_getint("general", "threads", 10, list(range(1, 50))) self.excludeStatusCodes = config.safe_get("general", "exclude-status", None) self.redirect = config.safe_getboolean("general", "follow-redirects", False) self.recursive = config.safe_getboolean("general", "recursive", False) self.recursive_level_max = config.safe_getint("general", "recursive-level-max", 1) self.suppressEmpty = config.safe_getboolean("general", "suppress-empty", False) self.testFailPath = config.safe_get("general", "scanner-fail-path", "").strip() self.saveHome = config.safe_getboolean("general", "save-logs-home", False) # Reports self.autoSave = config.safe_getboolean("reports", "autosave-report", False) self.autoSaveFormat = config.safe_get("reports", "autosave-report-format", "plain", ["plain", "json", "simple"]) # Dictionary self.wordlist = config.safe_get("dictionary", "wordlist", FileUtils.buildPath(self.script_path, "db", "dicc.txt")) self.lowercase = config.safe_getboolean("dictionary", "lowercase", False) self.forceExtensions = config.safe_get("dictionary", "force-extensions", False) # Connection self.useRandomAgents = config.safe_get("connection", "random-user-agents", False) self.useragent = config.safe_get("connection", "user-agent", None) self.delay = config.safe_get("connection", "delay", 0) self.timeout = config.safe_getint("connection", "timeout", 30) self.maxRetries = config.safe_getint("connection", "max-retries", 5) self.proxy = config.safe_get("connection", "http-proxy", None) self.httpmethod = config.safe_get("connection", "httpmethod", "get", ["get", "head", "post"]) self.requestByHostname = config.safe_get("connection", "request-by-hostname", False)
def parseConfig(self): config = DefaultConfigParser() configPath = FileUtils.buildPath(self.script_path, "default.conf") config.read(configPath) # General self.threadsCount = config.safe_getint("general", "threads", 10, list(range(1, 50))) self.excludeStatusCodes = config.safe_get("general", "exclude-status", None) self.redirect = config.safe_getboolean("general", "follow-redirects", False) self.recursive = config.safe_getboolean("general", "recursive", False) self.testFailPath = config.safe_get("general", "scanner-fail-path", "").strip() self.saveHome = config.safe_getboolean("general", "save-logs-home", False) # Reports self.autoSave = config.safe_getboolean("reports", "autosave-report", False) self.autoSaveFormat = config.safe_get("reports", "autosave-report-format", "plain", ["plain", "json", "simple"]) # Dictionary self.wordlist = config.safe_get( "dictionary", "wordlist", FileUtils.buildPath(self.script_path, "db", "dicc.txt")) self.lowercase = config.safe_getboolean("dictionary", "lowercase", False) self.forceExtensions = config.safe_get("dictionary", "force-extensions", False) # Connection self.useRandomAgents = config.safe_get("connection", "random-user-agents", False) self.useragent = config.safe_get("connection", "user-agent", None) self.timeout = config.safe_getint("connection", "timeout", 30) self.maxRetries = config.safe_getint("connection", "max-retries", 5) self.proxy = config.safe_get("connection", "http-proxy", None)
def __check_args_attack_bruteforce(self): """Check arguments for subcommand Attack > Bruteforce options""" #for f in self.args.userlist, self.args.passlist, self.args.weblist: for f in self.args.userlist, self.args.passlist: if f: if not FileUtils.can_read(f): logger.error('File {file} does not exist or cannot be read'.format( file=f)) return False return True
def createToolDirectory(self, output): """ Create the tool directory if necessary @Args output: CLIOutput instance @Returns Boolean indicating operation status """ if FileUtils.is_dir(self.tool_dir): output.printInfo('Directory "{0}" already exists'.format( self.tool_dir)) return True try: FileUtils.create_directory(self.tool_dir) except Exception as e: output.printError( 'Unable to create new directory "{0}": {1}'.format( self.tool_dir, e)) return False output.printInfo('New directory "{0}" created'.format(self.tool_dir)) return True
def __parse_all_conf_files(self, files): """ Parse all configuration files into the settings directory. Initialize ServicesConfig object with list of supported services. :param list files: List of files in settings directory """ services = list() for f in files: name = FileUtils.remove_ext(f).lower().strip() if name not in (INSTALL_STATUS_CONF_FILE, TOOLBOX_CONF_FILE, ATTACK_PROFILES_CONF_FILE): services.append(name) full_path = FileUtils.concat_path(SETTINGS_DIR, f) self.config_parsers[name] = DefaultConfigParser() # Utf8 to avoid encoding issues self.config_parsers[name].read(full_path, 'utf8') services.append('multi') # Add support for special "multi" service self.services = ServicesConfig(services)
def parseConfig(self): config = DefaultConfigParser() configPath = FileUtils.buildPath(self.script_path, "default.conf") config.read(configPath) # General self.threadsCount = config.safe_getint("general", "threads", 10, range(1, 50)) self.excludeStatusCodes = config.safe_get("general", "exclude-status", None) self.redirect = config.safe_getboolean("general", "follow-redirects", False) self.recursive = config.safe_getboolean("general", "recursive", False) self.testFailPath = config.safe_get("general", "test-fail-path", "youCannotBeHere7331").strip() # Reports self.autoSave = config.safe_getboolean("reports", "autosave-report", False) self.autoSaveFormat = config.safe_get("reports", "autosave-report-format", "plain", ["plain", "json", "simple"]) # Dictionary self.wordlist = config.safe_get("dictionary", "wordlist", FileUtils.buildPath(self.script_path, "db", "dicc.txt")) self.lowercase = config.safe_getboolean("dictionary", "lowercase", False) # Connection self.useragent = config.safe_get("connection", "user-agent", None) self.timeout = config.safe_getint("connection", "timeout", 30) self.maxRetries = config.safe_getint("connection", "max-retries", 5) self.proxy = config.safe_get("connection", "http-proxy", None)
def __create_tool_dir(self): """ Create the tool directory if necessary. :return: Status :rtype: bool """ if self.tool_dir: if FileUtils.is_dir(self.tool_dir): logger.info('Directory "{dir}" already exists'.format(dir=self.tool_dir)) return True try: FileUtils.create_directory(self.tool_dir) except Exception as e: logger.error('Unable to create new directory "{dir}": {exc}'.format( dir=self.tool_dir, exc=e)) return False logger.info('New directory "{dir}" created'.format(dir=self.tool_dir)) return True else: return False
def checkInstall(self, output): """ Check if the tool is correctly installed. Basically, it runs the installed tool without any option @Args output: CLIOutput instance @Returns Boolean indicating status """ # Directory where the tool should be installed if not FileUtils.is_dir(self.tool_dir): output.printFail( 'Directory where the tool should be installed (\'{0}\') does not exist !' .self.tool_dir) return False # Try to run the tool output.printInfo('Trying to run the tool {0}...'.format(self.name)) splitted = self.command.strip().split(' ') cmd = '' if splitted[0].lower() == 'sudo' and len(splitted) > 1: cmd = 'sudo ' splitted = splitted[1:] cmd += splitted[0] if splitted[0].lower() in ('python', 'python3', 'perl', 'ruby') and len(splitted) > 1: if splitted[1] != '-m': cmd += ' {0}'.format(splitted[1]) elif len(splitted) > 2: cmd += ' -m {0}'.format(splitted[2]) elif splitted[0].lower() == 'java' and len(splitted) > 2: if splitted[1].lower() == '-jar': cmd += ' -jar {0}'.format(splitted[2]) c = Command(self.tool_dir, cmd, None, self.toolbox_dir, None, None, None, None) cmd_check = c.getStandardCommandLine() cmd_check_print = cmd_check[cmd_check.index(';') + 1:].strip() output.printBeginCmd(cmd_check_print) process = ProcessLauncher(cmd_check, output, None) process.start() output.printEndCmd() # Prompt output.printPrompt( 'Does the tool {0} seem to be running correctly ? [Y/n]'.format( self.name)) return CLIUtils.promptYesNo(output, default='Y')
def remove(self, settings): """ Remove the tool: - Remove tool directory into toolbox - Change install status to false. :param Settings settings: Settings from config files :return: Removal status :rtype: bool """ # Delete tool directory if tool was installed inside toolbox directory if self.install_command: if not FileUtils.is_dir(self.tool_dir): logger.warning('Directory "{dir}" does not exist'.format( dir=self.tool_dir)) return False elif not FileUtils.remove_directory(self.tool_dir): logger.error('Unable to delete directory "{dir}". ' \ 'Check permissions and/or re-run with sudo'.format( dir=self.tool_dir)) return False else: logger.success('Tool directory "{dir}" deleted'.format( dir=self.tool_dir)) # Make sure "installed" option in config file is set to False if settings.change_installed_status(self.target_service, self.name, install_status=False): logger.success('Tool marked as uninstalled') else: logger.error('An unexpected error occured when trying to mark the tool ' \ 'as uninstalled !') return False self.installed = False return True
def __init__(self, settings_dir, toolbox_dir, output): """ Initialize Settings object @Args settings_dir: directory where config files are stored toolbox_dir: directory where the toolbox is stored output: Instance of CLIOutput """ self.settings_dir = settings_dir self.toolbox_dir = toolbox_dir self.output = output # config_parsers: dict of config_parsers indexed by conf_filename self.config_parsers = {} # general_settings: 2 dimensions dict - [service_name][option_name] self.general_settings = {} self.toolbox = Toolbox(self) # Check directory and presence of *.conf files if not FileUtils.is_dir(settings_dir): self.output.printError( 'Configuration directory ({0}) does not exist'.format( settings_dir)) raise ValueError files = FileUtils.list_directory(settings_dir) for f in files: if not FileUtils.check_extension(f, CONF_EXT): files.remove(f) if not files: self.output.printError( 'Configuration directory ({0}) does not store any *.conf file'. format(settings_dir)) raise ValueError # Parse config files self.parseAllConfFiles(files)
def defineOutputDir(self, output_dir, host, port, protocol, service): """ Define the output directory name for storing results @Args output_dir: name coming from argument (if defined by user) host: target host name port: target port number protocol: protocol tcp or udp service: @Returns the final name to use as directory name """ if output_dir: return output_dir return FileUtils.concat_path(DEFAULT_OUTPUT_DIR, \ 'output_{0}-{1}{2}_{3}_{4}'.format(host, port, protocol, service, str(time.time()).split('.')[0]))
def removeTool(self, settings, output): """ Remove the tool: - Remove tool directory into toolbox - Change install status to false @Args settings: Settings instance output: CLIOutput instance @Returns Boolean indicating operation status """ if self.tooltype == ToolType.USE_MULTI: output.printInfo('"{0}" is a reference to the tool "{1}" used for multi services. Not deleted'.format(\ self.name, self.tool_ref_name)) return False if not FileUtils.is_dir(self.tool_dir): output.printInfo('Directory "{0}" does not exist'.format( self.tool_dir)) else: if not FileUtils.remove_directory(self.tool_dir): output.printFail( 'Unable to delete directory "{0}". Check permissions and/or re-run with sudo' .format(self.tool_dir)) return False else: output.printInfo('Directory "{0}" deleted'.format( self.tool_dir)) # Make sure "installed" option in config file is set to False if not settings.changeInstalledOption(self.service_name, self.name, False): output.printError( 'An unexpected error occured when trying to mark the tool as uninstalled !' ) self.installed = False return True
def statusReport(self, path, response): """ @brief Given URL Path Response Status Report @pattern [23:59:59] Status Code (e.g. 302) - File Size (e.g. 222 B) - /php -> Target URL """ with self.mutex: contentLength = None status = response.status # Check Blacklist if status in self.blacklists and path in self.blacklists[status]: return # Format Messages try: size = int(response.headers['content-length']) except (KeyError, ValueError): size = len(response.body) finally: contentLength = FileUtils.sizeIEC(size) if self.basePath is None: showPath = urllib.parse.urljoin("/", path) else: showPath = urllib.parse.urljoin("/", self.basePath) showPath = urllib.parse.urljoin(showPath, path) # Concatenate The URL Response Report Message message = '[{0}] {1} - {2} - {3}'.format( time.strftime('%H:%M:%S'), status, contentLength.rjust(6, ' '), showPath) # HTTP Response Code List if status == 200: # OK message = Fore.GREEN + message + Style.RESET_ALL elif status == 401: # Unauthorized message = Fore.YELLOW + message + Style.RESET_ALL elif status == 403: # Forbidden message = Fore.RED + message + Style.RESET_ALL # Check If Redirect --> Response Code # 301 (Moved Permanently), 302 (Found -> Moved temporarily"), 307 (Temporary Redirect) elif (status in [301, 302, 307]) and ('location' in [ h.lower() for h in response.headers ]): message = Fore.CYAN + message + Style.RESET_ALL message += ' -> {0}'.format(response.headers['location']) self.newLine(message)
def getBlacklists(self): """ @brief Get The Local Preset Status Code Related Blacklist @param self The Object @return Target Blacklist Dictionary """ blacklists = {} # Target Dictionary (Consists of Status Code Lists) # 400 -> Bad Request, 403 -> Forbidden, 500 ->Internal Server Error db_Path = FileUtils.createPath(self.script_path, 'db') # Local DB Path for status in [400, 403, 500]: blacklistFileName = FileUtils.createPath( # Join Status Code as Filename (e.g. 403_blacklist.txt) db_Path, '{}_blacklist.txt'.format(status)) blacklists[status] = [] # Status Code List Contained In Dictionary if not FileUtils.canRead(blacklistFileName): continue # Skip Unreadable File for line in FileUtils.getLines(blacklistFileName): if line.lstrip().startswith('#'): continue # Skip Comments In The File blacklists[status].append(line) return blacklists
def checkArgsOutput(self): """ Check arguments related to Output """ if not self.contparsing or self.args.list_specific: return if self.args.output_dir: self.args.output_dir = self.args.output_dir.strip() else: self.args.output_dir = self.defineOutputDir( self.args.output_dir, self.target.host, self.target.port, self.target.protocol, self.target.service) if FileUtils.is_dir(self.args.output_dir): self.output.printError( 'Directory "{0}" already exists. Choose another name.'.format( self.args.output_dir)) sys.exit(0) if not FileUtils.create_directory(self.args.output_dir): self.output.printError( 'Impossible to create output directory "{0}". Check permissions' .format(self.args.output_dir)) sys.exit(0)
def __init__(self, settings_dir, toolbox_dir, output): """ Constructor of Settings object @Args settings_dir: directory where config files are stored toolbox_dir: directory where the toolbox is stored output: Instance of CLIOutput """ self.settings_dir = settings_dir self.toolbox_dir = toolbox_dir self.output = output # config_parsers: dict of config_parsers indexed by service_name self.config_parsers = {} # general_settings: 2 dimensions dict - [service_name][setting] self.general_settings = {} self.toolbox = Toolbox(self) # Check directory and presence of *.conf files if not FileUtils.is_dir(settings_dir): self.output.printError( 'Configuration directory ({0}) does not exist'.format( settings_dir)) sys.exit(0) files = FileUtils.list_directory(settings_dir) for f in files: if not FileUtils.check_extension(f, CONF_EXT): files.remove(f) if not files: self.output.printError( 'Configuration directory ({0}) does not store any *.conf file'. format(settings_dir)) sys.exit(0) # Parse config files # i.e. extract tools categories and optional/specific settings for each service self.parseConfFiles(files)
def recording(self): # Override """ @report_pattern HTTP Response Status Code + Package Size + URL + Path """ record = '' # self.pathList.append((path, status, contentLength)) for path, status, contentLength in self.pathList: record += '{0} '.format(status) record += '{0} '.format( FileUtils.sizeIEC(contentLength).rjust(6, ' ')) record += '{0}://{1}:{2}/'.format(self.protocol, self.host, self.port) record += ('{0}\n'.format(path) if self.basePath is '' else '{0}/{1}\n'.format(self.basePath, path)) return record
def generate(self): result = "" for path, status, contentLength, location in self.getPathIterator(): result += "{0} ".format(status) result += "{0} ".format( FileUtils.sizeHuman(contentLength).rjust(6, " ")) result += "{0}://{1}:{2}/".format(self.protocol, self.host, self.port) result += ("{0}".format(path) if self.basePath == "" else "{0}/{1}".format(self.basePath, path)) if location: result += " -> REDIRECTS TO: {0}".format(location) result += "\n" return result
def save(self, conf_filename): """ Save change permanently into the file :param conf_filename: Settings filename without extension :return: Boolean indicating status """ try: config_file = FileUtils.concat_path(SETTINGS_DIR, conf_filename+CONF_EXT) with open(config_file, 'w') as handle: self.config_parsers[conf_filename].write(handle) # Re-read to take change into account self.config_parsers[conf_filename].read(config_file, 'utf8') # warning: takes filename as param return True except: logger.error('Error occured when saving changes in settings file named "{filename}"'.format( filename=conf_filename)) traceback.print_exc() return False
def saveSettings(self, service_name): """ Save settings into config file. Make sure changes are thus taken into account. @Args service_name: service targeted by the tool @Returns Boolean indicating operation status """ try: config_file = FileUtils.concat_path(self.settings_dir, service_name + '.conf') with open(config_file, 'w') as handle: self.config_parsers[service_name].write(handle) # Re-read to take change into account self.config_parsers[service_name].read( config_file) # warning: takes filename as param return True except: traceback.print_exc() return False
def saveSettings(self, conf_filename): """ Save settings into config file. Make sure changes are thus taken into account. @Args conf_filename: configuration filename (without extension) @Returns Boolean indicating operation status """ try: config_file = FileUtils.concat_path(self.settings_dir, conf_filename + CONF_EXT) with open(config_file, 'w') as handle: self.config_parsers[conf_filename].write(handle) # Re-read to take change into account self.config_parsers[conf_filename].read( config_file) # warning: takes filename as param return True except: traceback.print_exc() return False
def __init__(self, script_path): global VERSION self.script_path = script_path self.arguments = self.parse_arguments() self.scanners = [HttpAuthScanner, FormDataScanner] self.running = False self.setup_logger() PROGRAM_BANNER = open( FileUtils.build_path(script_path, "lib", "controller", "commandline", "banner.txt")).read().format(**VERSION) print(PROGRAM_BANNER) self.exit = False self.dictionary = Dictionary() self.parse_settings() self.print_lock = threading.Lock() for wordlist in self.get_wordlists(): self.dictionary.append_from_wordlist(wordlist) self.start_loop() self.logger.info("Finished")
def getReportsPath(self, requester): if self.arguments.autoSave: # Default True basePath = ('/' if requester.basePath is '' else requester.basePath) basePath = basePath.replace(os.path.sep, '.')[1:-1] # Generate File Name & Directory Path fileName = None directoryPath = None if self.batch: fileName = requester.host directoryPath = self.batchDirectoryPath else: fileName = ('{}_'.format(basePath) if basePath is not '' else '') fileName += time.strftime('%y-%m-%d_%H-%M-%S') directoryPath = FileUtils.createPath(self.savePath, 'reports', requester.host) if not FileUtils.exists(directoryPath): FileUtils.createDirectory(directoryPath) if not FileUtils.exists(directoryPath): self.output.error("Cannot Create Reports Folder {}".format( directoryPath)) sys.exit(1) # Generate Reports File Path outputFile = FileUtils.createPath(directoryPath, fileName) # Rename If Duplicate File is Found In Target Directory if FileUtils.exists(outputFile): i = 2 while FileUtils.exists(outputFile + "_" + str(i)): i += 1 outputFile += "_" + str(i) return outputFile, directoryPath
def __generate_index(self): """ Generate HTML index code from template "index.tpl.html" """ tpl = FileUtils.read(REPORT_TPL_DIR + '/index.tpl.html') tpl = tpl.replace('{{MISSION_NAME}}', self.mission) tpl = tpl.replace('{{TABLE_SERVICES_CONTENT}}', self.__generate_table_services()) tpl = tpl.replace('{{TABLE_HOSTS_CONTENT}}', self.__generate_table_hosts()) tpl = tpl.replace('{{TABLE_WEB_CONTENT}}', self.__generate_table_web()) tpl = tpl.replace('{{TABLE_OPTIONS_CONTENT}}', self.__generate_table_options()) tpl = tpl.replace('{{TABLE_PRODUCTS_CONTENT}}', self.__generate_table_products()) tpl = tpl.replace('{{TABLE_CREDS_CONTENT}}', self.__generate_table_credentials()) tpl = tpl.replace('{{TABLE_VULNS_CONTENT}}', self.__generate_table_vulns()) return tpl
def saveHomeOption(self): # If saveHome == True --> Get 'savePath' Path savePath = self.getSavePath() if not FileUtils.exists(savePath): # Check Existence FileUtils.createDirectory(savePath) if FileUtils.exists( savePath) and not FileUtils.isDir(savePath): # Check Status self.output.error( "NOT Available ! {} is a File, Should be a Directory.\nPlease Check Again." .format(savePath)) exit(1) if not FileUtils.canWrite(savePath): # Check Writability self.output.error( "Directory {} is Not Writable.\nPlease Check Again.".format( savePath)) exit(1) return savePath
def __init__(self, name, description, target_service, installed, last_update='', virtualenv='', install_command=None, update_command=None, check_command=None): """ Construct the Tool object. :param str name: Name of the tool ([a-zA-Z0-9-_]) :param str description: Short description of the tool :param str target_service: Name of service targeted by this tool (might be "multi" for tools that could be used against various services) :param bool installed: Install status :param str last_update: Datetime of the last updated ('' if not installed) :param str virtualenv: Language of virtual environment to use (optional) :param Command install_command: Install command (optional) :param Command update_command: Update command (optional) :param Command check_command: Command to check install (optional) """ self.name = name self.description = description self.target_service = target_service self.installed = installed if isinstance(installed, bool) else False self.last_update = last_update self.virtualenv = virtualenv self.install_command = install_command self.update_command = update_command self.check_command = check_command self.tool_dir = FileUtils.absolute_path( '{toolbox}/{service}/{name}'.format( toolbox=TOOLBOX_DIR, service=self.target_service, name=self.name)) if self.install_command else ''
def setupReports(self, requester): if self.arguments.autoSave: # Default True # Auto Save Format Option if FileUtils.canWrite(self.directoryPath): report = None if self.arguments.autoSaveFormat == 'simple': report = SimpleReport(requester.host, requester.port, requester.protocol, requester.basePath, self.reportsPath) if self.arguments.autoSaveFormat == 'json': report = JSONReport(requester.host, requester.port, requester.protocol, requester.basePath, self.reportsPath) else: # PlainTextReport report = PlainTextReport( requester.host, requester.port, requester.protocol, requester.basePath, self.reportsPath) self.reportController.addReport(report) else: self.output.error("Cannot Write Reports to {}".format( self.directoryPath)) sys.exit(1) # Save Format Option if self.arguments.simpleOutputFile is not None: # Simple Format self.reportController.addReport( SimpleReport(requester.host, requester.port, requester.protocol, requester.basePath, self.arguments.simpleOutputFile)) if self.arguments.plainTextOutputFile is not None: # Plain Text Format self.reportController.addReport( PlainTextReport(requester.host, requester.port, requester.protocol, requester.basePath, self.arguments.plainTextOutputFile)) if self.arguments.jsonOutputFile is not None: # JSON Format self.reportController.addReport( JSONReport(requester.host, requester.port, requester.protocol, requester.basePath, self.arguments.jsonOutputFile))
def parseConfig(self): config = DefaultConfigParser() configPath = FileUtils.buildPath(os.path.expanduser('~'), ".dirsearch", "dirsearch.conf") if not FileUtils.exists(configPath): FileUtils.createDirectory(os.path.dirname(configPath)) shutil.copyfile(FileUtils.buildPath(self.script_path, 'default.conf'), configPath) config.read(configPath) # General self.threadsCount = config.safe_getint("general", "threads", 10, list(range(1, 50))) self.excludeStatusCodes = config.safe_get("general", "exclude-status", None) self.redirect = config.safe_getboolean("general", "follow-redirects", False) self.recursive = config.safe_getboolean("general", "recursive", False) self.recursive_level_max = config.safe_getint("general", "recursive-level-max", 1) self.suppressEmpty = config.safe_getboolean("general", "suppress-empty", False) self.testFailPath = config.safe_get("general", "scanner-fail-path", "").strip() self.saveHome = config.safe_getboolean("general", "save-logs-home", False) # Reports self.autoSave = config.safe_getboolean("reports", "autosave-report", False) self.autoSaveFormat = config.safe_get("reports", "autosave-report-format", "plain", ["plain", "json", "simple"]) # Dictionary self.wordlist = config.safe_get("dictionary", "wordlist", FileUtils.buildPath(self.script_path, "db", "dicc.txt")) self.lowercase = config.safe_getboolean("dictionary", "lowercase", False) self.forceExtensions = config.safe_get("dictionary", "force-extensions", False) # Connection self.useRandomAgents = config.safe_get("connection", "random-user-agents", False) self.useragent = config.safe_get("connection", "user-agent", None) self.delay = config.safe_get("connection", "delay", 0) self.timeout = config.safe_getint("connection", "timeout", 30) self.maxRetries = config.safe_getint("connection", "max-retries", 5) self.proxy = config.safe_get("connection", "http-proxy", None) self.httpmethod = config.safe_get("connection", "httpmethod", "get", ["get", "head", "post"]) self.requestByHostname = config.safe_get("connection", "request-by-hostname", False)
def parser(self): tmp_dict = [] for line in FileUtils.getLines(self.dicfile): #line的格式为admin:admin tmp_dict.append(line) return tmp_dict
def run(self): # Create report directory dirname = '{mission}-{datetime}'.format( mission=StringUtils.clean(self.mission.replace(' ', '_'), allowed_specials=('_', '-')), datetime=datetime.datetime.now().strftime('%Y%m%d%H%M%S')) self.output_path = self.output_path + '/' + dirname if not FileUtils.create_directory(self.output_path): logger.error('Unable to create report directory: "{path}"'.format( path=self.output_path)) return False # Retrieve all services in selected mission req = ServicesRequester(self.sqlsession) req.select_mission(self.mission) services = req.get_results() # Generate screenshots processor = ScreenshotsProcessor(self.mission, self.sqlsession) processor.run() screens_dir = self.output_path + '/screenshots' if not FileUtils.create_directory(screens_dir): logger.warning( 'Unable to create screenshots directory: "{path}"'.format( path=screens_dir)) else: for service in services: if service.name == 'http' and service.screenshot is not None \ and service.screenshot.status == ScreenStatus.OK: img_name = 'scren-{ip}-{port}-{id}'.format( ip=str(service.host.ip), port=service.port, id=service.id) path = screens_dir + '/' + img_name ImageUtils.save_image(service.screenshot.image, path + '.png') ImageUtils.save_image(service.screenshot.thumbnail, path + '.thumb.png') # Create index.html html = self.__generate_index() if FileUtils.write(self.output_path + '/index.html', html): logger.info('index.html file generated') else: logger.error('An error occured while generating index.html') return False # Create results-<service>.html (1 for each service) for service in services: # Useless to create page when no check has been run for the service if len(service.results) == 0: continue html = self.__generate_results_page(service) # Create a unique name for the service HTML file filename = 'results-{ip}-{port}-{service}-{id}.html'.format( ip=str(service.host.ip), port=service.port, service=service.name, id=service.id) if FileUtils.write(self.output_path + '/' + filename, html): logger.info( '{filename} file generated'.format(filename=filename)) else: logger.error( 'An error occured while generating {filename}'.format( filename=filename)) return False logger.success('HTML Report written with success in: {path}'.format( path=self.output_path)) logger.info('Important: If running from Docker container, make sure to run ' \ '"xhost +" on the host before') if Output.prompt_confirm('Would you like to open the report now ?', default=True): webbrowser.open(self.output_path + '/index.html') return True
def __generate_table_web(self): """ Generate the table with HTTP services registered in the mission """ req = ServicesRequester(self.sqlsession) req.select_mission(self.mission) filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition('http', FilterData.SERVICE_EXACT)) req.add_filter(filter_) services = req.get_results() if len(services) == 0: html = """ <tr class="notfound"> <td colspan="7">No record found</td> </tr> """ else: html = '' # Unavailable thumbnail with open(REPORT_TPL_DIR + '/../img/unavailable.png', 'rb') as f: unavailable_b64 = base64.b64encode(f.read()).decode('ascii') for service in services: # Results HTML page name results = 'results-{ip}-{port}-{service}-{id}.html'.format( ip=str(service.host.ip), port=service.port, service=service.name, id=service.id) # Encrypted ? (SSL/TLS) enc = '<span class="mdi mdi-lock" title="SSL/TLS encrypted"></span>' \ if service.is_encrypted() else '' # Web technos (in a specific order) # try: # technos = ast.literal_eval(service.web_technos) # except Exception as e: # logger.debug('Error when retrieving "web_technos" field ' \ # 'from db: {exc} for {service}'.format( # exc=e, service=service)) # technos = list() # tmp = list() # for t in technos: # tmp.append('{}{}{}'.format( # t['name'], # ' ' if t['version'] else '', # t['version'] if t['version'] else '')) # webtechnos = ' | '.join(tmp) webtechnos = '' product_types = ('web-server', 'web-appserver', 'web-cms', 'web-language', 'web-framework', 'web-jslib') for t in product_types: product = service.get_product(t) if product: webtechnos += '<span class="badge badge-{type} badge-light">' \ '{name}{version}</span>'.format( type=t, name=product.name, version=' '+str(product.version) \ if product.version else '') # Web Application Firewall product = service.get_product('web-application-firewall') waf = '' if product: waf = '<span class="badge badge-web-application-firewall ' \ 'badge-light">{name}{version}</span>'.format( name=product.name, version=' '+str(product.version) \ if product.version else '') # Screenshot img_name = 'scren-{ip}-{port}-{id}'.format(ip=str( service.host.ip), port=service.port, id=service.id) path = self.output_path + '/screenshots' if service.screenshot is not None \ and service.screenshot.status == ScreenStatus.OK \ and FileUtils.exists(path + '/' + img_name + '.png') \ and FileUtils.exists(path + '/' + img_name + '.thumb.png'): screenshot = """ <a href="{screenlarge}" title="{url} - {title}" class="image-link"> <img src="{screenthumb}" class="border rounded"> </a> """.format(url=service.url, screenlarge='screenshots/' + img_name + '.png', title=service.html_title, screenthumb='screenshots/' + img_name + '.thumb.png') else: screenshot = """ <img src="data:image/png;base64,{unavailable}"> """.format(unavailable=unavailable_b64) # HTML for table row html += """ <tr{clickable}> <td>{url}</td> <td>{enc}</td> <td>{title}</td> <td>{webtechnos}</td> <td>{waf}</td> <td>{screenshot}</td> <td>{checks}</td> </tr> """.format( clickable=' class="clickable-row" data-href="{results}"'.format( results=results) if len(service.results) > 0 else '', url='<a href="{}" title="{}">{}</a>'.format( service.url, service.url, StringUtils.shorten(service.url, 50)) \ if service.url else '', enc=enc, title=StringUtils.shorten(service.html_title, 40), webtechnos=webtechnos, waf=waf, screenshot=screenshot, checks=len(service.results)) return html
def __generate_table_web(self): """ Generate the table with HTTP services registered in the mission """ req = ServicesRequester(self.sqlsession) req.select_mission(self.mission) filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition('http', FilterData.SERVICE_EXACT)) req.add_filter(filter_) services = req.get_results() if len(services) == 0: html = """ <tr class="notfound"> <td colspan="5">No record found</td> </tr> """ else: html = '' # Unavailable thumbnail with open(REPORT_TPL_DIR + '/../img/unavailable.png', 'rb') as f: unavailable_b64 = base64.b64encode(f.read()).decode('ascii') for service in services: # Results HTML page name results = 'results-{ip}-{port}-{service}-{id}.html'.format( ip=str(service.host.ip), port=service.port, service=service.name, id=service.id) # Web technos try: technos = ast.literal_eval(service.web_technos) except Exception as e: logger.debug('Error when retrieving "web_technos" field ' \ 'from db: {exc} for {service}'.format( exc=e, service=service)) technos = list() tmp = list() for t in technos: tmp.append('{}{}{}'.format( t['name'], ' ' if t['version'] else '', t['version'] if t['version'] else '')) webtechnos = ' | '.join(tmp) # Screenshot img_name = 'scren-{ip}-{port}-{id}'.format(ip=str( service.host.ip), port=service.port, id=service.id) path = self.output_path + '/screenshots' if service.screenshot is not None \ and service.screenshot.status == ScreenStatus.OK \ and FileUtils.exists(path + '/' + img_name + '.png') \ and FileUtils.exists(path + '/' + img_name + '.thumb.png'): screenshot = """ <a href="{screenlarge}" title="{title}" class="image-link"> <img src="{screenthumb}" class="border rounded"> </a> """.format(screenlarge='screenshots/' + img_name + '.png', title=service.html_title, screenthumb='screenshots/' + img_name + '.thumb.png') else: screenshot = """ <img src="data:image/png;base64,{unavailable}"> """.format(unavailable=unavailable_b64) # HTML for table row html += """ <tr{clickable}> <td>{url}</td> <td>{title}</td> <td>{webtechnos}</td> <td>{screenshot}</td> <td>{checks}</td> </tr> """.format( clickable=' class="clickable-row" data-href="{results}"'.format( results=results) if len(service.results) > 0 else '', url='<a href="{}" title="{}">{}</a>'.format( service.url, service.url, StringUtils.shorten(service.url, 50)) \ if service.url else '', title=StringUtils.shorten(service.html_title, 40), webtechnos=webtechnos, screenshot=screenshot, checks=len(service.results)) return html
def runUpdate(self, settings, output): """ Run the update for the tool @Args settings: Settings instance output: CLIOutput instance @Returns Boolean indicating status """ # Tool not installed yet if not self.installed: output.printInfo( '{0} is not installed yet (according to config), skipped.'. format(self.name)) print return False # Not installed, but no update command specified elif not self.update: output.printWarning( 'No tool update command specified in config file, skipped.') print return False # Create directory for the tool if necessary (should not be necessary because only update) if not FileUtils.is_dir(self.tool_dir): output.printFail( 'Tool directory does not exist but tool marked as installed. Trying to re-install it...' ) return self.runInstall(settings, output) # Update command parsing cmd_update = self.getUpdateCmd() cmd_update_print = cmd_update[cmd_update.index(';') + 1:].strip() output.printInfo('Update Command:') output.printInfo(cmd_update_print) output.printPrompt('{0} > {1} - Update ? [Y/n]'.format( self.category, self.name)) # Prompt to_update = CLIUtils.promptYesNo(output, default='Y') # Run update command if wanted if to_update: output.printBeginCmd(cmd_update_print) process = ProcessLauncher(cmd_update, output, None) process.start() output.printEndCmd() output.printSuccess('Tool update has finished') output.printInfo('Now, checking if {0} has been updated correctly. '.format(self.name) + \ 'Hit any key to run test...') CLIUtils.getch() # Check update, update config options if self.checkInstall(output): if not settings.changeLastUpdateOption(self.service_name, self.section_name): output.printWarning( 'An unexpected error occured when trying to last update date.' ) else: output.printSuccess( 'Tool {0} has been marked as successfully updated.'. format(self.name)) else: # If test fails, ask user if re-install ? output.printFail( 'Tool {0} has not been marked as updated.'.format( self.name)) output.printPrompt( 'Do you want to try to re-install {0} ? [Y/n]'.format( self.name)) # Prompt to_reinstall = CLIUtils.promptYesNo(output, default='Y') # Re-Install if to_reinstall: self.reinstallTool(settings, output) else: output.printFail('Tool has not been updated') print