def __check_pre_update(self, settings, fast_mode=False): """ Perform some checks before trying to update the tool (already installed ?, update 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 not self.installed: logger.info('{tool} is not installed yet (according to settings), ' \ 'skipped'.format(tool=self.name)) return False elif not self.update_command: logger.warning( 'No tool update command specified in config file, skipped.') return False # Create directory for the tool if necessary # (should not be necessary because only update) if self.install_command and not FileUtils.is_dir(self.tool_dir): logger.warning('Tool directory does not exist but tool marked as ' \ 'installed. Trying to re-install it...') return self.install(settings, fast_mode) return True
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 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 delete(self): """Delete selected services""" results = self.get_results() if not results: logger.error('No matching service') else: for r in results: logger.info('Service {service} host={ip}{hostname} ' \ 'port={port}/{proto} deleted'.format( service = r.name, ip = r.host.ip, hostname = '('+r.host.hostname+')' if r.host.hostname else '', port = r.port, proto = {Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( r.protocol))) self.sqlsess.delete(r) self.sqlsess.commit() # Delete host if no more service in it if len(r.host.services) == 0: logger.info('Host {ip} {hostname} deleted because it does not ' \ 'have service anymore'.format( ip=r.host.ip, hostname='('+r.host.hostname+')' if r.host.hostname else '')) self.sqlsess.delete(r.host) self.sqlsess.commit()
def __init__(self): """ Start the parsing of settings files and create the Settings object. :raises SettingsException: Exception raised if any error is encountered while parsing files (syntax error, missing mandatory file...) """ self.config_parsers = dict( ) # Dict of DefaultConfigParser indexed by filename self.toolbox = None # Receives Toolbox object self.services = None # Receives ServicesConfig object self.attack_profiles = None # Receives AttackProfiles object # Check directory if not FileUtils.is_dir(SETTINGS_DIR): raise SettingsException('Configuration directory ({dir}) does not ' \ 'exist'.format(dir=SETTINGS_DIR)) # Check presence of *.conf files files = FileUtils.list_directory(SETTINGS_DIR) for f in files: if not FileUtils.check_extension(f, CONF_EXT): files.remove(f) if not files: raise SettingsException('Configuration directory ({dir}) does not ' \ 'store any *.conf file'.format(dir=SETTINGS_DIR)) if TOOLBOX_CONF_FILE + CONF_EXT not in files: raise SettingsException('Missing mandatory {toolbox}{ext} settings ' \ 'file in directory "{dir}"'.format( toolbox=TOOLBOX_CONF_FILE, ext=CONF_EXT, dir=SETTINGS_DIR)) if ATTACK_PROFILES_CONF_FILE + CONF_EXT not in files: raise SettingsException('Missing mandatory {profiles}{ext} settings ' \ 'file in directory "{dir}"'.format( profiles=ATTACK_PROFILES_CONF_FILE, ext=CONF_EXT, dir=SETTINGS_DIR)) # Create _install_status.conf file if necessary if INSTALL_STATUS_CONF_FILE + CONF_EXT not in files: open(SETTINGS_DIR + '/' + INSTALL_STATUS_CONF_FILE + CONF_EXT, 'a').close() logger.info('{status}{ext} settings file created in directory ' \ '"{dir}"'.format(status=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT, dir=SETTINGS_DIR)) files.append(INSTALL_STATUS_CONF_FILE + CONF_EXT) # Parse configuration files and create objects from them self.__parse_all_conf_files(files) self.__create_toolbox() self.__create_all_services_config_and_checks() self.__create_attack_profiles()
def change_current_mission(self, name, verbose=False): mission = self.sqlsess.query(Mission).filter(Mission.name == name).first() if not mission: logger.error('No mission with this name') else: self.current_mission = name self.prompt = Output.colored('jok3rdb', color='light_green', attrs='bold') + \ Output.colored('[{mission}]'.format(mission=name), color='light_blue', attrs='bold') + \ Output.colored('> ', color='light_green', attrs='bold') if verbose: logger.info('Selected mission is now {name}'.format(name=name))
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 add_target(self, target): """ Add a new service into the current mission scope in database from a Target object. :param Target target: Target to add """ mission = self.sqlsess.query(Mission)\ .filter(Mission.name == self.current_mission).first() matching_service = self.sqlsess.query(Service)\ .join(Host)\ .join(Mission)\ .filter(Host.ip == target.get_ip())\ .filter(Mission.name == self.current_mission)\ .filter(Service.name == target.get_service_name())\ .filter(Service.port == target.get_port())\ .filter(Service.protocol == target.get_protocol2())\ .filter(Service.url == target.get_url()).first() # If service already exists in db, update it if necessary if matching_service: logger.info('A matching service has been found in the database') matching_service.merge(target.service) self.sqlsess.commit() # Make sure to replace target info by newly created service target.service = matching_service # Add host in db if it does not exist or update its info (merging) else: host = self.sqlsess.query(Host).join(Mission)\ .filter(Mission.name == self.current_mission)\ .filter(Host.ip == target.get_ip()).first() if host: host.merge(target.service.host) self.sqlsess.commit() target.service.host = host else: self.sqlsess.add(target.service.host) mission.hosts.append(target.service.host) self.sqlsess.commit() # Add service in db self.sqlsess.add(target.service) self.sqlsess.commit() logger.success('{action}: host {ip} | port {port}/{proto} | ' \ 'service {service}'.format( action = 'Updated' if matching_service else 'Added', ip = target.get_ip(), port = target.get_port(), proto = target.get_protocol(), service = target.get_service_name()))
def run(self): args = self.arguments.args self.creds = defaultdict(list) self.users = defaultdict(list) self.options = defaultdict(list) # Load smart modules self.smartmodules_loader = SmartModulesLoader(self.sqlsess, self.settings.services) # Initialize provided credentials if args.creds: for c in args.creds: self.creds[c['service']].append( Credential(type=c['auth_type'], username=c['username'], password=c['password'])) # Initialize provided single usernames if args.users: for u in args.users: self.users[c['service']].append( Credential(type=u['auth_type'], username=u['username'], password=None)) # Initialize provided context-specific options if args.specific: for option_name in args.specific: service = self.settings.services.get_service_for_specific_option( option_name) if service: self.options[service].append( Option(name=option_name, value=args.specific[option_name])) # Run the attack self.attack_scope = AttackScope(self.settings, ResultsRequester(self.sqlsess), self.smartmodules_loader, args.cat_only, args.checks, fast_mode=args.fast_mode) begin = time.time() if args.target_ip_or_url: self.__run_for_single_target(args) else: self.__run_for_multi_targets(args) print() logger.info('Done. Time spent: {0} seconds'.format(time.time() - begin))
def __reinstall(self, settings, fast_mode): """ Try to re-install the tool, ie. remove and install :param settings: Settings instance :param fast_mode: Boolean indicating whether prompts must be displayed or not :return: Boolean indicating status """ logger.info('First, the tool directory will be removed...') if not self.remove(settings): return False logger.info('Now, running a new install for {tool}...'.format( tool=self.name_display)) return self.install(settings)
def delete(self): results = self.get_results() if not results: logger.error('No matching host') else: for r in results: logger.info( 'Host {ip} {hostname} (and its {nb_services} services) deleted' .format(ip=r.ip, hostname='(' + r.hostname + ')' if r.hostname else '', nb_services=len(r.services))) self.sqlsess.delete(r) self.sqlsess.commit()
def __reinstall(self, settings, fast_mode): """ Try to re-install the tool, i.e. remove and install. :param Settings settings: Settings from config files :param fast_mode: Set to true to disable prompts :return: Status of reinstall :rtype: bool """ logger.info('First, the tool directory will be removed...') if not self.remove(settings): return False logger.info('Now, running a new install for {tool}...'.format(tool=self.name)) return self.install(settings)
def __reverse_dns_lookup(self): """ Attempt to perform reverse DNS lookup (i.e. IP -> Hostname) Updated in this method: - self.service.host.hostname """ hostname = NetUtils.reverse_dns_lookup(self.service.host.ip) if hostname != self.service.host.ip: logger.info('{ip} -> {hostname}'.format(ip=self.service.host.ip, hostname=hostname)) else: logger.info('No DNS name found for IP') self.service.host.hostname = hostname
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 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 delete(self): """Delete selected vulnerabilities""" results = self.get_results() if not results: logger.error('No matching vulnerability') else: for r in results: logger.info('Vulnerability deleted: "{vuln}" for service={service} ' \ 'host={ip} port={port}/{proto}'.format( vuln=StringUtils.shorten(r.name, 50), service=r.service.name, ip=r.service.host.ip, port=r.service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( r.service.protocol))) self.sqlsess.delete(r) self.sqlsess.commit()
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 delete(self): """Delete selected specific options""" results = self.get_results() if not results: logger.error('No matching specific option') else: for r in results: logger.info('Option {name}={value} for host={ip} service={service} ' \ '({port}/{proto}) deleted'.format( name=r.name, value=r.value, ip=r.service.host.ip, service=r.service.name, port=r.service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( r.service.protocol))) self.sqlsess.delete(r) self.sqlsess.commit()
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 delete(self): results = self.get_results() if not results: logger.error('No matching credential') else: for r in results: logger.info( 'Credential {username}/{password} from host={ip} service={service} ({port}/{proto}) deleted' .format(username=r.username, password=r.password, ip=r.service.host.ip, service=r.service.name, port=r.service.port, proto={ Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(r.service.protocol))) self.sqlsess.delete(r) self.sqlsess.commit()
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 delete(self): results = self.get_results() if not results: logger.error('No matching service') else: for r in results: logger.info( 'Service {service} host={ip}{hostname} port={port}/{proto} deleted' .format(service=r.name, ip=r.host.ip, hostname='(' + r.host.hostname + ')' if r.host.hostname else '', port=r.port, proto={ Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(r.protocol))) self.sqlsess.delete(r) self.sqlsess.commit()
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 delete(self): """Delete selected products""" results = self.get_results() if not results: logger.error('No matching product') else: for r in results: logger.info('Product deleted: {type}={name}{version} for ' \ 'service={service} host={ip} port={port}/{proto}'.format( type = r.type, name = r.name, version = ' '+r.version if r.version else '', service = r.service.name, ip = r.service.host.ip, port = r.service.port, proto = {Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( r.service.protocol))) self.sqlsess.delete(r) self.sqlsess.commit()
def __run_for_multi_targets(self, args): """Run attack against multiple targets from the database""" # Get Mission from which targets must be extracted mission = self.sqlsess.query(Mission)\ .filter(Mission.name == args.mission).first() if mission: logger.info('Extracting targets from mission "{mission}" ...'.format( mission=mission.name)) else: raise AttackException('Mission {mission} does not exist into the ' \ 'database'.format(mission=args.mission)) # Initialize Services requester and add filter if provided req = ServicesRequester(self.sqlsess) req.select_mission(args.mission) if args.filters_combined: for filt in args.filter: logger.info('Applying filters on mission scope: {filter}'.format( filter=filt)) if len(args.filter) > 1: logger.info('Note: Logical OR is applied between each filter') req.add_filter(args.filters_combined) # Retrieve targeted services from database services = req.get_results() if not services: raise AttackException('There is no matching service to target into the ' \ 'database') # Add each targeted service into Attack scope for service in services: # Update credentials, options, products if specified in command-line if args.creds: for c in args.creds[service.name]: service.add_credential(c.clone()) if args.users: for u in args.users[service.name]: service.add_credential(u.clone()) if args.products: for p in args.products[service.name]: service.add_product(p.clone()) if args.options: for o in args.options[service.name]: service.add_option(o.clone()) # Initialize Target try: target = Target(service, self.settings.services) except TargetException as e: logger.error(e) continue self.attack_scope.add_target(target) # Run the attack self.attack_scope.attack()
def run(self): """Run the Attack Controller""" args = self.arguments.args logger.debug('CLI arguments:') logger.debug(args) # Attack configuration: Categories of checks to run categories = self.settings.services.list_all_categories() # default: all if args.cat_only: categories = [ cat for cat in categories if cat in args.cat_only ] elif args.cat_exclude: categories = [ cat for cat in categories if cat not in args.cat_exclude ] # Create the attack scope self.attack_scope = AttackScope( self.settings, self.arguments, self.sqlsess, args.mission or args.add, filter_categories=categories, filter_checks=args.checks, attack_profile=args.profile, fast_mode=args.fast_mode) # Run the attack begin = datetime.datetime.now() if args.target_ip_or_url: self.__run_for_single_target(args) else: self.__run_for_multi_targets(args) print() duration = datetime.datetime.now() - begin logger.info('Finished. Duration: {}'.format(format_timespan(duration.seconds)))
def __run_install_update(self, fast_mode, update=False): """ Run install/update command :param update: Mode selector, True for update | False for install (default) :return: Boolean indicating status """ 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 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 add_service(self, ip, hostname, port, protocol, service): protocol = { 'tcp': Protocol.TCP, 'udp': Protocol.UDP }.get(protocol, Protocol.TCP) matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\ .filter(Mission.name == self.current_mission)\ .filter(Host.ip == ip)\ .filter(Service.port == int(port))\ .filter(Service.protocol == protocol).first() if protocol == Protocol.TCP: up = NetUtils.is_tcp_port_open(ip, port) else: up = NetUtils.is_udp_port_open(ip, port) if matching_service: logger.warning('Service already present into database') else: if up: logger.info( 'Grabbing banner from {ip}:{port} with Nmap...'.format( ip=ip, port=port)) banner = NetUtils.grab_banner_nmap(ip, port) logger.info('Banner: {}'.format(banner or 'None')) os = NetUtils.os_from_nmap_banner(banner) if os: logger.info('Detected Host OS: {}'.format(os)) else: logger.warning('Port seems to be closed !') # Add service in db (and host if not existing) service = Service(name=service, port=int(port), protocol=protocol, up=up, banner=banner) matching_host = self.sqlsess.query(Host).join(Mission)\ .filter(Mission.name == self.current_mission)\ .filter(Host.ip == ip).first() new_host = Host(ip=ip, hostname=hostname, os=os) if matching_host: matching_host.merge(new_host) self.sqlsess.commit() service.host = matching_host else: mission = self.sqlsess.query(Mission).filter( Mission.name == self.current_mission).first() new_host.mission = mission service.host = new_host self.sqlsess.add(new_host) self.sqlsess.add(service) self.sqlsess.commit() logger.success('Service added')
def run(self): # Extract HTTP services from the mission in database req = ServicesRequester(self.sqlsession) req.select_mission(self.mission_name) filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition('http', FilterData.SERVICE_EXACT)) req.add_filter(filter_) services = req.get_results() if len(services) == 0: return logger.info('Taking web page screenshots for HTTP services (total: ' \ '{nb})...'.format(nb=len(services))) screenshoter = WebScreenshoter() if not screenshoter.create_driver(): logger.error('No screenshot will be added to the report') return i = 1 for s in services: if s.screenshot is not None \ and s.screenshot.status == ScreenStatus.OK \ and s.screenshot.image is not None \ and s.screenshot.thumbnail is not None: logger.info('[{i}/{nb}] Screenshot already in database for {url}'.format( i=i, nb=len(services), url=s.url)) else: logger.info('[{i}/{nb}] Taking screenshot for {url}...'.format( i=i, nb=len(services), url=s.url)) status, screen = screenshoter.take_screenshot(s.url) # Create Screenshot entry in database if necessary if s.screenshot is None: screenshot = Screenshot(status=status) self.sqlsession.add(screenshot) s.screenshot = screenshot self.sqlsession.commit() # Create thumbnail if status is OK if status == ScreenStatus.OK: thumb = ImageUtils.create_thumbnail(screen, 300, 300) if not thumb: status = ScreenStatus.ERROR s.screenshot.status = status s.screenshot.image = screen s.screenshot.thumbnail = thumb else: s.screenshot.status = status self.sqlsession.commit() i += 1