예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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 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 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()
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
    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
예제 #10
0
    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()  
예제 #11
0
    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()