Ejemplo n.º 1
0
    def rename(self, old, new):
        """
        Rename selected missions.
        :param str old: Name of the mission to rename
        :param str new: New mission name
        """
        if old == 'default':
            logger.warning('Default mission cannot be renamed')
            return False

        mission = self.sqlsess.query(Mission).filter(
            Mission.name == old).first()
        if mission:
            new_mission = self.sqlsess.query(Mission).filter(
                Mission.name == new).first()
            if new_mission:
                logger.warning(
                    'A mission named "{name}" already exists'.format(name=new))
                return False
            else:
                mission.name = new
                self.sqlsess.commit()
                logger.success('Mission renamed: {old} -> {new}'.format(
                    old=old, new=new))
                return True
        else:
            logger.warning('Mission "{name}" doesn\'t exists'.format(name=old))
            return False
Ejemplo n.º 2
0
    def __run_install_update(self, fast_mode, update=False):
        """
        Run install or update command.

        :param fast_mode: Set to true to disable prompts
        :param update: Mode selector, True for update | False for install (default)
        :return: Install/Update status
        :rtype: bool
        """
        if update: cmd = self.update_command.get_cmdline(self)
        else: cmd = self.install_command.get_cmdline(self)

        mode = 'update' if update else 'install'

        logger.info('Description: {descr}'.format(descr=self.description))
        #Output.print('{mode} command : {cmd}'.format(
        #   mode=mode.capitalize(), cmd=cmd_short))

        if fast_mode \
           or Output.prompt_confirm('Confirm {mode} ?'.format(mode=mode), default=True):

            Output.begin_cmd(cmd)
            returncode, _ = ProcessLauncher(cmd).start()
            Output.delimiter()
            if returncode != 0:
                logger.warning('Tool {mode} has finished with an error ' \
                    'exit code: {code}'.format(mode=mode, code=returncode))
            else:
                logger.success(
                    'Tool {mode} has finished with success exit code'.format(
                        mode=mode))
            return True
        else:
            logger.warning('Tool {mode} aborted'.format(mode=mode))
            return False
 def reset(self):
     self.sqlsess.query(Mission).delete()
     self.sqlsess.commit()
     self.sqlsess.add(Mission(name='default', comment='Default scope'))
     self.sqlsess.commit()
     logger.success(
         'All missions deleted & fresh "default" mission created')
    def do_nmap(self, args):
        """Import Nmap results"""
        print()

        file = os.path.expanduser(args.file[0])
        if not FileUtils.can_read(file):
            logger.error('Cannot read specified file')
            return
        logger.info('Importing Nmap results from {file}'.format(file=file))
        if not args.no_http_recheck:
            logger.info('Each service will be re-checked to detect HTTP services. Use --no-http-recheck if you want to disable it (faster import)')

        parser = NmapResultsParser(file, self.settings.services)
        results = parser.parse(not args.no_http_recheck)
        if results is not None:
            if len(results) == 0:
                logger.warning('No new service has been added into current mission')
            else:
                req = HostsRequester(self.sqlsess)
                req.select_mission(self.current_mission)
                for host in results:
                    req.add_or_merge_host(host)
                logger.success('Nmap results imported with success into current mission')

        print()
