Example #1
0
    def __init__(self, debug=False):
        """
        Initialize Installer.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # Command configuraton path
        self._COMMAND_PATH = "securetea/lib/auto_server_patcher/configs/commands.json"
        # Load configuraton data
        self.config_data = self.open_json(self._COMMAND_PATH)

        # Categorize OS
        self.os_name = utils.categorize_os()
        if self.os_name:
            try:
                self.os_config_data = self.config_data[self.os_name]
            except KeyError:
                self.logger.log("Could not load OS configuraton data.",
                                logtype="error")
        else:
            self.logger.log("Could not determine OS specific config.")
Example #2
0
    def __init__(self, debug=False, url=None):
        """
        Initialize SSLScanner.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # API URL
        self._API_URL = "https://api.ssllabs.com/api/v3/analyze/"

        self.analyze_payload = {
            'startNew': 'on',
            'publish': 'off',
            'all': 'done',
            'ignoreMismatch': 'on'
        }

        # URL / website to scan
        self.url = str(url)
Example #3
0
    def __init__(self, debug=False, cred=None):
        """
        Initialize SecureTeaAutoServerPatcher.

        Args:
            debug (bool): Log on terminal or not
            url (str): URL to scan for SSL vulnerabilites

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        if not utils.check_root():
            self.logger.log("Please run as root, exiting.", logtype="error")
            sys.exit(0)

        if not cred:
            self.logger.log("No credentials specified.", logtype="error")
            sys.exit(0)

        # List of files to patch
        self.to_patch = list()

        url = cred['url']
        apache = int(cred['apache'])
        ssh = int(cred['ssh'])
        login = int(cred['login'])
        sysctl = int(cred['sysctl'])

        # Determine which file to patch
        if apache == 1:
            self.to_patch.append("apache")
        if ssh == 1:
            self.to_patch.append("ssh")
        if login == 1:
            self.to_patch.append("login")
        if sysctl == 1:
            self.to_patch.append("sysctl")

        if url and url != "XXXX":  # if valid URL
            self.url = url
        else:
            self.url = None

        # Create Installer object
        self.installer = Installer(debug=debug)
        # Create Patcher object
        self.patcher = ConfigPatcher(debug=debug, to_patch=self.to_patch)
        if self.url:
            # Create SSLScanner object
            self.ssl_scanner = SSLScanner(debug=debug, url=self.url)
Example #4
0
    def __init__(self, debug=False, to_patch=None):
        """
        Initialize ConfigPatcher.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # Configuration file path
        self._CONFIG_PATH = "/etc/securetea/asp/config.json"
        # Load configuration
        self.config_data = self.open_json(self._CONFIG_PATH)
        # Categorize OS
        os_name = utils.categorize_os()

        if os_name:
            try:
                self.os_config_data = self.config_data[
                    os_name]  # if OS in configuration
            except KeyError:
                self.logger.log("Could not load OS specific configuration.",
                                logtype="error")
        else:
            self.logger.log("Operating system cannot be determined.",
                            logtype="error")
            sys.exit(0)

        # List of files to patch
        if to_patch:
            self.to_patch = to_patch
        else:
            self.to_patch = []
