def __run_for_single_target(self, args): """Run attack against a single target specified into args""" req = ServicesRequester(self.sqlsess) mission = None # Get Mission if target must be added into a mission scope if args.add: mission = self.sqlsess.query(Mission).filter(Mission.name == args.add).first() if not mission: raise AttackException('The specified mission does not exist in the ' \ 'database. You should create it if needed') # Create new Service/Host objects (if service already exist, # will be merged by ServicesRequester.add_target) url = args.target_ip_or_url if args.target_mode == TargetMode.URL else '' ip = args.target_ip_or_url if args.target_mode == TargetMode.IP else '' service = Service( name=args.service, port=int(args.target_port), protocol=self.settings.services.get_protocol2(args.service), url=url) host = Host(ip=ip) # Will be updated when initializing Target() host.services.append(service) # Update context (credentials, options, products) if specified in command-line if args.creds: for c in args.creds[args.service]: self.sqlsess.add(c) service.credentials.append(c) if args.users: for u in args.users[args.service]: self.sqlsess.add(u) service.credentials.append(u) if args.products: for p in args.products[args.service]: self.sqlsess.add(p) service.products.append(p) if args.options: for o in args.options[args.service]: self.sqlsess.add(o) service.options.append(o) # Initialize Target try: target = Target(service, self.settings.services) except TargetException as e: logger.error(e) sys.exit(1) # Check Target and update its information: # - Reverse DNS lookup: by default # - Port check: always # - Nmap service detection: by default # - HTML title grabbing: always # - Web technologies detection: always # - Context initialization via SmartStart: always reachable = target.smart_check( reverse_dns_lookup=(args.reverse_dns is None or args.reverse_dns == 'on'), availability_check=True, nmap_banner_grabbing=(args.nmap_banner_grab is None \ or args.nmap_banner_grab == 'on'), html_title_grabbing=True, web_technos_detection=True, smart_context_initialize=True) # Display availability status, exit if not reachable if args.target_mode == TargetMode.IP: msg = 'Target service {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) else: msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') if reachable: logger.success(msg) else: logger.error(msg) return # Commit the target with updated information inside the appropriate # mission in the database if mission: logger.info('Results from this attack will be saved under mission ' \ '"{mission}" in database'.format(mission=mission.name)) req.select_mission(mission.name) req.add_target(target) # Run the attack self.attack_scope.add_target(target) self.attack_scope.attack() return
def __run_for_single_target(self, args): """ Run attack against a single target specified into argss """ req = ServicesRequester(self.sqlsess) mission = None # Get Mission if target must be added into a mission scope if args.add: mission = self.sqlsess.query(Mission).filter( Mission.name == args.add).first() if not mission: raise AttackException( 'The specified mission does not exist in the database. You should create it if needed' ) # Create new Service/Host objects (if service already exist, will be merged by ServicesRequester.add_target) service = Service( name=args.service, port=int(args.target_port), protocol={ 'tcp': Protocol.TCP, 'udp': Protocol.UDP }.get(self.settings.services.get_protocol(args.service)), url=args.target_ip_or_url if args.target_mode == TargetMode.URL else '') host = Host( ip=args.target_ip_or_url if args.target_mode == TargetMode.IP else '') # Will be updated when initializing Target() host.services.append(service) # Update credentials and options if needed for c in self.creds[args.service]: service.credentials.append(c) for u in self.users[args.service]: service.credentials.append(u) for o in self.options[args.service]: service.options.append(o) # Initialize Target and check if reachable target = Target(service, self.settings.services) if args.disable_banner_grab: logger.info('Check if target is reachable...') else: logger.info( 'Check if target is reachable and grab banner using Nmap...') reachable = target.smart_check( grab_banner_nmap=not args.disable_banner_grab) if args.target_mode == TargetMode.IP: msg = 'Target {neg}reachable: host {ip} | port {port}/{proto} | service {service}'.format( neg='not ' if not reachable else '', ip=target.get_ip(), port=target.get_port(), proto=target.get_protocol(), service=target.get_service_name()) else: msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') if reachable: service.up = True logger.success(msg) else: raise AttackException(msg) # Commit new data into database if target must be added to a mission if mission: logger.info( 'Results from this attack will be saved under mission "{mission}" in database' .format(mission=mission.name)) req.select_mission(mission.name) req.add_target(target) # Run the attack self.attack_scope.add_target(target) self.attack_scope.attack()
def __run_for_single_target(self, args): """Run attack against a single target specified into args""" req = ServicesRequester(self.sqlsess) mission = None # Get Mission if target must be added into a mission scope if args.add: mission = self.sqlsess.query(Mission).filter(Mission.name == args.add).first() if not mission: raise AttackException('The specified mission does not exist in the ' \ 'database. You should create it if needed') # Create new Service/Host objects (if service already exist, # will be merged by ServicesRequester.add_target) url = args.target_ip_or_url if args.target_mode == TargetMode.URL else '' ip = args.target_ip_or_url if args.target_mode == TargetMode.IP else '' service = Service(name=args.service, port=int(args.target_port), protocol=self.settings.services.get_protocol2(args.service), url=url) host = Host(ip=ip) # Will be updated when initializing Target() host.services.append(service) # Update credentials, options, products if specified in command-line for c in self.creds[args.service] : service.credentials.append(c) for u in self.users[args.service] : service.credentials.append(u) for p in self.products[args.service] : service.products.append(p) for o in self.options[args.service] : service.options.append(o) # Initialize Target try: target = Target(service, self.settings.services) except TargetException as e: logger.error(e) sys.exit(1) # Check if target is reachable # (by default, perform reverve DNS lookup & Nmap banner grabbing) reachable = target.smart_check( reverse_dns=(args.reverse_dns is None or args.reverse_dns == 'on'), availability_check=True, grab_banner_nmap=(args.nmap_banner_grab is None \ or args.nmap_banner_grab == 'on')) if args.target_mode == TargetMode.IP: msg = 'Target {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) else: msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') if reachable: logger.success(msg) else: # Skip target if not reachable logger.error(msg) return # Commit new data into database if target must be added to a mission if mission: logger.info('Results from this attack will be saved under mission ' \ '"{mission}" in database'.format(mission=mission.name)) req.select_mission(mission.name) req.add_target(target) # Run the attack self.attack_scope.add_target(target) self.attack_scope.attack()
class AttackScope: """Stores all targets selected for the current attack.""" def __init__(self, settings, arguments, sqlsession, mission, filter_categories=None, filter_checks=None, attack_profile=None, fast_mode=False): """ Construct AttackScope object :param Settings settings: Settings :param ArgumentsParser arguments: Arguments from command-line :param Session sqlsession: SQLAlchemy session :param str mission: Mission name :param list filter_categories: Selection of categories of checks to run (default is None, for all categories) :param list filter_checks: Selection of checks to run (default is None, for all checks) :param AttackProfile attack_profile: Attack profile (default is None, meaning no profile) :param bool fast_mode: Set to true to disable prompts """ self.settings = settings self.arguments = arguments self.sqlsess = sqlsession self.mission_name = mission self.services_requester = ServicesRequester(self.sqlsess) self.targets = list() self.current_targetid = 1 self.filter_categories = filter_categories self.filter_checks = filter_checks self.attack_profile = attack_profile self.fast_mode = fast_mode self.services_requester.select_mission(self.mission_name) #------------------------------------------------------------------------------------ def add_target(self, target): """ Add a target to the scope. :param Target target: Target to add """ self.targets.append(target) #------------------------------------------------------------------------------------ # Run Methods def attack(self): """Run the attack against all targets in the scope""" # Initialize top status/progress bar # If single target (total=None), the counter format will be used instead of # the progress bar format attack_progress = manager.counter( total=len(self.targets) + 1 if len(self.targets) > 1 else None, desc='', unit='target', bar_format=STATUSBAR_FORMAT, # For multi targets counter_format=STATUSBAR_FORMAT_SINGLE) # For single target time.sleep(.5) # hack for progress bar display # Loop over the targets for i in range(1, len(self.targets) + 1): # In Multi-targets mode: # Display summary table and prompt for target selection # (not if too many target to avoid poor output) if 2 <= len(self.targets) <= 15: self.show_summary() if not self.fast_mode and len(self.targets) > 1: self.current_targetid = Output.prompt_choice_range( 'Attack target # (Ctrl+C to quit) ? [{default}] '.format( default=self.current_targetid), 1, len(self.targets), self.current_targetid) target = self.targets[self.current_targetid - 1] # Update status/progress bar status = 'Current target [{cur}/{total}]: {target}'.format( cur=i, total=len(self.targets), target=target) attack_progress.desc = '{status}{fill}'.format( status=status, fill=' ' * (DESC_LENGTH - len(status))) attack_progress.update() print() # Check the current target # For single target mode: already done in AttackController if len(self.targets) > 1: # By default, do NOT perform reverve DNS lookup & Nmap banner grabbing # because we assume it has been added via Nmap results in most cases # and thus, has already been done (behaviour can be changed with opt) reachable = target.smart_check( reverse_dns=(self.arguments.args.reverse_dns == 'on'), availability_check=True, grab_banner_nmap=( self.arguments.args.nmap_banner_grab == 'on'), web_technos_detection=False) if target.service.name == 'http': msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') else: msg = 'Target {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) # Update info into database if needed self.services_requester.add_target(target) if reachable: #target.service.up = True logger.success(msg) else: # Skip target if not reachable logger.error(msg) continue # In Single-target mode: Display summary table and prompt for confirmation if len(self.targets) == 1: self.show_summary() if not self.fast_mode: if Output.prompt_confirm('Start attack ?', default=True): self.current_targetid = 1 else: logger.warning('Attack canceled !') sys.exit(1) # Launch the attack on the selected target self.__attack_target(target, attack_progress) self.current_targetid += 1 self.current_targetid = self.current_targetid % len(self.targets) attack_progress.update() time.sleep(.5) attack_progress.close() manager.stop() # Clear progress bars def __attack_target(self, target, attack_progress): """ Run security checks against one target. :param Target target: The Target :param enlighten.Counter attack_progress: Attack progress """ # Print target information target.print_http_headers() target.print_context() # Run start method from SmartModule start = SmartStart(target.service, self.sqlsess) start.run() # Run security checks service_checks = self.settings.services.get_service_checks( target.get_service_name()) service_checks.run(target, self.arguments, self.sqlsess, filter_categories=self.filter_categories, filter_checks=self.filter_checks, attack_profile=self.attack_profile, attack_progress=attack_progress) #------------------------------------------------------------------------------------ # Output methods def show_summary(self): """Display a table showing the summary of the attack scope.""" data = list() columns = [ 'id', 'IP/', 'Hostname', 'Port', 'Proto', 'Service', 'Banner', 'URL', ] id_ = 1 for target in self.targets: pointer_color = 'blue' if self.current_targetid == id_ else None pointer_attr = 'bold' if self.current_targetid == id_ else None data.append([ Output.colored('>'+str(id_) if self.current_targetid == id_ \ else str(id_), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_ip(), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_host(), 50), color=pointer_color, attrs=pointer_attr), Output.colored(str(target.get_port()), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_protocol(), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_service_name(), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_banner(), 55), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_url(), 50), color=pointer_color, attrs=pointer_attr), ]) id_ += 1 print() Output.table(columns, data, hrules=False) print()
class AttackScope: """Stores all targets selected for the current attack.""" def __init__(self, settings, arguments, sqlsession, mission, filter_categories=None, filter_checks=None, attack_profile=None, fast_mode=False): """ Construct AttackScope object :param Settings settings: Settings :param ArgumentsParser arguments: Arguments from command-line :param Session sqlsession: SQLAlchemy session :param str mission: Mission name :param list filter_categories: Selection of categories of checks to run (default is None, for all categories) :param list filter_checks: Selection of checks to run (default is None, for all checks) :param AttackProfile attack_profile: Attack profile (default is None, meaning no profile) :param bool fast_mode: Set to true to disable prompts """ self.settings = settings self.arguments = arguments self.sqlsess = sqlsession self.mission_name = mission self.services_requester = ServicesRequester(self.sqlsess) self.targets = list() self.current_targetid = 1 self.filter_categories = filter_categories self.filter_checks = filter_checks self.attack_profile = attack_profile self.fast_mode = fast_mode self.services_requester.select_mission(self.mission_name) #------------------------------------------------------------------------------------ def add_target(self, target): """ Add a target to the scope. :param Target target: Target to add """ self.targets.append(target) #------------------------------------------------------------------------------------ # Run Methods def attack(self): """Run the attack against all targets in the scope""" # Initialize top status/progress bar # If single target (total=None), the counter format will be used instead of # the progress bar format attack_progress = manager.counter( total=len(self.targets)+1 if len(self.targets) > 1 else None, desc='', unit='target', bar_format=STATUSBAR_FORMAT, # For multi targets counter_format=STATUSBAR_FORMAT_SINGLE) # For single target time.sleep(.5) # hack for progress bar display # Loop over the targets for i in range(1,len(self.targets)+1): # Display table with targets self.show_summary() # Prompt for target selection if not self.fast_mode: if len(self.targets) == 1: if Output.prompt_confirm('Start attack ?', default=True): self.current_targetid = 1 else: logger.warning('Attack canceled !') sys.exit(1) else: self.current_targetid = Output.prompt_choice_range( 'Attack target # (Ctrl+C to quit) ? [{default}] '.format( default=self.current_targetid), 1, len(self.targets), self.current_targetid) target = self.targets[self.current_targetid-1] # Update status/progress bar status = 'Current target [{cur}/{total}]: {target}'.format( cur = i, total = len(self.targets), target = target) attack_progress.desc = '{status}{fill}'.format( status = status, fill = ' '*(DESC_LENGTH-len(status))) attack_progress.update() print() # Check the selected target and update its information # This is done for targets loaded from the database in multi-targets mode # (For single target, done before adding it to a mission in AttackController) # # - Reverse DNS lookup: not by default (should have already been done) # - Port check: always (target might not been reachable anymore) # - Nmap service detection: not by default (should have already been done) # - HTML title grabbing: always # - Web technologies detection: always # - Context initialization via SmartStart: always if len(self.targets) > 1: reachable = target.smart_check( reverse_dns_lookup=(self.arguments.args.reverse_dns == 'on'), availability_check=True, nmap_banner_grabbing=(self.arguments.args.nmap_banner_grab == 'on'), html_title_grabbing=True, web_technos_detection=True, smart_context_initialize=True) # Update info into database if needed self.services_requester.add_target(target) # Display availability status, skip if not reachable if target.service.name == 'http': msg = 'Target URL {url} is {neg}reachable'.format( url=target.get_url(), neg='not ' if not reachable else '') else: msg = 'Target {neg}reachable: {target}'.format( neg='not ' if not reachable else '', target=target) if reachable: logger.success(msg) else: logger.error(msg) self.__next_target() continue # Launch the attack on the selected target self.__attack_target(target, attack_progress) # Move to next target self.__next_target() # Clear progress bars attack_progress.update() time.sleep(.5) attack_progress.close() manager.stop() def __attack_target(self, target, attack_progress): """ Run security checks against one target. :param Target target: The Target :param enlighten.Counter attack_progress: Attack progress """ # Print target information target.print_http_headers() target.print_context() # Run security checks service_checks = self.settings.services.get_service_checks( target.get_service_name()) service_checks.run(target, self.arguments, self.sqlsess, filter_categories=self.filter_categories, filter_checks=self.filter_checks, attack_profile=self.attack_profile, attack_progress=attack_progress) def __next_target(self): """ Move to next target by incrementing current target id """ if self.current_targetid == len(self.targets): self.current_targetid = 1 else: self.current_targetid += 1 #------------------------------------------------------------------------------------ # Output methods def show_summary(self): """ Display a table showing the summary of the attack scope. The table has a max size defined in lib.core.Config, to avoid displaying an unreadable summary when large amount of targets have been loaded. """ if len(self.targets) > ATTACK_SUMMARY_TABLE_MAX_SIZE: id_min = self.current_targetid-2 if id_min < 1: id_min = 1 id_max = self.current_targetid+ATTACK_SUMMARY_TABLE_MAX_SIZE-1 \ -(self.current_targetid-id_min) if id_max > len(self.targets): id_min = id_min-(id_max-len(self.targets)) id_max = len(self.targets) else: id_min = 1 id_max = len(self.targets) data = list() columns = [ 'id', 'IP', 'Hostname', 'Port', 'Proto', 'Service', 'Banner', 'URL', ] id_ = 1 for target in self.targets: if id_ < id_min: id_ += 1 continue if id_ > id_max: break pointer_color = 'blue' if self.current_targetid == id_ else None pointer_attr = 'bold' if self.current_targetid == id_ else None data.append([ Output.colored('>'+str(id_) if self.current_targetid == id_ \ else str(id_), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_ip(), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_host(), 50), color=pointer_color, attrs=pointer_attr), Output.colored(str(target.get_port()), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_protocol(), color=pointer_color, attrs=pointer_attr), Output.colored(target.get_service_name(), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_banner(), 55), color=pointer_color, attrs=pointer_attr), Output.colored(StringUtils.wrap(target.get_url(), 50), color=pointer_color, attrs=pointer_attr), ]) id_ += 1 print() Output.table(columns, data, hrules=False) if len(self.targets) > ATTACK_SUMMARY_TABLE_MAX_SIZE: logger.info('Table has been truncated. Total number of loaded ' \ 'targets: {}'.format(len(self.targets))) print()