Ejemplo n.º 5
0
    def check(self):
        """
        Check the toolbox: Run all check commands (when available) from all
        installed tools, in automatic mode (i.e. checks based on exit codes)

        In case of an error code returned by one check command (!= 0), the function
        stops and exits the program with exit code 1 (error). 
        Otherwise, if all check commands have returned a success exit code (0), 
        it exits the program with exit code 0 (success).

        Designed to be used for Continuous Integration.
        """
        Output.title1('Automatic check of installed tools')
        for service in self.services:
            for tool in self.tools[service]:
                if tool.installed:
                    # Automatic mode (no prompt), only based on exit status
                    status = tool.run_check_command(fast_mode=True)
                    if not status:
                        logger.error('An error occured with the tool "{tool}". Exit ' \
                            'check with exit code 1...'.format(tool=tool.name))
                        sys.exit(1)
                print()
                print()

        logger.success('No error has been detected with all tools check commands. ' \
            'Exit with success code 0...')
        sys.exit(0)
 def edit_comment(self, comment):
     results = self.get_results()
     if not results:
         logger.error('No mission with this name')
     else:
         for r in results:
             r.comment = comment
         self.sqlsess.commit()
         logger.success('Comment edited')
 def rename(self, old, new):
     if old == 'default':
         logger.warning('Default mission cannot be renamed')
     else:
         mission = self.sqlsess.query(Mission).filter(
             Mission.name == old).first()
         mission.name = new
         self.sqlsess.commit()
         logger.success('Mission renamed: {old} -> {new}'.format())
 def delete(self):
     results = self.get_results()
     if not results:
         logger.error('No mission with this name')
     else:
         for r in results:
             self.sqlsess.delete(r)
         self.sqlsess.commit()
         logger.success('Mission deleted')
 def edit_comment(self, comment):
     results = self.get_results()
     if not results:
         logger.error('No matching credential')
     else:
         for r in results:
             r.comment = comment
         self.sqlsess.commit()
         logger.success('Comment edited')
    def add_service(self, ip, hostname, port, protocol, service):
        protocol = {
            'tcp': Protocol.TCP,
            'udp': Protocol.UDP
        }.get(protocol, Protocol.TCP)
        matching_service = self.sqlsess.query(Service).join(Host).join(Mission)\
                                       .filter(Mission.name == self.current_mission)\
                                       .filter(Host.ip == ip)\
                                       .filter(Service.port == int(port))\
                                       .filter(Service.protocol == protocol).first()
        if protocol == Protocol.TCP:
            up = NetUtils.is_tcp_port_open(ip, port)
        else:
            up = NetUtils.is_udp_port_open(ip, port)

        if matching_service:
            logger.warning('Service already present into database')
        else:
            if up:
                logger.info(
                    'Grabbing banner from {ip}:{port} with Nmap...'.format(
                        ip=ip, port=port))
                banner = NetUtils.grab_banner_nmap(ip, port)
                logger.info('Banner: {}'.format(banner or 'None'))
                os = NetUtils.os_from_nmap_banner(banner)
                if os:
                    logger.info('Detected Host OS: {}'.format(os))
            else:
                logger.warning('Port seems to be closed !')

            # Add service in db (and host if not existing)
            service = Service(name=service,
                              port=int(port),
                              protocol=protocol,
                              up=up,
                              banner=banner)

            matching_host = self.sqlsess.query(Host).join(Mission)\
                                        .filter(Mission.name == self.current_mission)\
                                        .filter(Host.ip == ip).first()
            new_host = Host(ip=ip, hostname=hostname, os=os)
            if matching_host:
                matching_host.merge(new_host)
                self.sqlsess.commit()
                service.host = matching_host
            else:
                mission = self.sqlsess.query(Mission).filter(
                    Mission.name == self.current_mission).first()
                new_host.mission = mission
                service.host = new_host
                self.sqlsess.add(new_host)

            self.sqlsess.add(service)
            self.sqlsess.commit()

            logger.success('Service added')
 def switch_https(self):
     results = self.get_results()
     if not results:
         logger.error('No matching service')
     else:
         for r in results:
             if r.url:
                 r.url = WebUtils.switch_http_https(r.url)
         self.sqlsess.commit()
         logger.success('Switch done')
Ejemplo n.º 12
0
    def add_cred(self, service_id, username, password, auth_type=None):
        """
        Add new credential for a given service.
        :param int service_id: Id of service
        :param str username: Username
        :param str password: Password (None if unknown)
        :param str auth_type: Authentication type for HTTP service
        """
        cred = self.sqlsess.query(Credential).join(Service)\
                           .filter(Service.id == service_id)\
                           .filter(Credential.username == username)\
                           .filter(Credential.password == password)\
                           .filter(Credential.type == auth_type).first()
        if cred:
            logger.warning('Credential already exists in database')
        else:
            service = self.sqlsess.query(Service).filter(Service.id == service_id)\
                                  .first()
            if not service:
                logger.error(
                    'Service id {id} is invalid'.format(id=service_id))
            else:
                cred = Credential(
                    username=username,
                    password=password,
                    type=auth_type if service.name == 'http' else None)

                self.sqlsess.add(cred)
                service.credentials.append(cred)

                username = '******' if username == '' else username
                password = {
                    '': '<empty>',
                    None: '<???>'
                }.get(password, password)
                auth_typ = '(' + str(auth_type) + ')' if auth_type else ''
                hostname = '(' + service.host.hostname + ')' if service.host.hostname else ''
                protocol = {
                    Protocol.TCP: 'tcp',
                    Protocol.UDP: 'udp'
                }.get(service.protocol)

                logger.success('Credential {username}/{password}{auth_type} added ' \
                    'to service {service} host={ip}{hostname} ' \
                    'port={port}/{proto}'.format(
                    username  = username,
                    password  = password,
                    auth_type = auth_typ,
                    service   = service.name,
                    ip        = service.host.ip,
                    hostname  = hostname,
                    port      = service.port,
                    proto     = protocol))

                self.sqlsess.commit()