Example #5
0
class ConfigPatcher(object):
    """ConfigPatcher class."""
    def __init__(self, debug=False, to_patch=None):
        """
        Initialize ConfigPatcher.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # Configuration file path
        self._CONFIG_PATH = "/etc/securetea/asp/config.json"
        # Load configuration
        self.config_data = self.open_json(self._CONFIG_PATH)
        # Categorize OS
        os_name = utils.categorize_os()

        if os_name:
            try:
                self.os_config_data = self.config_data[
                    os_name]  # if OS in configuration
            except KeyError:
                self.logger.log("Could not load OS specific configuration.",
                                logtype="error")
        else:
            self.logger.log("Operating system cannot be determined.",
                            logtype="error")
            sys.exit(0)

        # List of files to patch
        if to_patch:
            self.to_patch = to_patch
        else:
            self.to_patch = []

    def open_file(self, path):
        """
        Open the file and return the data as list.

        Args:
            path (str): Path of the file

        Raises:
            None

        Returns:
            None
        """
        try:
            with open(path, "r") as f:
                return f.readlines()
        except Exception as e:
            self.logger.log("Error occured: " + str(e), logtype="error")

    def open_json(self, path):
        """
        Open the JSON file and return the data as dict.

        Args:
            path (str): Path of the file

        Raises:
            None

        Returns:
            None
        """
        try:
            with open(path, "r") as json_data_file:
                data = json.load(json_data_file)
                return data
        except Exception as e:
            self.logger.log("Error occured: " + str(e), logtype="error")

    def write_data(self, path, data):
        """
        Write the data into the file.

        Args:
            path (str): Path of the file
            data (list): List of data to write

        Raises:
            None

        Returns:
            None
        """
        try:
            with open(path, "w") as wf:
                for line in data:
                    wf.write(line + "\n")
        except Exception as e:
            self.logger.log("Error occured: " + str(e), logtype="error")

    def patch(self):
        """
        Patch the configuration file based
        on the configuration data stored.

        Args:
            None

        Raises:
            None

        Returns:
            None
        """
        for path in self.os_config_data:  # iterate over the configuration
            patch_this = False  # patch this file or not
            for req_patch in self.to_patch:
                if req_patch in path:
                    patch_this = True

            if patch_this:
                self.logger.log("Patching: " + str(path), logtype="info")
                file_data = self.open_file(path)  # open the file to configure

                new_data = []  # new data to over-write
                config_added = []  # successfully configured parameters
                config_not_added = []  # not configured parameters

                sep = self.os_config_data[path]["sep"]  # separator

                for index, line in enumerate(file_data):
                    flag = 0  # write the original line
                    for rep_text in self.os_config_data[path]["config"].keys():
                        hold = False  # forward refrencing not needed
                        in_front = False  # not found in forward refrence

                        if rep_text in line:
                            if line.strip(" ").strip("\n").startswith(
                                    "#"):  # found comment
                                hold = True  # hold, prepare for forward refrence

                            if hold:  # if forward refrence is needed
                                for _, nf_line in enumerate(file_data,
                                                            start=index + 1):
                                    if (rep_text in nf_line
                                            and not nf_line.strip(" ").strip(
                                                "\n").startswith("#")):
                                        in_front = True  # found in forward refrencing

                            if not in_front:  # not in forward refrencing
                                self.logger.log("Old config line: " +
                                                line.strip("\n"),
                                                logtype="info")
                                new_config_line = rep_text + sep + \
                                                  self.os_config_data[path]["config"][rep_text]
                                new_data.append(new_config_line)
                                config_added.append(rep_text)
                                flag = 1  # write the new line
                                self.logger.log("New config line: " +
                                                new_config_line,
                                                logtype="info")

                    if flag == 0:  # write the original line
                        new_data.append(line.strip(" ").strip("\n"))
                    elif flag == 1:  # already written
                        flag = 0  # reset flag

                # Look which parameters were not over-written
                # as they were not found in the config file
                for rep_text in self.os_config_data[path]["config"].keys():
                    if rep_text not in config_added:
                        new_config_line = rep_text + sep + \
                                          self.os_config_data[path]["config"][rep_text]
                        config_not_added.append(new_config_line)

                # Extend the new configuration
                new_data.extend(config_not_added)
                # Write the data (overwrite) the config file
                self.write_data(path=path, data=new_data)
                self.logger.log("Patched: " + str(path), logtype="info")

                # Empty the list for the next configuration file
                new_data.clear()
                config_added.clear()
                config_not_added.clear()
Example #6
0
class Installer(object):
    """Installer Class."""
    def __init__(self, debug=False):
        """
        Initialize Installer.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # Command configuraton path
        self._COMMAND_PATH = "securetea/lib/auto_server_patcher/configs/commands.json"
        # Load configuraton data
        self.config_data = self.open_json(self._COMMAND_PATH)

        # Categorize OS
        self.os_name = utils.categorize_os()
        if self.os_name:
            try:
                self.os_config_data = self.config_data[self.os_name]
            except KeyError:
                self.logger.log("Could not load OS configuraton data.",
                                logtype="error")
        else:
            self.logger.log("Could not determine OS specific config.")

    @staticmethod
    def open_json(path):
        """
        Read from JSON file.

        Args:
            path (str): Path of the JSON file

        Raises:
            None

        Returns:
            None
        """
        with open(path, "r") as json_data_file:
            data = json.load(json_data_file)
            return data

    @staticmethod
    def excecute_command(command):
        """
        Execute command passed using the subprocess module.

        Args:
            command (str): Command to execute

        Returns:
            str: Output of the command executed
            str: Error(if any) of the command executed

        Raises:
            None
        """
        command = command.split(' ')
        process_respose = subprocess.Popen(command,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE)
        output, error = process_respose.communicate()

        if output:
            output = output.decode('utf-8')
        if error:
            error = error.decode('utf-8')

        return output, error

    def install(self):
        """
        Perform the configuraton commands.

        Args:
            None

        Raises:
            None

        Returns:
            None
        """
        for command in self.os_config_data["commands"]:
            self.logger.log("Executing command: " + command, logtype="info")
            output, error = self.excecute_command(command)

            if output:
                msg = "Ouput: " + str(output)
                self.logger.log(msg, logtype="info")

            if error:
                msg = "Error: " + str(output)
                self.logger.log(msg, logtype="info")
