def do_mission(self, args): """Missions management""" print() req = MissionsRequester(self.sqlsess) if args.add: if req.add(args.add): self.change_current_mission(args.add, verbose=True) elif args.comment: req.add_filter(Condition(args.comment[0], FilterData.MISSION_EXACT)) req.edit_comment(args.comment[1]) elif args.delete: req.add_filter(Condition(args.delete, FilterData.MISSION_EXACT)) req.delete() elif args.reset: if Output.prompt_confirm('Are you sure you want to delete all missions ?', default=False): req.reset() elif args.rename: req.rename(args.rename[0], args.rename[1]) elif args.search: filter_ = Filter() filter_.add_condition(Condition(args.search, FilterData.MISSION)) filter_.add_condition(Condition(args.search, FilterData.COMMENT_MISSION)) req.add_filter(filter_) req.show(self.current_mission) elif args.name: self.change_current_mission(args.name, verbose=True) else: print('All available missions:') req.show(self.current_mission) print()
def __run_install_update(self, fast_mode, update=False): """ Run install or update command. :param fast_mode: Set to true to disable prompts :param update: Mode selector, True for update | False for install (default) :return: Install/Update status :rtype: bool """ if update : cmd = self.update_command.get_cmdline(self.tool_dir) else : cmd = self.install_command.get_cmdline(self.tool_dir) mode = 'update' if update else 'install' logger.info('Description: {descr}'.format(descr=self.description)) #Output.print('{mode} command : {cmd}'.format( # mode=mode.capitalize(), cmd=cmd_short)) if fast_mode \ or Output.prompt_confirm('Confirm {mode} ?'.format(mode=mode), default=True): Output.begin_cmd(cmd) ProcessLauncher(cmd).start() Output.delimiter() logger.success('Tool {mode} has finished'.format(mode=mode)) return True else: logger.warning('Tool {mode} aborted'.format(mode=mode)) return False
def attack(self): """ Run the attack against all targets :param fast_mode: """ # Initialize top status/progress bar # If single target (total=None), the counter format will be used instead of the progress bar format attack_progress = manager.counter( total=len(self.targets) + 1 if len(self.targets) > 1 else None, desc='', unit='target', bar_format=STATUSBAR_FORMAT, # For multi targets counter_format=STATUSBAR_FORMAT_SINGLE) # For single target time.sleep(.5) # hack for progress bar display for i in range(1, len(self.targets) + 1): print() self.show_summary() print() # Target selection if not self.fast_mode: if len(self.targets) > 1: self.current_targetid = Output.prompt_choice_range( 'Attack target # ? [{default}] '.format( default=self.current_targetid), 1, len(self.targets), self.current_targetid) else: if Output.prompt_confirm('Start attack ?', default=True): self.current_targetid = 1 else: logger.warning('Attack canceled !') sys.exit(1) target = self.targets[self.current_targetid - 1] # Update status/progress bar status = 'Current target [{cur}/{total}]: host {ip} | port {port}/{proto} | service {service}'.format( cur=i, total=len(self.targets), ip=target.get_ip(), port=target.get_port(), proto=target.get_protocol(), service=target.get_service_name()) attack_progress.desc = '{status}{fill}'.format( status=status, fill=' ' * (DESC_LENGTH - len(status))) attack_progress.update() print() # Launch the attack on the selected target self.__attack_target(self.current_targetid, attack_progress) self.current_targetid += 1 attack_progress.update() time.sleep(.5) attack_progress.close() manager.stop() # Clear progress bars
def do_hosts(self, args): """Hosts in the current mission scope""" print() req = HostsRequester(self.sqlsess) req.select_mission(self.current_mission) # Logical AND is applied between all specified filtering options filter_ = Filter(FilterOperator.AND) if args.addrs: for addr in args.addrs: if NetUtils.is_valid_ip(addr) or NetUtils.is_valid_ip_range(addr): filter_.add_condition(Condition(addr, FilterData.IP)) else: filter_.add_condition(Condition(addr, FilterData.HOST)) if args.search: filter_search = Filter(FilterOperator.OR) filter_search.add_condition(Condition(args.search, FilterData.HOST)) filter_search.add_condition(Condition(args.search, FilterData.OS)) filter_search.add_condition(Condition(args.search, FilterData.COMMENT_HOST)) filter_.add_condition(filter_search) if args.order: req.order_by(args.order) try: req.add_filter(filter_) except FilterException as e: logger.error(e) return # Operations if args.comment: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to edit comment for ALL hosts in current mission ?', default=False): logger.info('Canceled') return req.edit_comment(args.comment) elif args.delete: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to delete ALL hosts and related services in current mission', default=False): logger.info('Canceled') return req.delete() else: req.show() print()
def __check_post_install_update(self, settings, fast_mode, update=False): """ Post-install/update checks :param settings: Settings instance :param fast_mode: Boolean indicating whether prompts must be displayed or not :return: Boolean indicating status """ mode = ('update', 'updated') if update else ('install', 'installed') status = True if not fast_mode: if not self.check_command: logger.info('No check_command defined in settings for {tool}, will assume it is ' \ 'correctly {mode}'.format(tool=self.name_display, mode=mode[1])) else: logger.info( 'Now, checking if {tool} has been {mode} correctly. Hit any key to run test...' .format(tool=self.name_display, mode=mode[1])) CLIUtils.getch() status = self.__run_check_command() # Change install status in configuration file if status: try: if settings.change_installed_status(self.target_service, self.name_clean, install_status=True): logger.success( 'Tool {tool} has been marked as successfully {mode}'. format(tool=self.name_display, mode=mode[1])) return True else: logger.error( 'Error when updating configuration file "{filename}{ext}"' .format(filename=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT)) return False except SettingsException as e: logger.error('An unexpected error occured when trying to mark the tool as {mode}: ' \ '{exception}'.format(exception=e, mode=mode[1])) if not update: self.remove(settings) return False else: logger.warning('Tool {tool} has not been marked as {mode}'.format( tool=self.name_display, mode=mode[1])) if not update: self.remove(settings) else: if not fast_mode and Output.prompt_confirm( 'Do you want to try to re-install ?', default=True): return self.__reinstall(settings, fast_mode) return False
def __check_pre_install(self, settings, fast_mode=False): """ Perform some checks before trying to install the tool (already installed ?, install command ?). :param Settings settings: Settings from config files :param bool fast_mode: Set to true to disable prompts :return: Result of checks :rtype: bool """ if self.installed: logger.info('{tool} is already installed (according to settings), ' \ 'skipped'.format(tool=self.name)) return False elif not self.install_command: logger.warning('The tool {tool} has no installation command specified in ' \ 'config file'.format(tool=self.name)) if fast_mode \ or Output.prompt_confirm('Is the tool already installed on your system ?', default=True): try: if settings.change_installed_status(self.target_service, self.name, True): logger.success('Tool {tool} has been marked as installed in ' \ 'settings'.format(tool=self.name)) return True else: logger.error('Error when saving the configuration file ' \ '"{filename}{ext}"'.format( filename=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT)) return False except SettingsException as e: logger.error(e) self.remove(settings) return False else: logger.info('Tool {tool} is still not marked as installed in ' \ 'settings'.format(tool=self.name)) return False return True
def __run_check_command(self): """ Run the check command. The goal is to quickly check if the tool is not buggy or missing some dependencies :return: Boolean indicating if tool is correctly installed """ logger.info('Running the check command for the tool {tool}...'.format( tool=self.name_display)) cmd = self.check_command.get_cmdline(self.tool_dir) Output.begin_cmd(cmd) ProcessLauncher(cmd).start() Output.delimiter() return Output.prompt_confirm( 'Does the tool {tool} seem to be running correctly ?'.format( tool=self.name_display), default=True)
def __check_pre_install(self, settings, fast_mode): """ Checks to run before installing the tool :param settings: Settings instance :param fast_mode: Boolean indicating whether prompts must be displayed or not :return: Boolean indicating status """ if self.installed: logger.info( '{tool} is already installed (according to settings), skipped'. format(tool=self.name_display)) return False elif not self.install_command: logger.warning( 'The tool {tool} has no installation command specified in config file' .format(tool=self.name_display)) if fast_mode or Output.prompt_confirm( 'Is the tool already installed on your system ?', default=True): try: if settings.change_installed_status( self.target_service, self.name_clean, True): logger.success( 'Tool {tool} has been marked as installed in settings' .format(tool=self.name_display)) return True else: logger.error( 'Error when saving the configuration file "{filename}{ext}"' .format(filename=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT)) return False except SettingsException as e: logger.error(e) self.remove(settings) return False else: logger.info( 'Tool {tool} is still not marked as installed in settings'. format(tool=self.name_display)) return False return True
def __init__(self): try: print(BANNER) # Parse settings files settings = Settings() # Parse command-line arguments arguments = ArgumentsParser(settings) # Create db if needed and initialize sqlalchemy session Base.metadata.create_all(engine) session = Session() # Create "default" mission if necessary mission = session.query(Mission).filter( Mission.name == 'default').first() if not mission: mission = Mission(name='default', comment='Default scope') session.add(mission) session.commit() # Controller controller = MainController(arguments, settings, session) controller.run() except KeyboardInterrupt: print() if Output.prompt_confirm('Are you sure you want to exit ?', default=False): logger.error('User aborted') sys.exit(0) except (SettingsException, AttackException) as e: logger.error(e) sys.exit(1) except (ValueError, ArgumentsException): print sys.exit(1) except Exception as e: print logger.error('Unexpected error occured: {0}'.format(str(e))) traceback.print_exc() sys.exit(1)
def __run_check_command(self): """ Run the check command. The goal is to quickly check if the tool is not buggy or missing some dependencies. The user must analyze the output and gives an answer. :return: Response from user :rtype: bool """ logger.info('Running the check command for the tool {tool}...'.format( tool=self.name)) cmd = self.check_command.get_cmdline(self.tool_dir) Output.begin_cmd(cmd) ProcessLauncher(cmd).start() Output.delimiter() return Output.prompt_confirm('Does the tool {tool} seem to be running ' \ 'correctly ?'.format(tool=self.name), default=True)
def run_check_command(self, fast_mode=False): """ Run the check command. The goal is to quickly check if the tool is not buggy or missing some dependencies. The user must analyze the output and gives an answer. :param bool fast_mode: Set to true to disable prompts :return: Response from user in interactive mode, otherwise status based on exit code (True if exit code is 0) :rtype: bool """ if not self.check_command: logger.info('No check_command defined in settings for the tool ' \ '{tool}'.format(tool=self.name)) return True logger.info('Running the check command for the tool {tool}...'.format( tool=self.name)) cmd = self.check_command.get_cmdline(self) Output.begin_cmd(cmd) returncode, _ = ProcessLauncher(cmd).start() Output.delimiter() if returncode != 0: logger.warning('Check command has finished with an error ' \ 'exit code: {code}'.format(code=returncode)) else: logger.success('Check command has finished with success exit code') if fast_mode: return (returncode == 0) else: return Output.prompt_confirm('Does the tool {tool} seem to be running ' \ 'correctly ?'.format(tool=self.name), default=True)
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 attack(self): """Run the attack against all targets in the scope""" # Initialize top status/progress bar # If single target (total=None), the counter format will be used instead of # the progress bar format attack_progress = manager.counter( total=len(self.targets)+1 if len(self.targets) > 1 else None, desc='', unit='target', bar_format=STATUSBAR_FORMAT, # For multi targets counter_format=STATUSBAR_FORMAT_SINGLE) # For single target time.sleep(.5) # hack for progress bar display # Loop over the targets for i in range(1,len(self.targets)+1): # Display table with targets self.show_summary() # Prompt for target selection if not self.fast_mode: if len(self.targets) == 1: if Output.prompt_confirm('Start attack ?', default=True): self.current_targetid = 1 else: logger.warning('Attack canceled !') sys.exit(1) else: self.current_targetid = Output.prompt_choice_range( 'Attack target # (Ctrl+C to quit) ? [{default}] '.format( default=self.current_targetid), 1, len(self.targets), self.current_targetid) target = self.targets[self.current_targetid-1] # Update status/progress bar status = 'Current target [{cur}/{total}]: {target}'.format( cur = i, total = len(self.targets), target = target) attack_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) attack_progress.update() print() # Check the selected target and update its information # This is done for targets loaded from the database in multi-targets mode # (For single target, done before adding it to a mission in AttackController) # # - Reverse DNS lookup: not by default (should have already been done) # - Port check: always (target might not been reachable anymore) # - Nmap service detection: not by default (should have already been done) # - HTML title grabbing: always # - Web technologies detection: always # - Context initialization via SmartStart: always if len(self.targets) > 1: reachable = target.smart_check( reverse_dns_lookup=(self.arguments.args.reverse_dns == 'on'), availability_check=True, nmap_banner_grabbing=(self.arguments.args.nmap_banner_grab == 'on'), html_title_grabbing=True, web_technos_detection=True, smart_context_initialize=True) # Update info into database if needed self.services_requester.add_target(target) # Display availability status, skip if not reachable if target.service.name == 'http': msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') else: msg = 'Target {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) if reachable: logger.success(msg) else: logger.error(msg) self.__next_target() continue # Launch the attack on the selected target self.__attack_target(target, attack_progress) # Move to next target self.__next_target() # Clear progress bars attack_progress.update() time.sleep(.5) attack_progress.close() manager.stop()
def do_services(self, args): """Services in the current mission scope""" print() req = ServicesRequester(self.sqlsess) req.select_mission(self.current_mission) # Logical AND is applied between all specified filtering options filter_ = Filter(FilterOperator.AND) if args.names: for n in args.names: if not self.settings.services.is_service_supported(n, multi=False): logger.error('Service {name} is not valid/supported'.format(name=n.lower())) return filter_.add_condition(Condition(args.names, FilterData.SERVICE_EXACT)) if args.order: req.order_by(args.order) if args.hostname: # OR between submitted hostnames filter_.add_condition(Condition(args.hostname.split(','), FilterData.HOST)) if args.ip: # OR between submitted ips/ranges filter_.add_condition(Condition(args.ip.split(','), FilterData.IP)) if args.port: # OR between ports/port-ranges filter_.add_condition(Condition(args.port.split(','), FilterData.PORT)) if args.proto: filter_.add_condition(Condition(args.proto, FilterData.PROTOCOL)) if args.up: filter_.add_condition(Condition(args.up, FilterData.UP)) if args.search: filter_search = Filter(FilterOperator.OR) filter_search.add_condition(Condition(args.search, FilterData.HOST)) filter_search.add_condition(Condition(args.search, FilterData.BANNER)) filter_search.add_condition(Condition(args.search, FilterData.URL)) filter_search.add_condition(Condition(args.search, FilterData.COMMENT_SERVICE)) filter_.add_condition(filter_search) try: req.add_filter(filter_) except FilterException as e: logger.error(e) return # Operations if args.add: host, port, service = args.add if NetUtils.is_valid_ip(host): ip = host hostname = NetUtils.reverse_dns_lookup(ip) logger.info('Reverse DNS lookup on IP {ip}: {hostname}'.format(ip=ip, hostname=hostname)) else: ip = NetUtils.dns_lookup(host) if not ip: logger.error('Cannot resolve hostname') return hostname = host logger.info('DNS lookup on {hostname}: IP {ip}'.format(hostname=host, ip=ip)) if not NetUtils.is_valid_port(port): logger.error('Port is invalid, not in range [0-65535]') elif not self.settings.services.is_service_supported(service, multi=False): logger.error('Service {name} is not valid/supported'.format(name=service.lower())) else: req.add_service(ip, hostname, port, self.settings.services.get_protocol(service), service) elif args.url: args.url = WebUtils.add_prefix_http(args.url) if not WebUtils.is_valid_url(args.url): logger.error('URL is invalid') else: req.add_url(args.url) elif args.delete: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to delete ALL services in current mission ?', default=False): logger.info('Canceled') return req.delete() elif args.comment: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to edit comment for ALL services in current mission ?', default=False): logger.info('Canceled') return req.edit_comment(args.comment) elif args.https: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to apply switch for ALL URLs in current mission ?', default=False): logger.info('Canceled') return req.switch_https() elif args.addcred: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to add same creds for ALL services in current mission ?', default=False): logger.info('Canceled') return req.add_cred(args.addcred[0], args.addcred[1], None) elif args.addcred_http: if not req.are_only_http_services_selected(): logger.warning('Some non-HTTP services are selected. Use --addcred instead for non-HTTP services') return if not self.settings.services.is_valid_authentication_type(args.addcred_http[2]): logger.warning('Invalid HTTP authentication type') logger.info('List of supported authentication types: ') for auth_type in self.settings.services.get_authentication_types('http'): logger.info('- {type}'.format(type=auth_type)) return if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to add same creds for ALL HTTP services in current mission ?', default=False): logger.info('Canceled') return req.add_cred(args.addcred_http[0], args.addcred_http[1], args.addcred_http[2]) elif args.adduser: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to add same username for ALL services in current mission ?', default=False): logger.info('Canceled') return req.add_cred(args.adduser[0], None, None) elif args.adduser_http: if not req.are_only_http_services_selected(): logger.warning('Some non-HTTP services are selected. Use --adduser instead for non-HTTP services') return if not self.settings.services.is_valid_authentication_type(args.adduser_http[1]): logger.warning('Invalid HTTP authentication type') logger.info('List of supported authentication types: ') for auth_type in self.settings.services.get_authentication_types('http'): logger.info('- {type}'.format(type=auth_type)) return if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to add same username for ALL HTTP services in current mission ?', default=False): logger.info('Canceled') return req.add_cred(args.adduser_http[0], None, args.adduser_http[1]) else: req.show() print()
def __check_post_install_update(self, settings, fast_mode=False, update=False): """ Perform some operation after install/update: - Check if correctly installed by running "check_command" and prompting, - Update install status in configuration file. :param Settings settings: Settings from config files :param bool fast_mode: Set to true to disable prompts :param update: Mode selector, True for update | False for install (default) :return: Status of operations :rtype: bool """ mode = ('update','updated') if update else ('install','installed') status = True # Check install/update if not fast_mode: if not self.check_command: logger.info('No check_command defined in settings for {tool}, will ' \ 'assume it is correctly {mode}'.format(tool=self.name, mode=mode[1])) else: logger.info('Now, checking if {tool} has been {mode} correctly. ' \ 'Hit any key to run test...'.format(tool=self.name, mode=mode[1])) CLIUtils.getch() status = self.__run_check_command() # Change install status in configuration file if status: try: if settings.change_installed_status(self.target_service, self.name, install_status=True): logger.success('Tool {tool} has been marked as successfully ' \ '{mode}'.format(tool=self.name, mode=mode[1])) return True else: logger.error('Error when updating configuration file ' \ '"{filename}{ext}"'.format( filename=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT)) return False except SettingsException as e: logger.error('An unexpected error occured when trying to mark the '\ 'tool as {mode}: {exc}'.format(mode=mode[1], exc=e)) if not update: self.remove(settings) return False else: logger.warning('Tool {tool} has not been marked as {mode}'.format( tool=self.name, mode=mode[1])) if not update: self.remove(settings) else: if not fast_mode \ and Output.prompt_confirm('Do you want to try to re-install ?', default=True): return self.__reinstall(settings, fast_mode) return False
def do_creds(self, args): """Credentials in the current mission scope""" print() req = CredentialsRequester(self.sqlsess) req.select_mission(self.current_mission) # Logical AND is applied between all specified filtering options filter_ = Filter(FilterOperator.AND) if args.username: filter_.add_condition(Condition(args.username, FilterData.USERNAME)) if args.password: filter_.add_condition(Condition(args.password, FilterData.PASSWORD)) if args.both: filter_.add_condition(Condition(args.both, FilterData.USER_AND_PASS)) elif args.onlyuser: filter_.add_condition(Condition(args.onlyuser, FilterData.ONLY_USER)) if args.hostname: # OR between submitted hostnames filter_.add_condition(Condition(args.hostname.split(','), FilterData.HOST)) if args.ip: # OR between submitted ips/ranges filter_.add_condition(Condition(args.ip.split(','), FilterData.IP)) if args.port: # OR between ports/port-ranges filter_.add_condition(Condition(args.port.split(','), FilterData.PORT)) if args.service: for s in args.service.split(','): if not self.settings.services.is_service_supported(s, multi=False): logger.error('Service {name} is not valid/supported'.format(name=s.lower())) return filter_.add_condition(Condition(args.service.split(','), FilterData.SERVICE_EXACT)) if args.order: req.order_by(args.order) if args.search: filter_search = Filter(FilterOperator.OR) filter_search.add_condition(Condition(args.search, FilterData.HOST)) filter_search.add_condition(Condition(args.search, FilterData.AUTH_TYPE)) filter_search.add_condition(Condition(args.search, FilterData.USERNAME)) filter_search.add_condition(Condition(args.search, FilterData.PASSWORD)) filter_search.add_condition(Condition(args.search, FilterData.URL)) filter_search.add_condition(Condition(args.search, FilterData.COMMENT_CRED)) filter_.add_condition(filter_search) try: req.add_filter(filter_) except FilterException as e: logger.error(e) return # Operations add = args.addcred or args.addcred_http or args.adduser or args.adduser_http if add: try: service_id = int(add[0]) except: logger.error('Invalid service id') return if args.addcred: req.add_cred(service_id, args.add[1], args.add[2], None) elif args.addcred_http: if not self.settings.services.is_valid_authentication_type(args.addcred_http[3]): logger.warning('Invalid HTTP authentication type') logger.info('List of supported authentication types: ') for auth_type in self.settings.services.get_authentication_types('http'): logger.info('- {type}'.format(type=auth_type)) return req.add_cred(service_id, args.addcred_http[1], args.addcred_http[2], args.addcred_http[3]) elif args.adduser: req.add_cred(service_id, args.add[1], None, None) elif args.adduser_http: if not self.settings.services.is_valid_authentication_type(args.adduser_http[2]): logger.warning('Invalid HTTP authentication type') logger.info('List of supported authentication types: ') for auth_type in self.settings.services.get_authentication_types('http'): logger.info('- {type}'.format(type=auth_type)) return req.add_cred(service_id, args.adduser_http[1], None, args.adduser_http[2]) elif args.comment: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to edit comment for ALL creds in current mission ?', default=False): logger.info('Canceled') return req.edit_comment(args.comment) elif args.delete: if not req.filter_applied: if not Output.prompt_confirm('No filter applied. Are you sure you want to delete ALL creds in current mission ?', default=False): logger.info('Canceled') return req.delete() else: if not args.order: req.order_by('ip') req.show() print()
def attack(self): """Run the attack against all targets in the scope""" # Initialize top status/progress bar # If single target (total=None), the counter format will be used instead of # the progress bar format attack_progress = manager.counter( total=len(self.targets) + 1 if len(self.targets) > 1 else None, desc='', unit='target', bar_format=STATUSBAR_FORMAT, # For multi targets counter_format=STATUSBAR_FORMAT_SINGLE) # For single target time.sleep(.5) # hack for progress bar display # Loop over the targets for i in range(1, len(self.targets) + 1): # In Multi-targets mode: # Display summary table and prompt for target selection # (not if too many target to avoid poor output) if 2 <= len(self.targets) <= 15: self.show_summary() if not self.fast_mode and len(self.targets) > 1: self.current_targetid = Output.prompt_choice_range( 'Attack target # (Ctrl+C to quit) ? [{default}] '.format( default=self.current_targetid), 1, len(self.targets), self.current_targetid) target = self.targets[self.current_targetid - 1] # Update status/progress bar status = 'Current target [{cur}/{total}]: {target}'.format( cur=i, total=len(self.targets), target=target) attack_progress.desc = '{status}{fill}'.format( status=status, fill=' ' * (DESC_LENGTH - len(status))) attack_progress.update() print() # Check the current target # For single target mode: already done in AttackController if len(self.targets) > 1: # By default, do NOT perform reverve DNS lookup & Nmap banner grabbing # because we assume it has been added via Nmap results in most cases # and thus, has already been done (behaviour can be changed with opt) reachable = target.smart_check( reverse_dns=(self.arguments.args.reverse_dns == 'on'), availability_check=True, grab_banner_nmap=( self.arguments.args.nmap_banner_grab == 'on'), web_technos_detection=False) if target.service.name == 'http': msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') else: msg = 'Target {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) # Update info into database if needed self.services_requester.add_target(target) if reachable: #target.service.up = True logger.success(msg) else: # Skip target if not reachable logger.error(msg) continue # In Single-target mode: Display summary table and prompt for confirmation if len(self.targets) == 1: self.show_summary() if not self.fast_mode: if Output.prompt_confirm('Start attack ?', default=True): self.current_targetid = 1 else: logger.warning('Attack canceled !') sys.exit(1) # Launch the attack on the selected target self.__attack_target(target, attack_progress) self.current_targetid += 1 self.current_targetid = self.current_targetid % len(self.targets) attack_progress.update() time.sleep(.5) attack_progress.close() manager.stop() # Clear progress bars