Ejemplo n.º 13
0
 def switch_https(self):
     """Switch between HTTP and HTTPS on selected services"""
     results = self.get_results()
     if not results:
         logger.error('No matching service')
     else:
         for r in results:
             if r.url:
                 r.url = WebUtils.switch_http_https(r.url)
         self.sqlsess.commit()
         logger.success('Switch done')
Ejemplo n.º 14
0
    def __check_post_install_update(self, settings, fast_mode, update=False):
        """
        Post-install/update checks
        :param settings: Settings instance
        :param fast_mode: Boolean indicating whether prompts must be displayed or not
        :return: Boolean indicating status
        """
        mode = ('update', 'updated') if update else ('install', 'installed')
        status = True

        if not fast_mode:
            if not self.check_command:
                logger.info('No check_command defined in settings for {tool}, will assume it is ' \
                'correctly {mode}'.format(tool=self.name_display, mode=mode[1]))
            else:
                logger.info(
                    'Now, checking if {tool} has been {mode} correctly. Hit any key to run test...'
                    .format(tool=self.name_display, mode=mode[1]))
                CLIUtils.getch()
                status = self.__run_check_command()

        # Change install status in configuration file
        if status:
            try:
                if settings.change_installed_status(self.target_service,
                                                    self.name_clean,
                                                    install_status=True):
                    logger.success(
                        'Tool {tool} has been marked as successfully {mode}'.
                        format(tool=self.name_display, mode=mode[1]))
                    return True
                else:
                    logger.error(
                        'Error when updating configuration file "{filename}{ext}"'
                        .format(filename=INSTALL_STATUS_CONF_FILE,
                                ext=CONF_EXT))
                    return False
            except SettingsException as e:
                logger.error('An unexpected error occured when trying to mark the tool as {mode}: ' \
                    '{exception}'.format(exception=e, mode=mode[1]))
                if not update:
                    self.remove(settings)
                return False
        else:
            logger.warning('Tool {tool} has not been marked as {mode}'.format(
                tool=self.name_display, mode=mode[1]))
            if not update:
                self.remove(settings)
            else:
                if not fast_mode and Output.prompt_confirm(
                        'Do you want to try to re-install ?', default=True):
                    return self.__reinstall(settings, fast_mode)
            return False
Ejemplo n.º 15
0
    def add_target(self, target):
        """
        Add a new service into the current mission scope in database from a Target 
        object.

        :param Target target: Target to add
        """
        mission = self.sqlsess.query(Mission)\
                      .filter(Mission.name == self.current_mission).first()

        matching_service = self.sqlsess.query(Service)\
                              .join(Host)\
                              .join(Mission)\
                              .filter(Host.ip == target.get_ip())\
                              .filter(Mission.name == self.current_mission)\
                              .filter(Service.name == target.get_service_name())\
                              .filter(Service.port == target.get_port())\
                              .filter(Service.protocol == target.get_protocol2())\
                              .filter(Service.url == target.get_url()).first()

        # If service already exists in db, update it if necessary
        if matching_service:
            logger.info('A matching service has been found in the database')
            matching_service.merge(target.service)
            self.sqlsess.commit()
            # Make sure to replace target info by newly created service
            target.service = matching_service

        # Add host in db if it does not exist or update its info (merging)
        else:
            host = self.sqlsess.query(Host).join(Mission)\
                               .filter(Mission.name == self.current_mission)\
                               .filter(Host.ip == target.get_ip()).first()
            if host:
                host.merge(target.service.host)
                self.sqlsess.commit()
                target.service.host = host
            else:
                self.sqlsess.add(target.service.host)
                mission.hosts.append(target.service.host)
                self.sqlsess.commit()

            # Add service in db
            self.sqlsess.add(target.service)
            self.sqlsess.commit()

        logger.success('{action}: host {ip} | port {port}/{proto} | ' \
            'service {service}'.format(
            action  = 'Updated' if matching_service else 'Added',
            ip      = target.get_ip(),
            port    = target.get_port(),
            proto   = target.get_protocol(),
            service = target.get_service_name()))
