def show_command_outputs_for_check(self): """ Display command outputs text for selected result/check This method must call only when filtering on one Result.id, i.e. Condition(xxx, FilterData.CHECK_ID) """ result = self.get_first_result() if not result: logger.error('Invalid check id (not existing)') else: Output.title2('Results for check {category} > {check}:'.format( category=result.category, check=result.check)) if result.service.host.hostname: hostname = ' (' + result.service.host.hostname + ')' else: hostname = '' Output.title2('Target: host={ip}{hostname} | port={port}/{proto} | ' \ 'service {service}'.format( ip = result.service.host.ip, hostname = hostname, port = result.service.port, proto = {Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( result.service.protocol), service = result.service.name)) print() for o in result.command_outputs: Output.title3(o.cmdline) print() print(o.output) print()
def update_for_service(self, service, fast_mode=False): """ Update the tools for a given service. :param str service: Name of the service targeted by the tools to update (may be "multi") :param bool fast_mode: Set to true to disable prompts and install checks """ if service not in self.services: return Output.title1( 'Update tools for service: {service}'.format(service=service)) if not self.tools[service]: logger.info('No tool specific to this service in the toolbox') else: i = 1 for tool in self.tools[service]: if i > 1: print() Output.title2( '[{svc}][{i:02}/{max:02}] Update {tool_name}:'.format( svc=service, i=i, max=len(self.tools[service]), tool_name=tool.name)) tool.update(self.settings, fast_mode=fast_mode) i += 1
def show(self): """Display selected results""" results = self.get_results() Output.title2('Attacks results:') if not results: logger.warning('No results to display') else: data = list() columns = [ 'IP', 'Port', 'Proto', 'Service', 'Check id', 'Category', 'Check', '# Commands run', ] for r in results: data.append([ r.service.host.ip, r.service.port, { Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(r.service.protocol), r.service.name, r.id, r.category, r.check, len(r.command_outputs), ]) Output.table(columns, data, hrules=False)
def show_command_outputs(self, result_id): result_check = self.sqlsess.query(Result).join(Service).join( Host).filter(Result.id == result_id).first() if not result_check: logger.error('Invalid check id') return command_outputs = self.sqlsess.query(CommandOutput).filter( CommandOutput.result_id == result_id).all() Output.title2('Results for check {category} > {check}:'.format( category=result_check.category, check=result_check.check)) Output.title2( 'Target: host={ip}{hostname} | port={port}/{proto} | service {service}' .format(ip=result_check.service.host.ip, hostname=' (' + result_check.service.host.hostname + ')' if result_check.service.host.hostname else '', port=result_check.service.port, proto={ Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(result_check.service.protocol), service=result_check.service.name)) print() for o in command_outputs: Output.title3(o.cmdline) print() print(o.output) print()
def remove_tool(self, tool_name): """ Remove one tool from the toolbox. :param str tool_name: Name of the tool to remove :return: Status of removal :rtype: bool """ tool = self.get_tool(tool_name) if not tool: logger.warning('No tool with this name in the toolbox') return False else: Output.title2('Remove {tool_name}:'.format(tool_name=tool.name)) return tool.remove(self.settings)
def update_tool(self, tool_name, fast_mode=False): """ Update one tool from the toolbox. :param str tool_name: Name of the tool to update :param bool fast_mode: Set to true to disable prompts and install checks :return: Status of update :rtype: bool """ tool = self.get_tool(tool_name) if not tool: logger.warning('No tool with this name in the toolbox') return False else: Output.title2('Update {tool_name}:'.format(tool_name=tool.name)) return tool.update(self.settings, fast_mode)
def show_search_results(self, string, nb_words=12): """ Display command outputs search results. For good readability, only some words surrounding the search string are displayed. :param str string: Search string (accepts wildcard "%") :param int nb_words: Number of words surrounding the search string to show """ results = self.query.filter(CommandOutput.output.ilike('%'+string+'%')) if not results: logger.error('No result') else: Output.title2('Search results:') data = list() columns = [ 'IP', 'Port', 'Proto', 'Service', 'Check id', 'Category', 'Check', 'Matching text', ] for r in results: match = StringUtils.surrounding_text(r.outputraw, string, nb_words) # There might have several matches in one command result (one row # per match) for m in match: data.append([ r.result.service.host.ip, r.result.service.port, {Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( r.result.service.protocol), r.result.service.name, r.result.id, r.result.category, r.result.check, StringUtils.wrap(m, 70), ]) print() Output.table(columns, data, hrules=False)
def remove_for_service(self, service): """ Remove the tools for a given service. :param str service: Name of the service targeted by the tools to remove (may be "multi") """ if service not in self.services: return Output.title1( 'Remove tools for service: {service}'.format(service=service)) if not self.tools[service]: logger.info('No tool specific to this service in the toolbox') else: i = 1 status = True for tool in self.tools[service]: if i > 1: print() Output.title2( '[{svc}][{i:02}/{max:02}] Remove {tool_name}:'.format( svc=service, i=i, max=len(self.tools[service]), tool_name=tool.name)) status &= tool.remove(self.settings) i += 1 # Remove the service directory if all tools successfully removed if status: short_svc_path = '{toolbox}/{service}'.format( toolbox=TOOLBOX_DIR, service=service) full_svc_path = FileUtils.absolute_path(short_svc_path) if FileUtils.remove_directory(full_svc_path): logger.success( 'Toolbox service directory "{path}" deleted'.format( path=short_svc_path)) else: logger.warning('Toolbox service directory "{path}" cannot be ' \ 'deleted because it still stores some files'.format( path=short_svc_path))
def show_results(self, service_id): service = self.sqlsess.query(Service).filter( Service.id == service_id).first() if not service: logger.error('Invalid service id') else: Output.title2('Attacks results:') Output.title2( 'Target: host={ip}{hostname} | port={port}/{proto} | service {service}' .format(ip=service.host.ip, hostname=' (' + service.host.hostname + ')' if service.host.hostname else '', port=service.port, proto={ Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(service.protocol), service=service.name)) results = self.sqlsess.query(Result).filter( Result.service_id == service_id).all() if not results: logger.warning('No results to display') else: data = list() columns = [ 'Check id', 'Category', 'Check', '# Commands', ] for r in results: data.append([ r.id, r.category, r.check, len(r.command_outputs), ]) Output.table(columns, data, hrules=False)
def __run_special_mode(self, target, arguments, sqlsession, filter_checks=None, attack_profile=None, fast_mode=False, attack_progress=None): """ Run checks for the service in special mode, i.e. when user has provided either an attack profile (pre-selection of checks) or a list of checks (may even be one single check to run) :param Target target: Target :param ArgumentsParser arguments: Arguments from command-line :param Session sqlsession: SQLAlchemy session :param list filter_checks: Selection of checks to run (default: all) :param AttackProfile attack_profile: Attack profile (default: no profile) :param bool fast_mode: Set to true to disable prompts :param enlighten.Counter attack_progress: Attack progress """ # User has submitted list of checks if filter_checks: filter_checks = list(filter( lambda x: self.is_existing_check(x), filter_checks)) if not filter_checks: logger.warning('None of the selected checks is existing for the ' \ 'service {service}'.format(service=target.get_service_name())) return logger.info('Selected check(s) that will be run:') for c in filter_checks: check = self.get_check(c) if check: Output.print(' | - {name} ({category})'.format( name=c, category=check.category)) # User has submitted an attack profile else: if not attack_profile.is_service_supported(target.get_service_name()): logger.warning('The attack profile {profile} is not supported for ' \ 'target service {service}'.format( profile=attack_profile, service=target.get_service_name())) return else: filter_checks = attack_profile.get_checks_for_service( target.get_service_name()) logger.info('Selected attack profile: {}'.format(attack_profile)) # Initialize sub status/progress bar checks_progress = manager.counter(total=len(filter_checks)+1, desc='', unit='check', leave=False, bar_format=STATUSBAR_FORMAT) time.sleep(.5) # hack for progress bar display i = 1 for checkname in filter_checks: print() check = self.get_check(checkname) # Update status/progress bar status = ' +--> Current check [{cur}/{total}]: {category} > ' \ '{checkname}'.format( cur = i, total = len(filter_checks), category = check.category, checkname = checkname) checks_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) checks_progress.update() if attack_progress: # Hack to refresh the attack progress bar without incrementing # useful if the tool run during the check has cleared the screen attack_progress.update(incr=0, force=True) # Run the check if: # - The check has not been already run for this target (except # if --recheck is specified in command-line) # - Target is compliant with the check, # - The tool used for the check is well installed. results_req = ResultsRequester(sqlsession) results_req.select_mission(target.service.host.mission.name) filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(target.service.id, FilterData.SERVICE_ID)) filter_.add_condition(Condition(check.name, FilterData.CHECK_NAME)) results_req.add_filter(filter_) result = results_req.get_first_result() if result is None or arguments.args.recheck == True: if check.check_target_compliance(target): Output.title2('[Check {num:02}/{total:02}] {name} > ' \ '{description}'.format( num = i, total = len(filter_checks), name = check.name, description = check.description)) if not check.tool.installed: logger.warning('Skipped: the tool "{tool}" used by ' \ 'this check is not installed yet'.format( tool=check.tool.name)) else: try: check.run(target, arguments, sqlsession, fast_mode=fast_mode) except KeyboardInterrupt: print() logger.warning('Check {check} skipped !'.format( check=check.name)) else: logger.info('[Check {num:02}/{total:02}] ' \ '{name} > Skipped because context requirements are ' \ 'not matching the target'.format( name = check.name, num = i, total = len(filter_checks))) time.sleep(.2) else: logger.info('[Check {num:02}/{total:02}] ' \ '{name} > Skipped because the check has already ' \ 'been run'.format( name = check.name, num = i, total = len(filter_checks))) time.sleep(.2) i += 1 checks_progress.update() time.sleep(.5) checks_progress.close()
def __run_standard_mode(self, target, arguments, sqlsession, filter_categories, fast_mode=False, attack_progress=None): """ Run checks for the service in standard mode, i.e. when all or a subset of categories of checks must be run against the target. :param Target target: Target :param ArgumentsParser arguments: Arguments from command-line :param Session sqlsession: SQLAlchemy session :param list categories: Sorted list of categories to run :param enlighten.Counter attack_progress: Attack progress """ # logger.info('Categories of checks that will be run: {cats}'.format( # cats=', '.join(categories))) nb_checks = self.nb_checks() # Initialize sub status/progress bar checks_progress = manager.counter(total=nb_checks+1, desc='', unit='check', leave=False, bar_format=STATUSBAR_FORMAT) time.sleep(.5) # hack for progress bar display j = 1 for category in self.categories: # Apply filter on categories if category not in filter_categories: continue Output.title1('Category > {cat}'.format(cat=category.capitalize())) i = 1 for check in self.checks[category]: # Update status/progress bar status = ' +--> Current check [{cur}/{total}]: {category} > ' \ '{checkname}'.format( cur = j, total = nb_checks, category = check.category, checkname = check.name) checks_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) checks_progress.update() if attack_progress: # Hack to refresh the attack progress bar without incrementing # useful if the tool run during the check has cleared the screen attack_progress.refresh() # Run the check if and only if: # - The check has not been already run for this target (except # if --recheck is specified in command-line) # - Target is compliant with the check, # - The tool used for the check is well installed. if i > 1: print() results_req = ResultsRequester(sqlsession) results_req.select_mission(target.service.host.mission.name) filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(target.service.id, FilterData.SERVICE_ID)) filter_.add_condition(Condition(check.name, FilterData.CHECK_NAME)) results_req.add_filter(filter_) result = results_req.get_first_result() if result is None or arguments.args.recheck == True: if check.check_target_compliance(target): Output.title2('[{category}][Check {num:02}/{total:02}] ' \ '{name} > {description}'.format( category = category.capitalize(), num = j, total = nb_checks, name = check.name, description = check.description)) if not check.tool.installed: logger.warning('Skipped: the tool "{tool}" used by ' \ 'this check is not installed yet'.format( tool=check.tool.name)) else: try: check.run(target, arguments, sqlsession, fast_mode=fast_mode) except KeyboardInterrupt: print() logger.warning('Check {check} skipped !'.format( check=check.name)) else: logger.info('[{category}][Check {num:02}/{total:02}] ' \ '{name} > Skipped because context requirements are ' \ 'not matching the target'.format( name = check.name, category = category.capitalize(), num = j, total = nb_checks)) time.sleep(.2) else: logger.info('[{category}][Check {num:02}/{total:02}] ' \ '{name} > Skipped because the check has already ' \ 'been run'.format( name = check.name, category = category.capitalize(), num = j, total = nb_checks)) time.sleep(.2) i += 1 j += 1 checks_progress.update() time.sleep(.5) checks_progress.close() return
def run(self, target, smartmodules_loader, results_requester, filter_categories=None, filter_checks=None, fast_mode=False, attack_progress=None): """ Run checks for the service. By default, all the categories of checks are runned. Otherwise, only a list of categories can be runned. :param target: Target object :param results_requester: ResultsRequester object :param filter_categories: list of categories to run (None for all) :param filter_checks: list of checks to run (None for all) """ categories = self.categories if filter_categories is None else filter_categories # Standard mode # Selected/all categories of checks are run if filter_checks is None: nb_checks = self.nb_checks() # Initialize sub status/progress bar checks_progress = manager.counter(total=nb_checks+1, desc='', unit='check', leave=False, bar_format=STATUSBAR_FORMAT) time.sleep(.5) # hack for progress bar display j = 1 for category in categories: Output.title1('Category > {cat}'.format(cat=category.capitalize())) i = 1 for check in self.checks[category]: # Update status/progress bar status = ' +--> Current check [{cur}/{total}]: {category} > {checkname}'.format( cur = j, total = nb_checks, category = check.category, checkname = check.name) checks_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) checks_progress.update() if attack_progress: # Hack to refresh the attack progress bar without incrementing # useful if the tool run during the check has cleared the screen attack_progress.refresh() # Run the check if and only if: # - Check is matching context (i.e. at least one of its command is matching context), # - The tool used for the check is well installed if i > 1: print() if check.is_matching_context(target): Output.title2('[{category}][Check {num:02}/{total:02}] {name} > {description}'.format( category = category.capitalize(), num = i, total = len(self.checks[category]), name = check.name, description = check.description)) if not check.tool.installed: logger.warning('Skipped: the tool "{tool}" used by this check is not installed yet ' \ '(according to config)'.format(tool=check.tool.name_display)) else: try: check.run(target, smartmodules_loader, results_requester, fast_mode=fast_mode) except KeyboardInterrupt: print() logger.warning('Check {check} skipped !'.format(check=check.name)) else: logger.info('[{category}][Check {num:02}/{total:02}] {name} > Skipped because target\'s context is not matching'.format( name = check.name, category = category.capitalize(), num = i, total = len(self.checks[category]))) time.sleep(.2) i += 1 j += 1 checks_progress.update() time.sleep(.5) checks_progress.close() # Special mode # User has provided list of checks to run (may be one single check) else: filter_checks = list(filter(lambda x: self.is_existing_check(x), filter_checks)) if not filter_checks: logger.warning('None of the selected checks is existing for the service {service}'.format(service=target.get_service_name())) return # Initialize sub status/progress bar checks_progress = manager.counter(total=len(filter_checks)+1, desc='', unit='check', leave=False, bar_format=STATUSBAR_FORMAT) time.sleep(.5) # hack for progress bar display i = 1 for checkname in filter_checks: print() check = self.get_check(checkname) # Update status/progress bar status = ' +--> Current check [{cur}/{total}]: {category} > {checkname}'.format( cur = i, total = len(filter_checks), category = check.category, checkname = checkname) checks_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) checks_progress.update() if attack_progress: # Hack to refresh the attack progress bar without incrementing # useful if the tool run during the check has cleared the screen attack_progress.update(incr=0, force=True) # Run the check Output.title2('[Check {num:02}/{total:02}] {name} > {description}'.format( num = i, total = len(filter_checks), name = check.name, description = check.description)) try: check.run(target, smartmodules_loader, results_requester, fast_mode=fast_mode) except KeyboardInterrupt: print() logger.warning('Check {check} skipped !'.format(check=check.name)) i += 1 checks_progress.update() time.sleep(.5) checks_progress.close()