Example #7
0
class SSLScanner(object):
    """SSLScanner class."""
    def __init__(self, debug=False, url=None):
        """
        Initialize SSLScanner.

        Args:
            debug (bool): Log on terminal or not

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        # API URL
        self._API_URL = "https://api.ssllabs.com/api/v3/analyze/"

        self.analyze_payload = {
            'startNew': 'on',
            'publish': 'off',
            'all': 'done',
            'ignoreMismatch': 'on'
        }

        # URL / website to scan
        self.url = str(url)

    @staticmethod
    def request_api(url, payload):
        """
        Call the API url using the payload.

        Args:
            url (str): Parent API URL to call
            payload (dict): Payload

        Raises:
            None

        Returns:
            response (JSON): Response of the API
        """
        resp = requests.get(url, params=payload)
        return resp.json()

    def analyze(self):
        """
        Analyze / Scan the requested URL.

        Args:
            url (str): URL / website to scan

        Raises:
            None

        Returns:
            resp (JSON): Response of the API
        """
        self.analyze_payload.update({'host': self.url})
        # Start analyzing the website
        self.logger.log("Analyzing URL: " + self.url, logtype="info")
        resp = self.request_api(self._API_URL, self.analyze_payload)
        self.analyze_payload.pop('startNew')

        while resp['status'] != 'READY' and resp['status'] != 'ERROR':
            time.sleep(15)
            resp = self.request_api(self._API_URL, self.analyze_payload)

        return resp

    @staticmethod
    def vulnerability_parser(data):
        """
        Parse the returned JSON to extract
        vulnerability scan data.

        Args:
            data (JSON): Data file to parse

        Raises:
            None

        Returns:
            vuln_dict (dict): Parsed vulnerability dict
        """
        base_data = data['endpoints'][0]['details']

        # Parse the API data into a dict
        vuln_dict = {
            'beastAttack': base_data['vulnBeast'],
            'poodle': base_data['poodle'],
            'poodleTls': base_data['poodleTls'],
            'rc4': base_data['rc4Only'],
            'heartbeat': base_data['heartbeat'],
            'heartbleed': base_data['heartbleed'],
            'ticketbleed': base_data['ticketbleed'],
            'openSSL_CCS': base_data['openSslCcs'],
            'openSSL_padding': base_data['openSSLLuckyMinus20'],
            'robot': base_data['bleichenbacher'],
            'freak': base_data['freak'],
            'logjam': base_data['logjam'],
            'drown_attack': base_data['drownVulnerable'],
        }

        return vuln_dict

    @staticmethod
    def get_value(key, value):
        """
        Map numeric key to their text value.

        Args:
            key (str): Name of the vulnerability
            value (int): Parsed integer result

        Raises:
            None

        Returns:
            TYPE: str
        """
        main_dict = {
            'poodleTls': {
                '-3': 'timeout',
                '-2': 'TLS not supported',
                '-1': 'test failed',
                '0': 'unknown',
                '1': 'not vulnerable',
                '2': 'vulnerable'
            },
            'ticketbleed': {
                '-1': 'test failed',
                '0': 'unknown',
                '1': 'not vulnerable',
                '2': 'vulnerable and insecure'
            },
            'openSSL_CCS': {
                '-1': 'test failed',
                '0': 'unknown',
                '1': 'not vulnerable',
                '2': 'possibly vulnerable, but not exploitable',
                '3': 'vulnerable and exploitable'
            },
            'openSSL_padding': {
                '-1': 'test failed',
                '0': 'unknown',
                '1': 'not vulnerable',
                '2': 'vulnerable and insecure'
            },
            'robot': {
                '-1': 'test failed',
                '0': 'unknown',
                '1': 'not vulnerable',
                '2': 'vulnerable (weak oracle)',
                '3': 'vulnerable (strong oracle)',
                '4': 'inconsistent results'
            }
        }

        value = str(value)
        return main_dict[key][value]

    def log_data(self, dict_value):
        """
        Log the vulnerability scan report data.

        Args:
            dict_value (dict): Vulnerability scan result

        Raises:
            None

        Returns:
            None
        """
        self.logger.log("Vulnerability Scan Result: " + self.url,
                        logtype="info")

        for vuln, res in dict_value.items():
            if not isinstance(res, bool):
                res = self.get_value(vuln, res)
            self.logger.log(str(vuln) + ": " + str(res), logtype="info")

    def start_scan(self):
        """
        Start the SSL scanning process.

        Args:
            None

        Raises:
            None

        Returns:
            None
        """
        # Start analyzing
        resp = self.analyze()
        # Parse the result
        parsed_resp = self.vulnerability_parser(resp)
        # Log the parsed data
        self.log_data(parsed_resp)
Example #8
0
class SecureTeaAutoServerPatcher(object):
    """SecureTeaAutoServerPatcher Class."""
    def __init__(self, debug=False, cred=None):
        """
        Initialize SecureTeaAutoServerPatcher.

        Args:
            debug (bool): Log on terminal or not
            url (str): URL to scan for SSL vulnerabilites

        Raises:
            None

        Returns:
            None
        """
        # Initialize logger
        self.logger = PatchLogger(__name__, debug=debug)

        if not utils.check_root():
            self.logger.log("Please run as root, exiting.", logtype="error")
            sys.exit(0)

        if not cred:
            self.logger.log("No credentials specified.", logtype="error")
            sys.exit(0)

        # List of files to patch
        self.to_patch = list()

        # Parse arguments
        url = cred['url']
        if cred['apache']:
            apache = 1
        else:
            apache = 0
        if cred['ssh']:
            ssh = 1
        else:
            ssh = 0
        if cred['login']:
            login = 1
        else:
            login = 0
        if cred['sysctl']:
            sysctl = 1
        else:
            sysctl = 0

        # Determine which file to patch
        if apache == 1:
            self.to_patch.append("apache")
        if ssh == 1:
            self.to_patch.append("ssh")
        if login == 1:
            self.to_patch.append("login")
        if sysctl == 1:
            self.to_patch.append("sysctl")

        if url and url != "XXXX":  # if valid URL
            self.url = url
        else:
            self.url = None

        # Create Installer object
        self.installer = Installer(debug=debug)
        # Create Patcher object
        self.patcher = ConfigPatcher(debug=debug, to_patch=self.to_patch)
        if self.url:
            # Create SSLScanner object
            self.ssl_scanner = SSLScanner(debug=debug, url=self.url)

    def start(self):
        """
        Start SecureTea Auto Server Patcher.

        Args:
            None

        Raises:
            None

        Returns:
            None
        """
        # Start patching configuraton files
        self.patcher.patch()
        # Start executing configuraton commands
        self.installer.install()
        if self.url:  # if url is provided
            # Start SSL scanning
            self.ssl_scanner.start_scan()