Source code for sslscan

import importlib
import logging
import os
import re

from pprint import pformat

from six.moves.urllib.parse import urlparse, parse_qs

import flextls

from sslscan.__about__ import (
    __author__, __copyright__, __email__, __license__, __summary__, __title__,
    __uri__, __version__
)
from sslscan.config import ScanConfig
from sslscan.exception import ModuleNotFound
from sslscan.kb import KnowledgeBase
from sslscan.module.handler import BaseHandler
from sslscan.module.rating import BaseRating
from sslscan.module.report import BaseReport
from sslscan.module.scan import BaseScan


logger = logging.getLogger(__name__)


[docs]class Scanner(object): """ The main scanner object. """ config_options = [ ( "ssl2", { "default": False, "negation": "no-ssl2", "help": "", "type": "bool" } ), ( "ssl3", { "default": False, "negation": "no-ssl3", "help": "", "type": "bool" } ), ( "tls10", { "default": False, "negation": "no-tls10", "help": "", "type": "bool" } ), ( "tls11", { "default": False, "negation": "no-tls11", "help": "", "type": "bool" } ), ( "tls12", { "default": False, "negation": "no-tls12", "help": "", "type": "bool" } ), ( "dtls10", { "default": False, "negation": "no-dtls10", "help": "", "type": "bool" } ), ( "dtls12", { "default": False, "negation": "no-dtls12", "help": "", "type": "bool" } ) ] def __init__(self, module_manager=None): global modules self._module_manager = module_manager if self._module_manager is None: self._module_manager = modules self._modules = [] self.config = ScanConfig(options=self.config_options) self.handler = None self._kb = KnowledgeBase()
[docs] def append(self, module): """ Append a scan or report module. :param module: Instance of a scan or report module """ module.set_scanner(self) self._modules.append(module)
[docs] def append_load(self, name, config, base_class=None): """ Append a module but load it first by using the module manager. :param String name: Name of the module to load :param Mixed config: Config of the module :param class base_class: Module lookup filter :return: False if module not found """ module = self._module_manager.get(name, base_class=base_class) if module is None: raise ModuleNotFound(name=name,base_class=base_class) module = module(scanner=self) module.config.set_values(config) self.append(module)
[docs] def get_enabled_versions(self): """ Uses the scanner config to create and return a list of all enabled SSL/TLS protocol versions. :return: List of methods :rtype: List """ versions = [] if self.config.get_value('ssl2'): versions.append(flextls.registry.version.SSLv2) if self.config.get_value('ssl3'): versions.append(flextls.registry.version.SSLv3) if self.config.get_value('tls10'): versions.append(flextls.registry.version.TLSv10) if self.config.get_value('tls11'): versions.append(flextls.registry.version.TLSv11) if self.config.get_value('tls12'): versions.append(flextls.registry.version.TLSv12) if self.config.get_value('dtls10'): versions.append(flextls.registry.version.DTLSv10) if self.config.get_value('dtls12'): versions.append(flextls.registry.version.DTLSv12) return versions
[docs] def get_handler(self): """ Get the active protocol handler. :return: Instance of the handler :rtype: sslscan.module.handler.BaseHandler """ return self.handler
[docs] def get_knowledge_base(self): """Return the knowledge base used by this scanner.""" return self._kb
[docs] def get_module_manager(self): """Return the active module manager for this scanner.""" return self._module_manager
[docs] def load_handler_from_uri(self, host_uri): """ Load a handler from a given uri. :param String host_uri: The URI :return: The handler """ logger.debug("Loading handler from URI: %s", host_uri) if not re.search('^([a-zA-Z0-9]+:)?\/\/', host_uri): host_uri = '//' + host_uri uri = urlparse(host_uri) name = uri.scheme name = name.lower() if name == '': name = 'tcp' module = self._module_manager.get(name, base_class=BaseHandler) if module is None: return False module = module(host=uri.hostname, port=uri.port, scanner=self) tmp = parse_qs(uri.query, keep_blank_values=True) config = {} for k, v in tmp.items(): config[k] = v[0] logger.debug("Extracted config values: %s", pformat(config)) module.config.set_values(config) return module
[docs] def load_rating(self, name): """ Use the active module manager to load a rating module :param String name: Name of the rating module """ module = self._module_manager.get(name, base_class=BaseRating) if module is None: if name == "none": raise Exception("Internal error unable to load 'none' rating") return self.load_rating("none") return module(scanner=self)
[docs] def reset_knowledge_base(self): """Create and activate a new knowledge base for this scanner.""" self._kb = KnowledgeBase()
[docs] def run(self): """Execute all scan and report modules attached to the scanner.""" self.run_scans() self.run_reports()
[docs] def run_reports(self): """Execute all report modules attached to the scanner.""" for module in self._modules: if not isinstance(module, BaseReport): continue logger.info("Running report module '%s' ...", str(module)) module.run()
[docs] def run_scans(self): """Execute all scan modules attached to the scanner.""" for module in self._modules: if not isinstance(module, BaseScan): continue logger.info("Running scan module '%s' ...", str(module)) module.run()
[docs] def set_handler(self, handler): """ Set the active protocol handler. :param handler: Instance of the handler """ self.handler = handler
class ModuleManager(object): """ Manager all modules """ def __init__(self): self._modules = [] def get(self, name, base_class=None): """ Return a module. :param String name: Name of the module :param class base_class: The filter :return: If module exists return it or if not return None :rtype: Mixed """ for module in self._modules: if base_class is not None and not issubclass(module, base_class): continue if module.name == name: return module return None def get_modules(self, base_class=None): """ Return a list of available modules. Use the base_class as filter option :param class base_class: The filter :rtype: List """ result = [] for module in self._modules: if base_class is None or issubclass(module, base_class): result.append(module) return result def register(self, module): """ Register a new module. """ if module in self._modules: # ToDo: error handling return self._modules.append(module) def load_modules(self, pkg_names): """ Load all modules provided by a given python package. :param List pkg_names: List of String with package names """ logger.info("Loading modules ...") for base_pkg_name in pkg_names: logger.debug("Base package name: %s", base_pkg_name) base_pkg = importlib.import_module(base_pkg_name) logger.debug("Base package: %s", base_pkg) path = base_pkg.__path__[0] logger.debug("Base path: %s", path) for filename in os.listdir(path): if filename == "__init__.py": continue pkg_name = None if os.path.isdir(os.path.join(path, filename)) and \ os.path.exists(os.path.join(path, filename, "__init__.py")): pkg_name = filename if filename[-3:] == '.py': pkg_name = filename[:-3] if pkg_name is None: continue mod_name = "{}.{}".format(base_pkg_name, pkg_name) try: importlib.import_module(mod_name) logger.info("Loaded '%s' successfully", mod_name) except Exception as msg: logger.warning("Unable to load: '%s'", mod_name) logger.debug("An error occurred while importing '%s'", mod_name, exc_info=True) def load_global_modules(self): """ Load all global modules. """ pkg_names = [ "sslscan.module.handler", "sslscan.module.rating", "sslscan.module.report", "sslscan.module.scan" ] return self.load_modules(pkg_names) modules = ModuleManager()