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): # 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 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 __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
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 requester = ServicesRequester(self.sqlsess) requester.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('Logical or is applied between each filter') requester.add_filter(args.filters_combined) # Retrieve targeted services from database services = requester.get_results() if not services: raise AttackException( 'There is no matching service to target into the database') # Add each targeted service into Attack scope logger.info('Checking if targets are reachable...') for service in services: # Update credentials and options if needed for c in self.creds[service.name]: service.credentials.append(c) for u in self.users[service.name]: service.credentials.append(u) for o in self.options[service.name]: service.options.append(o) # Initialize Target and check if reachable target = Target(service, self.settings.services) service.up = target.smart_check(grab_banner_nmap=False) self.sqlsess.commit() msg = 'host {ip} | port {port}/{proto} | service {service}'.format( ip=target.get_ip(), port=target.get_port(), proto=target.get_protocol(), service=target.get_service_name()) if service.up: logger.success('Target reachable: ' + msg) else: logger.warning('Target not reachable (skipped): ' + msg) continue # Update info into database if needed #requester.add_target(target) self.attack_scope.add_target(target) self.attack_scope.attack()