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 show(self): """Display selected hosts""" results = self.get_results() if not results: logger.warning('No host to display') else: data = list() columns = [ 'IP', 'Hostname', 'OS', 'Type', 'Vendor', 'Comment', 'TCP', 'UDP', ] for r in results: data.append([ r.ip, StringUtils.wrap(r.hostname, 45) if r.hostname != str(r.ip) else '', StringUtils.wrap(r.os, 50), r.type, StringUtils.wrap(r.vendor, 30), StringUtils.shorten(r.comment, 40), r.get_nb_services(Protocol.TCP), r.get_nb_services(Protocol.UDP), ]) Output.table(columns, data, hrules=False)
def show(self): """Display selected services""" results = self.get_results() if not results: logger.warning('No service to display') else: data = list() columns = [ 'id', 'IP', #'Hostname', 'Port', 'Proto', 'Service', 'Banner', 'URL', 'Comment/Title', 'Checks', 'Creds', 'Vulns', ] for r in results: # Creds numbers nb_userpass = r.get_nb_credentials(single_username=False) nb_usernames = r.get_nb_credentials(single_username=True) nb_creds = '{}{}{}'.format( '{}'.format(Output.colored(str(nb_userpass), color='green' \ if nb_userpass > 0 else None)) if nb_userpass > 0 else '', '/' if nb_userpass > 0 and nb_usernames > 0 else '', '{} usr'.format(Output.colored(str(nb_usernames), color='yellow' \ if nb_usernames > 0 else None)) if nb_usernames > 0 else '') nb_vulns = Output.colored(str(len(r.vulns)), color='green' \ if len(r.vulns) > 0 else None) if len(r.vulns) > 0 else '' # Col "Comment/Title" (title is for HTML title for HTTP) if r.html_title: comment = r.html_title else: comment = r.comment data.append([ r.id, r.host.ip, #r.host.hostname, r.port, { Protocol.TCP: 'tcp', Protocol.UDP: 'udp' }.get(r.protocol), r.name, StringUtils.wrap(r.banner, 55), StringUtils.wrap(r.url, 50), StringUtils.shorten(comment, 40), len(r.results), nb_creds, nb_vulns, ]) Output.table(columns, data, hrules=False)
def __generate_table_credentials(self): """ Generate the table with all credentials registered in the mission """ req = CredentialsRequester(self.sqlsession) req.select_mission(self.mission) credentials = req.get_results() if len(credentials) == 0: html = """ <tr class="notfound"> <td colspan="10">No record found</td> </tr> """ else: html = '' for cred in credentials: html += """ <tr> <td>{ip}</td> <td>{hostname}</td> <td>{service}</td> <td>{port}</td> <td>{proto}</td> <td>{type}</td> <td class="font-weight-bold">{username}</td> <td class="font-weight-bold">{password}</td> <td>{url}</td> <td>{comment}</td> </tr> """.format( ip=cred.service.host.ip, hostname=cred.service.host.hostname \ if cred.service.host.hostname != str(cred.service.host.ip)\ else '', service=cred.service.name, port=cred.service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( cred.service.protocol), type=cred.type or '', username='******' if cred.username == '' else cred.username, password={'': '<empty>', None: '<???>'}.get( cred.password, cred.password), url='<a href="{}" title="{}">{}</a>'.format( cred.service.url, cred.service.url, StringUtils.shorten(cred.service.url, 50)) \ if cred.service.url else '', comment=cred.comment) return html
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 grab_html_title(url): """Return HTML title from an URL""" try: r = requests.get(url, verify=False) html = bs4.BeautifulSoup(r.text, 'html.parser') # Remove non-ASCII characters and duplicate spaces title = StringUtils.remove_non_printable_chars( html.title.text.strip()) title = " ".join(title.split()) # Shorten if necessary title = StringUtils.shorten(title, 250) return title except: return ''
def __generate_table_credentials(self): """ Generate the table with all credentials registered in the mission """ req = CredentialsRequester(self.sqlsession) req.select_mission(self.mission) credentials = req.get_results() if len(credentials) == 0: html = """ <tr class="notfound"> <td colspan="9">No record found</td> </tr> """ else: html = '' for cred in credentials: # Service name service_name = IconsMapping.get_icon_html( 'service', cred.service.name) service_name += str(cred.service.name) # Add color to username/password username = '******' if cred.username == '' else cred.username username = '******'.format( color='green' if cred.password is not None else 'yellow', username=username) password = { '': '<empty>', None: '<???>' }.get(cred.password, cred.password) password = '******'.format( color='green' if cred.password is not None else 'yellow', password=password) html += """ <tr> <td>{ip}</td> <td>{hostname}</td> <td>{service}</td> <td>{port} /{proto}</td> <td>{type}</td> <td class="font-weight-bold">{username}</td> <td class="font-weight-bold">{password}</td> <td>{url}</td> <td>{comment}</td> </tr> """.format( ip=cred.service.host.ip, hostname=cred.service.host.hostname \ if cred.service.host.hostname != str(cred.service.host.ip)\ else '', service=service_name, port=cred.service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( cred.service.protocol), type=cred.type or '', username=username, password=password, url='<a href="{}" title="{}">{}</a>'.format( cred.service.url, cred.service.url, StringUtils.shorten(cred.service.url, 50)) \ if cred.service.url else '', comment=cred.comment) 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_services(self): """ Generate the table with all services registered in the mission """ req = ServicesRequester(self.sqlsession) req.select_mission(self.mission) services = req.get_results() if len(services) == 0: html = """ <tr class="notfound"> <td colspan="12">No record found</td> </tr> """ else: html = '' for service in services: hostname = service.host.hostname \ if service.host.ip != service.host.hostname else '' # Number of checks if len(service.results) > 0: nb_checks = len(service.results) else: nb_checks = '<span class="mdi mdi-window-close"></span>' # Number of creds nb_userpass = service.get_nb_credentials(single_username=False) nb_usernames = service.get_nb_credentials(single_username=True) nb_creds = '{}{}{}'.format( '<span class="text-green">{}</span>'.format(str(nb_userpass)) \ if nb_userpass > 0 else '', '/' if nb_userpass > 0 and nb_usernames > 0 else '', '<span class="text-yellow">{}</span>'.format( str(nb_usernames)) if nb_usernames > 0 else '') #if nb_creds == '': # nb_creds = '<span class="mdi mdi-window-close"></span>' # Number of vulns if len(service.vulns) > 0: nb_vulns = '<span class="text-green">{}</span>'.format( len(service.vulns)) else: #nb_vulns = '<span class="mdi mdi-window-close"></span>' nb_vulns = '' # Encrypted ? (SSL/TLS) enc = '<span class="mdi mdi-lock" title="SSL/TLS encrypted"></span>' \ if service.is_encrypted() else '' # Service name service_name = IconsMapping.get_icon_html( 'service', service.name) service_name += str(service.name) # Technologies technos = '' # For HTTP, respect a given order for technos for better readability if service.name == 'http': product_types = ( 'web-server', 'web-appserver', # 'web-application-firewall', Displayed only in "web" tab # for better readability 'web-cms', 'web-language', 'web-framework', 'web-jslib') for t in product_types: product = service.get_product(t) if product: technos += '<span class="badge badge-{type} badge-light">' \ '{name}{version}</span>'.format( type=t, name=product.name, version=' '+str(product.version) \ if product.version else '') else: for p in service.products: technos += '<span class="badge badge-generic badge-light">' \ '{name}{version}</span>'.format( type=p.type, name=p.name, version=' '+str(p.version) if p.version else '') # Col "Comment/Title" (title is for HTML title for HTTP) if service.html_title: comment = service.html_title else: comment = service.comment # 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) html += """ <tr{clickable}> <td class="font-weight-bold">{ip}</td> <td>{hostname}</th> <td class="font-weight-bold">{port} /{proto}</td> <td>{service}</td> <td>{enc}</td> <td>{banner}</td> <td>{technos}</td> <td>{url}</td> <td>{comment}</td> <td>{nb_checks}</td> <td>{nb_creds}</td> <td>{nb_vulns}</td> </tr> """.format( clickable=' class="clickable-row" data-href="{results}"'.format( results=results) if len(service.results) > 0 else '', ip=service.host.ip, hostname=hostname, port=service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( service.protocol), service=service_name, enc=enc, banner=service.banner, technos=technos, url='<a href="{}" title="{}">{}</a>'.format( service.url, service.url, StringUtils.shorten(service.url, 40)) \ if service.url else '', comment=StringUtils.shorten(comment, 40), nb_checks=nb_checks, nb_creds=nb_creds, nb_vulns=nb_vulns) 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 __generate_table_services(self): """ Generate the table with all services registered in the mission """ req = ServicesRequester(self.sqlsession) req.select_mission(self.mission) services = req.get_results() if len(services) == 0: html = """ <tr class="notfound"> <td colspan="9">No record found</td> </tr> """ else: html = '' for service in services: # Creds numbers nb_userpass = service.get_nb_credentials(single_username=False) nb_usernames = service.get_nb_credentials(single_username=True) nb_creds = '{}{}{}'.format( '<span class="text-green">{}</span>'.format(str(nb_userpass)) \ if nb_userpass > 0 else '', '/' if nb_userpass > 0 and nb_usernames > 0 else '', '<span class="text-yellow">{}</span> user(s)'.format( str(nb_usernames)) if nb_usernames > 0 else '') # Col "Comment/Title" (title is for HTML title for HTTP) if service.html_title: comment = service.html_title else: comment = service.comment # 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) html += """ <tr{clickable}> <td>{ip}</td> <td>{port}</td> <td>{proto}</td> <td>{service}</td> <td>{banner}</td> <td>{url}</td> <td>{comment}</td> <td>{checks}</td> <td>{creds}</td> </tr> """.format( clickable=' class="clickable-row" data-href="{results}"'.format( results=results) if len(service.results) > 0 else '', ip=service.host.ip, port=service.port, proto={Protocol.TCP: 'tcp', Protocol.UDP: 'udp'}.get( service.protocol), service=service.name, banner=service.banner, url='<a href="{}" title="{}">{}</a>'.format( service.url, service.url, StringUtils.shorten(service.url, 50)) \ if service.url else '', comment=StringUtils.shorten(comment, 40), checks=len(service.results), creds=nb_creds) return html