Ejemplo n.º 16
0
    def add_cred(self, username, password, auth_type=None):
        """
        Add new credential for selected service(s).

        :param str username: Username
        :param str password: Password (None if unknown)
        :param str auth_type: Authentication type for HTTP service
        """
        results = self.get_results()
        if not results:
            logger.error('No matching service')
        else:
            for r in results:
                cred = self.sqlsess.query(Credential).join(Service)\
                                   .filter(Service.id == r.id)\
                                   .filter(Credential.username == username)\
                                   .filter(Credential.password == password)\
                                   .filter(Credential.type == auth_type).first()
                if not cred:
                    cred = Credential(
                        username=username,
                        password=password,
                        type=auth_type if r.name == 'http' else None)

                    self.sqlsess.add(cred)
                    r.credentials.append(cred)

                    username = '******' if cred.username == '' else cred.username
                    password = {
                        '': '<empty>',
                        None: '<???>'
                    }.get(cred.password, cred.password)
                    auth_type = '('+str(auth_type)+')' if \
                        (auth_type and r.name == 'http') else ''
                    hostname = '(' + r.host.hostname + ')' if r.host.hostname else ''
                    protocol = {
                        Protocol.TCP: 'tcp',
                        Protocol.UDP: 'udp'
                    }.get(r.protocol)

                    logger.success('Credential {username}/{password}{auth_type} ' \
                        'added to service {service} host={ip}{hostname} ' \
                        'port={port}/{proto}'.format(
                            username  = username,
                            password  = password,
                            auth_type = auth_type,
                            service   = r.name,
                            ip        = r.host.ip,
                            hostname  = hostname,
                            port      = r.port,
                            proto     = protocol))

            self.sqlsess.commit()
 def add(self, name):
     mission = self.sqlsess.query(Mission).filter(
         Mission.name == name).first()
     if mission:
         logger.warning('A mission named "{name}" already exists'.format(
             name=mission.name))
         return False
     else:
         self.sqlsess.add(Mission(name=name))
         self.sqlsess.commit()
         logger.success(
             'Mission "{name}" successfully added'.format(name=name))
         return True
Ejemplo n.º 18
0
 def rename(self, old, new):
     """
     Rename selected missions.
     :param str old: Name of the mission to rename
     :param str new: New mission name
     """
     if old == 'default':
         logger.warning('Default mission cannot be renamed')
     else:
         mission = self.sqlsess.query(Mission).filter(Mission.name == old).first()
         mission.name = new
         self.sqlsess.commit()
         logger.success('Mission renamed: {old} -> {new}'.format())
Ejemplo n.º 19
0
 def edit_comment(self, comment):
     """
     Edit comment of selected missions.
     :param str comment: New comment
     """
     results = self.get_results()
     if not results:
         logger.error('No mission with this name')
     else:
         for r in results:
             r.comment = comment
         self.sqlsess.commit()
         logger.success('Comment edited')
Ejemplo n.º 20
0
 def edit_comment(self, comment):
     """
     Edit comment of selected credentials.
     :param str comment: New comment
     """
     results = self.get_results()
     if not results:
         logger.error('No matching credential')
     else:
         for r in results:
             r.comment = comment
         self.sqlsess.commit()
         logger.success('Comment edited')
