Exemple #1
0
    def __parse_conf_file(self):
        parser = DefaultConfigParser()
        # Utf-8 to avoid encoding issues
        parser.read(EXPLOITS_CONF, 'utf8')

        for section in parser.sections():
            # Vulnerable product name
            product = parser.safe_get(section, 'product', '', None)
            if not product:
                raise SettingsException('No vulnerable product name specified for ' \
                    '[{}]'.format(section))

            # Vulnerability description
            description = parser.safe_get(section, 'description', '', None)
            if not description:
                raise SettingsException('Missing vulnerability description for ' \
                    '[{}]'.format(section))

            # Vulnerability type
            type_ = parser.safe_get(section, 'type', '', None)
            if type_ not in SUPPORTED_TYPES:
                raise SettingsException(
                    'Unsupported vulnerability type for [{}]'.format(section))

            # Detection command
            detection_rawcmd = parser.safe_get(section, 'detection_cmd', '',
                                               None)

            # Detection command output success
            detection_success = parser.safe_get(section, 'detection_success',
                                                '', None)
            if detection_rawcmd and len(
                    detection_rawcmd) > 0 and not detection_success:
                raise SettingsException('Missing "detection_success" for [{}] since ' \
                    '"detection_cmd" is defined'.format(section))

            # Exploit command
            exploit_rawcmd = parser.safe_get(section, 'exploit_cmd', '', None)

            # Exploit RCE output
            exploit_rce_output = parser.safe_get_boolean(
                section, 'exploit_rce_output', True)

            # Exploit command output success (for auto test when exploit_rce_output == True)
            exploit_success = parser.safe_get(section, 'exploit_success', '',
                                              None)
            if exploit_rawcmd and \
               len(exploit_rawcmd) > 0 and \
               exploit_rce_output and \
               not exploit_success:
                raise SettingsException(
                    'Missing "exploit_success" for [{}] since '
                    '"exploit_cmd" is defined and "exploit_rce_output=true"'.
                    format(section))

            exploit = Exploit(section, product, description, type_,
                              detection_rawcmd, detection_success,
                              exploit_rawcmd, exploit_rce_output,
                              exploit_success)
            self.exploits.append(exploit)
Exemple #2
0
    def __parse_section_supported_list_options(self, service, service_config):
        """
        Parse section [supported_list_options] in <service_name>.conf and update service 
        configuration with supported values for specific options of type list.
        Must be called after self.__parse_section_config() and 
        self.__parse_section_specific_options().

        :param defaultdict(str) service_config: Information about the service, updated 
            into this method
        :return: None
        :raises SettingsException: Exception raised if any unrecoverable error is 
            encountered while parsing the section
        """

        # Get names of specific options of type list
        options_list = list(
            filter(
                lambda x: service_config['specific_options'][x] == OptionType.
                LIST, service_config['specific_options'].keys()))

        if not options_list:
            return
        elif not self.config_parsers[service].has_section(
                'supported_list_options'):
            raise SettingsException('[{filename}{ext}] Missing section ' \
                '[supported_list_options] to store supported values for specific ' \
                'options of type "list"'.format(filename=service, ext=CONF_EXT))

        log_prefix = '[{filename}{ext} | Section "supported_list_options"]'.format(
            filename=service, ext=CONF_EXT)

        supported_list_options = dict()
        optparsed = self.config_parsers[service].options(
            'supported_list_options')

        # Loop over specific options of type list
        for opt in options_list:

            # If missing option
            if 'supported_' + opt not in optparsed:
                raise SettingsException('{prefix} No option "supported_{option}" ' \
                    'is defined'.format(prefix=log_prefix, option=opt))

            # Values are put in lowercase, no spaces, no special chars (except -, _)
            values = list(
                map(
                    lambda x: StringUtils.clean(x.lower(),
                                                allowed_specials=('-', '_')),
                    self.config_parsers[service].safe_get_list(
                        'supported_list_options', 'supported_' + opt, ',',
                        [])))

            if not values:
                raise SettingsException('{prefix} Option "supported_{option}" is ' \
                    'empty'.format(prefix=log_prefix, option=opt))

            supported_list_options[opt] = values

        # Update service configuration with lists of supported values
        service_config['supported_list_options'] = supported_list_options
