def __generate_sidebar_checks(self, service): """ Generate the sidebar with the list of checks that have been run for the specified service. :param Service service: Service Model """ req = ResultsRequester(self.sqlsession) req.select_mission(self.mission) # Filter on service id filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(service.id, FilterData.SERVICE_ID)) req.add_filter(filter_) results = req.get_results() html = '' i = 0 for r in results: # Icon category icon = IconsMapping.get_icon_html('category', r.category) html += """ <li{class_}> <a href="#{id}">{icon}{check}</a> </li> """.format(class_=' class="active"' if i == 0 else '', id=r.check, icon=icon, check=StringUtils.shorten(r.check, 28)) i += 1 return html
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 __check_args_attack_multi_targets(self): """ Check arguments for ATTACK > Multi targets options """ if self.args.filter is not None: # There can be several --filter, logical or is applied between them filter_ = Filter(FilterOperator.OR) for c in self.args.filter: combo = Filter(FilterOperator.AND) for cond in c.split(';'): if cond.count('=') != 1: logger.error('Filter syntax incorrect') return False name, val = cond.split('=') if name not in TARGET_FILTERS.keys(): logger.error('Filter option {filter} is not supported. Available options ' \ 'are: {options}'.format(filter=name, options=', '.join(TARGET_FILTERS.keys()))) return False try: condition = Condition(val.split(','), TARGET_FILTERS[name]) except Exception as e: logger.error('Filter exception: {msg}'.format(e)) return False combo.add_condition(condition) filter_.add_condition(combo) self.args.filters_combined = filter_ else: self.args.filters_combined = None return True
def __generate_command_outputs(self, service): """ Generate HTML code with all command outputs for the specified service. :param Service service: Service Model """ req = ResultsRequester(self.sqlsession) req.select_mission(self.mission) # Filter on service id filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(service.id, FilterData.SERVICE_ID)) req.add_filter(filter_) results = req.get_results() html = '' i = 0 for r in results: html += """ <div class="tab-pane{active}" id="{id}"> <div class="container-fluid"> <div class="row"> <div class="col-lg-12"> <h1 class="title-page">{category} > {check}</h1> """.format(active=' active' if i == 0 else '', id=r.check, category=r.category, check=r.check) for o in r.command_outputs: # Convert command output (with ANSI codes) to HTML conv = ansi2html.Ansi2HTMLConverter(inline=True, scheme='solarized', linkify=True) output = conv.convert(o.output) # Warning: ansi2html generates HTML document with <html>, <style>... # tags. We only keep the content inside <pre> ... </pre> m = re.search('<pre class="ansi2html-content">(?P<output>.*)' \ '</pre>\n</body>', output, re.DOTALL) if m: output = m.group('output') html += """ <pre class="cmdline">{cmdline}</pre> <pre>{output}</pre> """.format(cmdline=o.cmdline, output=output) html += """ </div> </div> </div> </div> """ i += 1 return html
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
def __check_args_attack_multi_targets(self): """Check arguments for subcommand Attack > Multi targets options""" # Check filter(s) # There can be several --filter, logical or is applied between them if self.args.filter: filter_ = Filter(FilterOperator.OR) for c in self.args.filter: combo = Filter(FilterOperator.AND) for cond in c.split(';'): if cond.count('=') != 1: logger.error('Filter syntax incorrect') return False name, val = cond.split('=') name = name.lower() if name not in TARGET_FILTERS.keys(): logger.error('Filter option {filter} is not supported. ' \ 'Available options are: {options}'.format( filter=name, options=', '.join(TARGET_FILTERS.keys()))) return False try: condition = Condition(val.split(','), TARGET_FILTERS[name]) except Exception as e: logger.error('Filter exception: {msg}'.format(e)) return False combo.add_condition(condition) filter_.add_condition(combo) self.args.filters_combined = filter_ else: self.args.filters_combined = None # Handle --only-new if self.args.unscanned: filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(None, FilterData.UNSCANNED)) filter_.add_condition(self.args.filters_combined) self.args.filters_combined = filter_ return True
def __generate_command_outputs(self, service): """ Generate HTML code with all command outputs for the specified service. :param Service service: Service Model """ req = ResultsRequester(self.sqlsession) req.select_mission(self.mission) # Filter on service id filter_ = Filter(FilterOperator.AND) filter_.add_condition(Condition(service.id, FilterData.SERVICE_ID)) req.add_filter(filter_) results = req.get_results() html = '' i = 0 for r in results: # Icon category icon = IconsMapping.get_icon_html('category', r.category) # Description/Tool of check if service.name in self.settings.services: check = self.settings.services[ service.name]['checks'].get_check(r.check) if check is not None: description = check.description tool = check.tool.name else: description = tool = '' html += """ <div class="tab-pane{active}" id="{id}"> <div class="container-fluid"> <div class="row"> <div class="col-lg-12"> <h1 class="title-page">{icon}{category} > {check}</h1> <p class="check-description rounded"> <span class="mdi mdi-information-outline"></span> {description} (using tool: {tool}). </p> """.format(active=' active' if i == 0 else '', id=r.check, icon=icon, category=r.category, check=r.check, description=description, tool=tool) for o in r.command_outputs: # Convert command output (with ANSI codes) to HTML conv = ansi2html.Ansi2HTMLConverter(inline=True, scheme='solarized', linkify=True) output = conv.convert(o.output) # Warning: ansi2html generates HTML document with <html>, <style>... # tags. We only keep the content inside <pre> ... </pre> m = re.search('<pre class="ansi2html-content">(?P<output>.*)' \ '</pre>\n</body>', output, re.DOTALL) if m: output = m.group('output') html += """ <pre class="cmdline rounded"># {cmdline}</pre> <pre>{output}</pre> """.format(cmdline=o.cmdline, output=output) html += """ </div> </div> </div> </div> """ i += 1 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="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 __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 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 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 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()