Ejemplo n.º 21
0
    def remove(self, settings):
        """
        Remove the tool:
            - Remove tool directory into toolbox
            - Change install status to false.

        :param Settings settings: Settings from config files
        :return: Removal status
        :rtype: bool
        """

        # Delete tool directory if tool was installed inside toolbox directory
        if self.install_command:
            if not FileUtils.is_dir(self.tool_dir):
                logger.warning('Directory "{dir}" does not exist'.format(
                    dir=self.tool_dir))
                #return False
            elif not FileUtils.remove_directory(self.tool_dir):
                logger.error('Unable to delete directory "{dir}". ' \
                    'Check permissions and/or re-run with sudo'.format(
                        dir=self.tool_dir))
                return False
            else:
                logger.success(
                    'Tool directory "{dir}" deleted'.format(dir=self.tool_dir))

        # Remove virtualenv files if necessary
        virtualenv_dir = '{}/{}'.format(VIRTUALENVS_DIR, self.name)
        if FileUtils.is_dir(virtualenv_dir):
            if FileUtils.remove_directory(virtualenv_dir):
                logger.success('Virtualenv directory deleted')
            else:
                logger.warning('Unable to delete Virtualenv directory')

        if self.virtualenv.startswith('ruby'):
            logger.info('Delete RVM environment ({ruby}@{name})...'.format(
                ruby=self.virtualenv, name=self.name))
            cmd = 'source /usr/local/rvm/scripts/rvm; rvm use {ruby} && ' \
                'rvm gemset delete {name} --force'.format(
                    ruby=self.virtualenv,
                    name=self.name)
            returncode, _ = ProcessLauncher(cmd).start()

            if returncode == 0:
                logger.success('RVM environment deleted with success')
            else:
                logger.warning('Unable to delete RVM environment')

        # Make sure "installed" option in config file is set to False
        if settings.change_installed_status(self.target_service,
                                            self.name,
                                            install_status=False):
            logger.success('Tool marked as uninstalled')
        else:
            logger.error('An unexpected error occured when trying to mark the tool ' \
                'as uninstalled !')
            return False

        self.installed = False
        return True
Ejemplo n.º 22
0
 def reset(self):
     """Delete all missions in database (re-create a fresh "default" mission)"""
     self.sqlsess.query(Mission).delete()
     self.sqlsess.query(CommandOutput).delete()
     self.sqlsess.query(Credential).delete()
     self.sqlsess.query(Host).delete()
     self.sqlsess.query(Option).delete()
     self.sqlsess.query(Product).delete()
     self.sqlsess.query(Result).delete()
     self.sqlsess.query(Screenshot).delete()
     self.sqlsess.query(Service).delete()
     self.sqlsess.query(Vuln).delete()
     self.sqlsess.commit()
     self.sqlsess.add(Mission(name='default', comment='Default scope'))
     self.sqlsess.commit()
     logger.success('All missions deleted & fresh "default" mission created')
Ejemplo n.º 23
0
    def __check_pre_install(self, settings, fast_mode=False):
        """
        Perform some checks before trying to install the tool (already installed ?,
        install command ?).

        :param Settings settings: Settings from config files
        :param bool fast_mode: Set to true to disable prompts
        :return: Result of checks
        :rtype: bool
        """
        if self.installed:
            logger.info('{tool} is already installed (according to settings), ' \
                'skipped'.format(tool=self.name))
            return False

        elif not self.install_command:
            logger.warning('The tool {tool} has no installation command specified in ' \
                'config file'.format(tool=self.name))

            if fast_mode \
               or Output.prompt_confirm('Is the tool already installed on your system ?',
                                        default=True):

                try:
                    if settings.change_installed_status(self.target_service, 
                                                        self.name, 
                                                        True):

                        logger.success('Tool {tool} has been marked as installed in ' \
                            'settings'.format(tool=self.name))
                        return True
                    else:
                        logger.error('Error when saving the configuration file ' \
                            '"{filename}{ext}"'.format(
                                filename=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT))
                        return False

                except SettingsException as e:
                    logger.error(e)
                    self.remove(settings)
                    return False
            else:
                logger.info('Tool {tool} is still not marked as installed in ' \
                    'settings'.format(tool=self.name))
            return False

        return True