Exemple #3
0
    def __init__(self):
        """
        :raises SettingsException:
        """
        self.config_parsers = dict() # dict of DefaultConfigParser indexed by filename
        self.toolbox        = None   # Receives Toolbox object
        self.services       = None   # Receives ServicesConfig object

        # Check directory and presence of *.conf files
        if not FileUtils.is_dir(SETTINGS_DIR):
            raise SettingsException('Configuration directory ({dir}) does not exist'.format(dir=SETTINGS_DIR))
        files = FileUtils.list_directory(SETTINGS_DIR)
        for f in files:
            if not FileUtils.check_extension(f, CONF_EXT):
                files.remove(f)

        if not files:
            raise SettingsException('Configuration directory ({dir}) does not store any *.conf file'.format(
                dir=SETTINGS_DIR))

        if TOOLBOX_CONF_FILE+CONF_EXT not in files:
            raise SettingsException('Missing mandatory {toolbox}{ext} settings file in directory "{dir}"'.format(
                toolbox=TOOLBOX_CONF_FILE, ext=CONF_EXT, dir=SETTINGS_DIR))

        if INSTALL_STATUS_CONF_FILE+CONF_EXT not in files:
            open(SETTINGS_DIR+'/'+INSTALL_STATUS_CONF_FILE+CONF_EXT, 'a').close()
            logger.info('{status}{ext} settings file created in directory "{dir}"'.format(
                status=INSTALL_STATUS_CONF_FILE, ext=CONF_EXT, dir=SETTINGS_DIR))
            files.append(INSTALL_STATUS_CONF_FILE+CONF_EXT)

        # Parse settings files, add tools inside toolbox and create scan configs
        self.__parse_all_conf_files(files)
        self.__create_toolbox()
        self.__create_all_services_checks()
Exemple #4
0
    def __parse_section_config(self, service, service_config):
        """
        Parse section [config] in <service_name>.conf, retrieve basic info about service
        (default port/protocol) and retrieve list of supported categories of checks for
        this service.

        :param str service: Service name
        :param defaultdict(str) service_config: Information about the service, updated 
            into this method
        :return: List of categories of checks
        :rtype: list(str)
        :raises SettingsException: Exception raised if any unrecoverable error is 
            encountered while parsing the section
        """
        log_prefix = '[{filename}{ext} | Section "config"]'.format(
            filename=service, ext=CONF_EXT)

        # Check presence of mandatory options in [config]
        optparsed = self.config_parsers[service].options('config')
        for opt in SERVICE_CHECKS_CONFIG_OPTIONS[MANDATORY]:
            if opt not in optparsed:
                raise SettingsException('{prefix} Missing mandatory option "{option}"' \
                    ', check the file'.format(prefix=log_prefix, option=opt))

        # Get port number
        default_port = self.config_parsers[service].safe_get_int(
            'config', 'default_port', None, None)

        if default_port is None or default_port < 0 or default_port > 65535:
            raise SettingsException('{prefix} Invalid value for option "default_port",' \
                ' must be in the range [0-65535]'.format(prefix=log_prefix))

        # Get protocol
        protocol = self.config_parsers[service].safe_get_lower(
            'config', 'protocol', 'tcp', ['tcp', 'udp'])

        # Get categories of checks as a list, clean each element
        categories = list(map(lambda x: StringUtils.clean(
            x.lower(), allowed_specials=('-', '_')), 
            self.config_parsers[service].safe_get_list('config', 'categories', ',', [])))

        if not categories:
            raise SettingsException('{prefix} Option "categories" must have at least '\
                'one category'.format(prefix=log_prefix))

        # Get authentication type (for HTTP) as a list, clean each element
        if 'auth_types' in optparsed:
            auth_types = list(map(lambda x: StringUtils.clean(
                x.lower(), allowed_specials=('-', '_')),
                self.config_parsers[service].safe_get_list(
                    'config', 'auth_types', ',', [])))
        else:
            auth_types = None

        # Update service configuration with parsed information
        service_config['default_port'] = default_port
        service_config['protocol']     = protocol
        service_config['auth_types']   = auth_types

        return categories
