Exemple #1
0
    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()
Exemple #4
0
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()
Exemple #5
0
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()