Ejemplo n.º 24
0
    def __check_pre_install(self, settings, fast_mode):
        """
        Checks to run before installing the tool
        :param settings: Settings instance
        :param fast_mode: Boolean indicating whether prompts must be displayed or not
        :return: Boolean indicating status
        """
        if self.installed:
            logger.info(
                '{tool} is already installed (according to settings), skipped'.
                format(tool=self.name_display))
            return False

        elif not self.install_command:
            logger.warning(
                'The tool {tool} has no installation command specified in config file'
                .format(tool=self.name_display))
            if fast_mode or Output.prompt_confirm(
                    'Is the tool already installed on your system ?',
                    default=True):
                try:
                    if settings.change_installed_status(
                            self.target_service, self.name_clean, True):
                        logger.success(
                            'Tool {tool} has been marked as installed in settings'
                            .format(tool=self.name_display))
                        return True
                    else:
                        logger.error(
                            'Error when saving the configuration file "{filename}{ext}"'
                            .format(filename=INSTALL_STATUS_CONF_FILE,
                                    ext=CONF_EXT))
                        return False
                except SettingsException as e:
                    logger.error(e)
                    self.remove(settings)
                    return False
            else:
                logger.info(
                    'Tool {tool} is still not marked as installed in settings'.
                    format(tool=self.name_display))
            return False

        return True
Ejemplo n.º 25
0
    def remove_for_service(self, service):
        """
        Remove the tools for a given service.

        :param str service: Name of the service targeted by the tools to remove
            (may be "multi")
        """
        if service not in self.services: return
        Output.title1(
            'Remove tools for service: {service}'.format(service=service))

        if not self.tools[service]:
            logger.info('No tool specific to this service in the toolbox')
        else:
            i = 1
            status = True
            for tool in self.tools[service]:
                if i > 1: print()
                Output.title2(
                    '[{svc}][{i:02}/{max:02}] Remove {tool_name}:'.format(
                        svc=service,
                        i=i,
                        max=len(self.tools[service]),
                        tool_name=tool.name))

                status &= tool.remove(self.settings)
                i += 1

            # Remove the service directory if all tools successfully removed
            if status:
                short_svc_path = '{toolbox}/{service}'.format(
                    toolbox=TOOLBOX_DIR, service=service)

                full_svc_path = FileUtils.absolute_path(short_svc_path)

                if FileUtils.remove_directory(full_svc_path):
                    logger.success(
                        'Toolbox service directory "{path}" deleted'.format(
                            path=short_svc_path))
                else:
                    logger.warning('Toolbox service directory "{path}" cannot be ' \
                        'deleted because it still stores some files'.format(
                            path=short_svc_path))
Ejemplo n.º 26
0
    def remove(self, settings):
        """
        Remove the tool:
            - Remove tool directory into toolbox
            - Change install status to false.

        :param Settings settings: Settings from config files
        :return: Removal status
        :rtype: bool
        """

        # Delete tool directory if tool was installed inside toolbox directory
        if self.install_command:
            if not FileUtils.is_dir(self.tool_dir):
                logger.warning('Directory "{dir}" does not exist'.format(
                    dir=self.tool_dir))
                return False
            elif not FileUtils.remove_directory(self.tool_dir):
                logger.error('Unable to delete directory "{dir}". ' \
                    'Check permissions and/or re-run with sudo'.format(
                        dir=self.tool_dir))  
                return False
            else:
                logger.success('Tool directory "{dir}" deleted'.format(
                    dir=self.tool_dir))

        # Make sure "installed" option in config file is set to False
        if settings.change_installed_status(self.target_service, 
                                            self.name, 
                                            install_status=False):
            logger.success('Tool marked as uninstalled')
        else:
            logger.error('An unexpected error occured when trying to mark the tool ' \
                'as uninstalled !')
            return False

        self.installed = False
        return True
Ejemplo n.º 27
0
    def __run_install_update(self, fast_mode, update=False):
        """
        Run install/update command
        :param update: Mode selector, True for update | False for install (default)
        :return: Boolean indicating status
        """
        if update: cmd = self.update_command.get_cmdline(self.tool_dir)
        else: cmd = self.install_command.get_cmdline(self.tool_dir)

        mode = 'update' if update else 'install'

        logger.info('Description: {descr}'.format(descr=self.description))
        #Output.print('{mode} command : {cmd}'.format(mode=mode.capitalize(), cmd=cmd_short))
        if fast_mode or Output.prompt_confirm(
                'Confirm {mode} ?'.format(mode=mode), default=True):
            Output.begin_cmd(cmd)
            ProcessLauncher(cmd).start()
            Output.delimiter()
            logger.success('Tool {mode} has finished'.format(mode=mode))
            return True
        else:
            logger.warning('Tool {mode} aborted'.format(mode=mode))
            return False