Exemple #5
0
    def __init__(self):
        """
        Start the parsing of settings files and create the Settings object.

        :raises SettingsException: Exception raised if any error is encountered while 
            parsing files (syntax error, missing mandatory file...)
        """
        self.config_parsers = dict(
        )  # Dict of DefaultConfigParser indexed by filename
        self.toolbox = None  # Receives Toolbox object
        self.services = None  # Receives ServicesConfig object
        self.attack_profiles = None  # Receives AttackProfiles object

        # Check directory
        if not FileUtils.is_dir(SETTINGS_DIR):
            raise SettingsException('Configuration directory ({dir}) does not ' \
                'exist'.format(dir=SETTINGS_DIR))

        # Check presence of *.conf files
        files = FileUtils.list_directory(SETTINGS_DIR)
        for f in files:
            if not FileUtils.check_extension(f, CONF_EXT):
                files.remove(f)

        if not files:
            raise SettingsException('Configuration directory ({dir}) does not ' \
                'store any *.conf file'.format(dir=SETTINGS_DIR))

        if TOOLBOX_CONF_FILE + CONF_EXT not in files:
            raise SettingsException('Missing mandatory {toolbox}{ext} settings ' \
                'file in directory "{dir}"'.format(
                    toolbox=TOOLBOX_CONF_FILE,
                    ext=CONF_EXT,
                    dir=SETTINGS_DIR))

        if ATTACK_PROFILES_CONF_FILE + CONF_EXT not in files:
            raise SettingsException('Missing mandatory {profiles}{ext} settings ' \
                'file in directory "{dir}"'.format(
                    profiles=ATTACK_PROFILES_CONF_FILE,
                    ext=CONF_EXT,
                    dir=SETTINGS_DIR))

        # Create _install_status.conf file if necessary
        if INSTALL_STATUS_CONF_FILE + CONF_EXT not in files:
            open(SETTINGS_DIR + '/' + INSTALL_STATUS_CONF_FILE + CONF_EXT,
                 'a').close()
            logger.info('{status}{ext} settings file created in directory ' \
                '"{dir}"'.format(status=INSTALL_STATUS_CONF_FILE,
                                 ext=CONF_EXT,
                                 dir=SETTINGS_DIR))
            files.append(INSTALL_STATUS_CONF_FILE + CONF_EXT)

        # Parse configuration files and create objects from them
        self.__parse_all_conf_files(files)
        self.__create_toolbox()
        self.__create_all_services_config_and_checks()
        self.__create_attack_profiles()
Exemple #6
0
    def change_installed_status(self, target_service, tool_name,
                                install_status):
        """
        Change the install status for a given tool.
        Change is made into the INSTALL_STATUS_CONF_FILE
        If tool installed, put the current datetime.

        :param str target_service: Name of service targeted by the tool
        :param str tool_name: Name of the tool
        :param bool install_status: New install status to set
        :return: Status of change
        :rtype: bool
        """
        if install_status:
            value = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        else:
            value = 'False'

        parser = self.config_parsers[INSTALL_STATUS_CONF_FILE]

        # Create the section [service] if needed
        if target_service not in parser.sections():
            parser.add_section(target_service)

        # Add/Update the install status
        if not parser.safe_set(target_service, tool_name, value):
            raise SettingsException('Unable to change install status value for the ' \
                'tool {tool}'.format(tool=tool_name))

        # Save change permanently into the file
        return self.save(INSTALL_STATUS_CONF_FILE)
Exemple #7
0
    def __parse_section_specific_options(self, service, service_config):
        """
        Parse section [specific_options] in <service_name>.conf and update service config
        :param service: Service name
        :param service_config: Dict storing info about service, updated into this method
        :return: None
        :raises SettingsException:
        """
        try:
            optparsed = self.config_parsers[service].options('specific_options')
        except configparser.NoSectionError:
            service_config['specific_options'] = dict()
            return 
        specific_options = dict()
        for opt in optparsed:
            option_type = self.config_parsers[service].safe_get_lower('specific_options', opt, None, None)
            if option_type.count(':') == 1:
                option_type, default_value = option_type.split(':')
            opt_clean   = StringUtils.clean(opt.lower(), allowed_specials=('-', '_'))

            if option_type == 'boolean' :  specific_options[opt_clean] = OptionType.BOOLEAN
            elif option_type == 'list'  :  specific_options[opt_clean] = OptionType.LIST
            elif option_type == 'var'   :  specific_options[opt_clean] = OptionType.VAR
            else:
                raise SettingsException('[{filename}{ext} | Section "specific_options"] Specific option named "{option}" has ' \
                    'an invalid type. Supported types are: boolean, list, var'.format(
                        filename = service, ext=CONF_EXT, option=opt))

        service_config['specific_options'] = specific_options