Ejemplo n.º 28
0
    def run_check_command(self, fast_mode=False):
        """
        Run the check command.
        The goal is to quickly check if the tool is not buggy or missing some 
        dependencies. The user must analyze the output and gives an answer.

        :param bool fast_mode: Set to true to disable prompts

        :return: Response from user in interactive mode, otherwise status
            based on exit code (True if exit code is 0)
        :rtype: bool
        """
        if not self.check_command:
            logger.info('No check_command defined in settings for the tool ' \
                '{tool}'.format(tool=self.name))
            return True

        logger.info('Running the check command for the tool {tool}...'.format(
            tool=self.name))

        cmd = self.check_command.get_cmdline(self)

        Output.begin_cmd(cmd)
        returncode, _ = ProcessLauncher(cmd).start()
        Output.delimiter()

        if returncode != 0:
            logger.warning('Check command has finished with an error ' \
                'exit code: {code}'.format(code=returncode))
        else:
            logger.success('Check command has finished with success exit code')

        if fast_mode:
            return (returncode == 0)
        else:
            return Output.prompt_confirm('Does the tool {tool} seem to be running ' \
                'correctly ?'.format(tool=self.name), default=True)
Ejemplo n.º 29
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
Ejemplo n.º 30
0
    def run(self):

        # Create report directory
        dirname = '{mission}-{datetime}'.format(
            mission=StringUtils.clean(self.mission.replace(' ', '_'),
                                      allowed_specials=('_', '-')),
            datetime=datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
        self.output_path = self.output_path + '/' + dirname

        if not FileUtils.create_directory(self.output_path):
            logger.error('Unable to create report directory: "{path}"'.format(
                path=self.output_path))
            return False

        # Retrieve all services in selected mission
        req = ServicesRequester(self.sqlsession)
        req.select_mission(self.mission)
        services = req.get_results()

        # Generate screenshots
        processor = ScreenshotsProcessor(self.mission, self.sqlsession)
        processor.run()

        screens_dir = self.output_path + '/screenshots'
        if not FileUtils.create_directory(screens_dir):
            logger.warning(
                'Unable to create screenshots directory: "{path}"'.format(
                    path=screens_dir))
        else:
            for service in services:
                if service.name == 'http' and service.screenshot is not None \
                        and service.screenshot.status == ScreenStatus.OK:

                    img_name = 'scren-{ip}-{port}-{id}'.format(
                        ip=str(service.host.ip),
                        port=service.port,
                        id=service.id)
                    path = screens_dir + '/' + img_name

                    ImageUtils.save_image(service.screenshot.image,
                                          path + '.png')
                    ImageUtils.save_image(service.screenshot.thumbnail,
                                          path + '.thumb.png')

        # Create index.html
        html = self.__generate_index()
        if FileUtils.write(self.output_path + '/index.html', html):
            logger.info('index.html file generated')
        else:
            logger.error('An error occured while generating index.html')
            return False

        # Create results-<service>.html (1 for each service)
        for service in services:
            # Useless to create page when no check has been run for the service
            if len(service.results) == 0:
                continue

            html = self.__generate_results_page(service)
            # Create a unique name for the service HTML file
            filename = 'results-{ip}-{port}-{service}-{id}.html'.format(
                ip=str(service.host.ip),
                port=service.port,
                service=service.name,
                id=service.id)
            if FileUtils.write(self.output_path + '/' + filename, html):
                logger.info(
                    '{filename} file generated'.format(filename=filename))
            else:
                logger.error(
                    'An error occured while generating {filename}'.format(
                        filename=filename))
                return False

        logger.success('HTML Report written with success in: {path}'.format(
            path=self.output_path))
        logger.info('Important: If running from Docker container, make sure to run ' \
            '"xhost +" on the host before')
        if Output.prompt_confirm('Would you like to open the report now ?',
                                 default=True):
            webbrowser.open(self.output_path + '/index.html')

        return True