Exemple #8
0
    def __parse_conf_file(self):
        parser = DefaultConfigParser()
        # Utf-8 to avoid encoding issues
        parser.read(EXPLOITS_CONF, 'utf8')

        for section in parser.sections():
            type_ = parser.safe_get(section, 'type', '', None)
            if type_ not in SUPPORTED_TYPES:
                raise SettingsException('Unsupported exploit type for [{}]'.format(type_))

            rawcmd = parser.safe_get(section, 'command', '', None)
            if not rawcmd:
                raise SettingsException('No command specified for [{}]'.format(rawcmd))

            description = parser.safe_get(section, 'description', '', None)
            success = parser.safe_get(section, 'success', '', None)

            exploit = Exploit(section, description, type_, rawcmd, success)
            self.exploits.append(exploit)
Exemple #9
0
    def __parse_section_specific_options(self, service, service_config):
        """
        Parse section [specific_options] in <service_name>.conf and update service 
        configuration with supported specific options for the service and their
        respective types.

        :param str service: Service name
        :param defaultdict(str) service_config: Information about the service, updated 
            into this method
        :return: None
        :raises SettingsException: Exception raised if any unrecoverable error is 
            encountered while parsing the section
        """

        # Case when no [specific_options] can be found
        try:
            optparsed = self.config_parsers[service].options(
                'specific_options')
        except configparser.NoSectionError:
            service_config['specific_options'] = dict()
            return

        specific_options = dict()

        # Loop over supported specific options
        for opt in optparsed:
            # Get option type
            option_type = self.config_parsers[service].safe_get_lower(
                'specific_options', opt, None, None)

            # Handle case when default value is specified (for boolean)
            if option_type.count(':') == 1:
                option_type, default_value = option_type.split(':')

            opt_clean = StringUtils.clean(opt.lower(),
                                          allowed_specials=('-', '_'))

            if option_type == 'boolean':
                specific_options[opt_clean] = OptionType.BOOLEAN

            elif option_type == 'list':
                specific_options[opt_clean] = OptionType.LIST

            elif option_type == 'var':
                specific_options[opt_clean] = OptionType.VAR

            else:
                raise SettingsException('[{filename}{ext} | Section ' \
                    '"specific_options"]  Specific option named "{option}" has ' \
                    'an invalid type. Supported types are: boolean, list, var'.format(
                        filename = service, ext=CONF_EXT, option=opt))

        # Update service configuration with specific options names and types
        service_config['specific_options'] = specific_options
Exemple #10
0
    def __parse_section_products(self, service, service_config):
        """
        Parse section [products] in <service_name>.conf and retrieve supported values 
        for each product type.

        :param str service: Service name
        :param dict service_config: Service configuration, updated into this method
        :return: None
        :raises SettingsException: Exception raised if unconsistent values detected
        """

        # First, check if config file has a [products] section
        if not self.config_parsers[service].has_section('products'):
            service_config['products'] = dict()
            return

        log_prefix = '[{filename}{ext} | Section "products"]'.format(
            filename=service, ext=CONF_EXT)

        products = dict()
        optparsed = self.config_parsers[service].options('products')

        # Loop over product types in [products]
        for product_type in optparsed:

            # Clean the product type
            product_type = StringUtils.clean(product_type.lower(),
                                             allowed_specials=('-', '_'))

            # Get supported product names as a list.
            # Only some special chars allowed, spaces allowed
            # '/' is used to separate vendor name (optional) and product name
            product_names = self.config_parsers[service].safe_get_list(
                'products', product_type, ',', [])
            product_names = list(
                map(
                    lambda x: StringUtils.clean(
                        x, allowed_specials=('-', '_', '.', '/', '\\', ' ')),
                    product_names))

            if not product_names:
                raise SettingsException(
                    '{prefix} Option "{option}" is empty'.format(
                        prefix=log_prefix, option=opt))

            products[product_type] = product_names

        # Update service configuration with supported products
        service_config['products'] = products
        return
Exemple #11
0
    def __parse_section_config(self, service, service_config):
        """
        Parse section [config] in <service_name>.conf, retrieve basic info about service
        (default port/protocol) and retrieve list of categories.
        :param service: Service name
        :param service_config: Dict storing info about service, updated into this method
        :return: List of categories of checks
        :raises SettingsException:
        """
        log_prefix = '[{filename}{ext} | Section "config"]'.format(filename=service, ext=CONF_EXT)

        optparsed = self.config_parsers[service].options('config')
        for opt in SERVICE_CHECKS_CONFIG_OPTIONS[MANDATORY]:
            if opt not in optparsed:
                raise SettingsException('{prefix} Missing mandatory option "{option}", check the file'.format(
                    prefix=log_prefix, option=opt))

        default_port = self.config_parsers[service].safe_get_int('config', 'default_port', None, None)
        protocol     = self.config_parsers[service].safe_get_lower('config', 'protocol', 'tcp', ['tcp', 'udp'])
        categories   = list(map(lambda x: StringUtils.clean(x.lower(), allowed_specials=('-', '_')),
                           self.config_parsers[service].safe_get_list('config', 'categories', ',', [])))
        auth_types   = list(map(lambda x: StringUtils.clean(x.lower(), allowed_specials=('-', '_')),
                           self.config_parsers[service].safe_get_list('config', 'auth_types', ',', []))) \
                       if 'auth_types' in optparsed else None

        if default_port is None or default_port < 0 or default_port > 65535:
            raise SettingsException('{prefix} Invalid value for option "default_port", must be in the range ' \
                '[0-65535]'.format(prefix=log_prefix))

        if not categories:
            raise SettingsException('{prefix} Option "categories" must have at least one category'.format(
                prefix=log_prefix))

        service_config['default_port'] = default_port
        service_config['protocol']     = protocol
        service_config['auth_types']   = auth_types
        return categories
Exemple #12
0
    def __init__(self):
        """
        Start the parsing of settings files and create the Settings object.

        :raises SettingsException: Exception raised if any error is encountered while 
            parsing files
        """
        self.exploits = list()

        # Check presence of exploits.conf files
        if not os.access(EXPLOITS_CONF, os.F_OK):
            raise SettingsException('Missing configuration file exploits.conf')

        # Parse configuration file
        self.__parse_conf_file()
Exemple #13
0
    def __parse_section_supported_list_options(self, service, service_config):
        """
        Parse section [supported_list_options] in <service_name>.conf and retrieve 
        supported values for specific options of type list.
        Must be called after self.__parse_section_config() and self.__parse_section_specific_options()
        :param service: Service name
        :param service_config: Dict storing info about service, updated into this method
        :return: None
        :raises SettingsException:
        """
        options_list = list(filter(lambda x: service_config['specific_options'][x] == OptionType.LIST, 
                                   service_config['specific_options'].keys()))
        if not options_list:
            return dict()
        elif not self.config_parsers[service].has_section('supported_list_options'):
            raise SettingsException('[{filename}{ext}] Missing section [supported_list_options] to store supported ' \
                'values for specific options of type list'.format(filename=service, ext=CONF_EXT))

        log_prefix = '[{filename}{ext} | Section "supported_list_options"]'.format(filename=service, ext=CONF_EXT)
        supported_list_options = dict()
        optparsed = self.config_parsers[service].options('supported_list_options')

        for opt in options_list:
            if 'supported_'+opt not in optparsed:
                raise SettingsException('{prefix} No option "supported_{option}" is defined'.format(
                    prefix=log_prefix, option=opt))

            values = list(map(lambda x: StringUtils.clean(x.lower(), allowed_specials=('-', '_')), 
                         self.config_parsers[service].safe_get_list('supported_list_options', 
                         'supported_'+opt, ',', [])))
            if not values:
                raise SettingsException('{prefix} Option "supported_{option}" is empty'.format(
                    prefix=log_prefix, option=opt))
            supported_list_options[opt] = values

        service_config['supported_list_options'] = supported_list_options
Exemple #14
0
    def change_installed_status(self, target_service, tool_name, install_status):
        """
        Change the install status for a given tool.
        Change is made into the INSTALL_STATUS_CONF_FILE
        If tool installed, put the current datetime

        :param target_service: Name of service targeted by the tool
        :param tool_name: Tool name (Attention: must be the clean name !)
        :param install_status: New install status to set
        :return: Boolean indicating change status
        """
        # 
        value = datetime.now().strftime('%Y-%m-%d %H:%M:%S') if install_status else 'False'

        parser = self.config_parsers[INSTALL_STATUS_CONF_FILE]
        # Create the section [service] if needed
        if target_service not in parser.sections():
            parser.add_section(target_service)

        if not parser.safe_set(target_service, tool_name, value):
            raise SettingsException('Unable to change install status value for the tool {tool}'.format(
                tool=tool_name))

        return self.save(INSTALL_STATUS_CONF_FILE)