Exemple #1
0
def retrieve_ssl_vulnerabilities_for_tcp_service(
        self,
        org_uuid=None,
        service_uuid=None,
        scan_uuid=None,
):
    """
    This performs various SSL vulnerability scans on the tcp service
    :param org_uuid: The UUID of the organization to collect information on behalf of.
    :param service_uuid: The UUID of the network service to retrieve the SSL certificate for.
    :param scan_uuid: The UUID of the network service scan that this SSL certificate retrieval is associated
    with.
    :return: None
    """
    ip_address, port, protocol = self.get_endpoint_information(service_uuid)
    ssl_vulnerabilities_record = SslVulnerabilitiesModel.from_database_model_uuid(
        uuid=scan_uuid,
        db_session=self.db_session
    )
    server_info = ServerConnectivityInfo(hostname=ip_address, ip_address=ip_address, port=port)
    try:
        server_info.test_connectivity_to_server()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        logger.error(
            "Error making an SSL connection to the server while looking for vulnerabilities, something went really wrong."
        )
    synchronous_scanner = SynchronousScanner()

    fallback_scsv_command = FallbackScsvScanCommand()
    fallback_scsv_result = synchronous_scanner.run_scan_command(server_info, fallback_scsv_command)
    ssl_vulnerabilities_record.supports_fallback_scsv = fallback_scsv_result.supports_fallback_scsv

    heartbleed_command = HeartbleedScanCommand()
    heartbleed_result = synchronous_scanner.run_scan_command(server_info, heartbleed_command)
    ssl_vulnerabilities_record.is_vulnerable_to_heartbleed = heartbleed_result.is_vulnerable_to_heartbleed

    openssl_css_injection_command = OpenSslCcsInjectionScanCommand()
    openssl_css_injection_result = synchronous_scanner.run_scan_command(server_info, openssl_css_injection_command)
    ssl_vulnerabilities_record.is_vulnerable_to_ccs_injection = openssl_css_injection_result.is_vulnerable_to_ccs_injection

    session_renegotion_command = SessionRenegotiationScanCommand()
    session_renegotion_result = synchronous_scanner.run_scan_command(server_info, session_renegotion_command)
    ssl_vulnerabilities_record.accepts_client_renegotiation = session_renegotion_result.accepts_client_renegotiation
    ssl_vulnerabilities_record.supports_secure_renegotiation = session_renegotion_result.supports_secure_renegotiation

    session_resumption_support_command = SessionResumptionSupportScanCommand()
    session_resumption_support_result = synchronous_scanner.run_scan_command(server_info, session_resumption_support_command)
    ssl_vulnerabilities_record.is_ticket_resumption_supported = session_resumption_support_result.is_ticket_resumption_supported

    ssl_vulnerabilities_record.save(org_uuid)
Exemple #2
0
    def ssl_scan(self):
        print("Running SSL Scan")
        try:
            server_info = ServerConnectivityInfo(
                hostname=self.options["TARGET"])
            server_info.test_connectivity_to_server()
            synchronous_scanner = SynchronousScanner()

            # TLS 1.0
            command = Tlsv10ScanCommand()
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)
            print("Available TLSv1.0 Ciphers:")
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

            # TLSv1.2
            command = Tlsv12ScanCommand()
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)
            print("Available TLSv1.2 Ciphers:")
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

            # Certificate information
            command = CertificateInfoScanCommand()
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)
            for entry in scan_result.as_text():
                print(entry)

            # Heartbleed vulnerability info
            command = HeartbleedScanCommand()
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)
            for entry in scan_result.as_text():
                print(entry)

            # HTTP Headers info
            command = HttpHeadersScanCommand()
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)
            for entry in scan_result.as_text():
                print(entry)

        except Exception as e:
            self.handle_exception(e, "Error running SSL scan")
            pass
Exemple #3
0
def test_ssl_service_for_ssl_vulnerability(
        self,
        org_uuid=None,
        network_service_uuid=None,
        network_service_scan_uuid=None,
        vulnerability_name=None,
):
    """
    Test the given network service for the specified SSL vulnerability.
    :param org_uuid: The UUID of the organization to enumerate SSL vulnerabilities on behalf of.
    :param network_service_uuid: The UUID of the network service that is being scanned.
    :param network_service_scan_uuid: The UUID of the network service scan that this enumeration is
    a part of.
    :param vulnerability_name: A string representing the vulnerability to test for.
    :return: None
    """
    logger.info(
        "Now testing network service %s for SSL vulnerability %s."
        % (network_service_uuid, vulnerability_name)
    )
    command_map = get_ssl_vulnerabilities_command_map()
    ValidationHelper.validate_in(to_check=vulnerability_name, contained_by=command_map.keys())
    command = command_map[vulnerability_name]["command"]
    ip_address, port, protocol = self.get_endpoint_information(network_service_uuid)
    scanner = SynchronousScanner()
    server_info = ServerConnectivityInfo(hostname=ip_address, ip_address=ip_address, port=port)
    try:
        server_info.test_connectivity_to_server()
    except ServerConnectivityError as e:
        logger.warning(
            "ServerConnectivityError thrown when attempting to test SSL at %s:%s for %s vulnerability: %s"
            % (ip_address, port, vulnerability_name, e.message)
        )
        return
    try:
        result = scanner.run_scan_command(server_info, command())
        vuln_model = SslVulnerabilityModel.from_database_model_uuid(
            uuid=network_service_scan_uuid,
            db_session=self.db_session,
            test_errored=False,
            vuln_test_name=vulnerability_name,
        )
        vuln_model.test_results = []
        for field in command_map[vulnerability_name]["fields"]:
            vuln_model.test_results.append({
                "key": field,
                "value": getattr(result, field),
            })
        vuln_model.save(org_uuid)
    except (socket.error, OpenSSLError):
        vuln_model = SslVulnerabilityModel.from_database_model_uuid(
            uuid=network_service_scan_uuid,
            db_session=self.db_session,
            test_errored=True,
        )
        vuln_model.save(org_uuid)
    logger.info(
        "Network service %s successfully tested for SSL vulnerability %s."
        % (network_service_uuid, vulnerability_name)
    )
Exemple #4
0
def server_scanner(p):
    try:
        logger = logging.getLogger(__name__)
        logger.info("Opening file")
        server_tester = ServerConnectivityTester(hostname='localhost', port=p)
        server_info = server_tester.perform()
        scanner = SynchronousScanner()

        results = dict()
        suite_names = [
            'SSLV2 Cipher Suites', 'SSLV3 Cipher Suites',
            'TLSV1_0 Cipher Suites', 'TLSV1_1 Cipher Suites',
            'TLSV1_2 Cipher Suites', 'TLSV1_3 Cipher Suites'
        ]
        suites = [
            Sslv20ScanCommand, Sslv30ScanCommand, Tlsv10ScanCommand,
            Tlsv11ScanCommand, Tlsv12ScanCommand, Tlsv13ScanCommand
        ]

        logger.info("Scanning SSL/TLS suites, writing to ciphers.json")
        for suite_name, suite in zip(suite_names, suites):
            scan = scanner.run_scan_command(server_info, suite())
            results[suite_name] = [
                cipher.name for cipher in scan.accepted_cipher_list
            ]

        with open(filename, 'w') as outfile:
            json.dump(results, outfile)

    except ServerConnectivityError as e:
        raise RuntimeError(
            f'Could not connect to {e.server_info.hostname}: {e.error_message}'
        )
Exemple #5
0
def enumerate_cipher_suites_for_ssl_service(
    self,
    org_uuid=None,
    network_service_uuid=None,
    network_service_scan_uuid=None,
    order_uuid=None,
):
    """
    Enumerate all of the cipher suites that the given SSL/TLS service supports.
    :param org_uuid: The UUID of the organization to enumerate cipher suites on behalf of.
    :param network_service_uuid: The UUID of the network service that is being scanned.
    :param network_service_scan_uuid: The UUID of the network service scan that this enumeration is
    a part of.
    :return: None
    """
    logger.info(
        "Now enumerating supported cipher suites for network service %s." %
        (network_service_uuid, ))
    ip_address = self.network_service.ip_address.address
    port = self.network_service.port
    server_info = ServerConnectivityInfo(hostname=ip_address,
                                         ip_address=ip_address,
                                         port=port)
    try:
        server_info.test_connectivity_to_server()
    except ServerConnectivityError as e:
        logger.warning(
            "ServerConnectivityError thrown when attempting to inspect SSL at %s:%s: %s"
            % (ip_address, port, e.message))
        return
    scanner = SynchronousScanner()
    bulk_query = BulkElasticsearchQuery()
    network_service_scan = self.network_service_scan
    for ssl_protocol, command in get_ssl_cipher_suite_commands():
        result = scanner.run_scan_command(server_info, command())
        ssl_support_record = SslSupportModel.from_database_model(
            network_service_scan,
            ssl_version=ssl_protocol,
            supported=len(result.accepted_cipher_list) > 0,
        )
        ssl_support_record.accepted_ciphers = [
            cipher.name for cipher in result.accepted_cipher_list
        ]
        ssl_support_record.rejected_ciphers = [
            cipher.name for cipher in result.rejected_cipher_list
        ]
        ssl_support_record.errored_ciphers = [
            cipher.name for cipher in result.errored_cipher_list
        ]
        ssl_support_record.preferred_cipher = result.preferred_cipher.name if result.preferred_cipher else None
        bulk_query.add_model_for_indexing(model=ssl_support_record,
                                          index=org_uuid)
    logger.info(
        "All cipher suite information converted to Elasticsearch data. Now updating via bulk query."
    )
    bulk_query.save()
    logger.info(
        "Bulk query completed. SSL cipher suites enumerated for network service %s."
        % (network_service_uuid, ))
Exemple #6
0
    def test_synchronous_scanner(self):
        server_test = ServerConnectivityTester(hostname='www.google.com')
        server_info = server_test.perform()

        sync_scanner = SynchronousScanner()
        plugin_result = sync_scanner.run_scan_command(server_info, CompressionScanCommand())
        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #7
0
    def test_synchronous_scanner(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        sync_scanner = SynchronousScanner()
        plugin_result = sync_scanner.run_scan_command(server_info, CompressionScanCommand())
        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #8
0
    def test_synchronous_scanner(self):
        server_test = ServerConnectivityTester(hostname='www.google.com')
        server_info = server_test.perform()

        sync_scanner = SynchronousScanner()
        plugin_result = sync_scanner.run_scan_command(server_info, CompressionScanCommand())
        assert plugin_result.as_text()
        assert plugin_result.as_xml()
Exemple #9
0
def ciphers_scanner(server_info: ServerConnectivityInfo) -> dict:
    scanner = SynchronousScanner()
    commands = COMMANDS
    ciphers_info: dict = {}
    for command in commands:
        scan_result = scanner.run_scan_command(server_info, command)
        ciphers: list = []
        for cipher in scan_result.accepted_cipher_list:
            ciphers.append(cipher.name)
        ciphers_info[command.get_cli_argument()] = ciphers
    return ciphers_info
Exemple #10
0
def scan_ssl(self, ssl_domain, scan_id, cipher_list):

    # Comparing ciphers --
    #used_ciphers = {cipher for cipher in sslyze.get("Accepted Ciphers").split(', ')}
    #bad_ciphers = list(used_ciphers - accepted_ciphers)
    #signature_algorithm = sslyze.get("Signature Algorithm", "sha1")
    #acceptable_ciphers = not bad_ciphers

    ssl_dict = {}
    ssl_dict["domain"] = ssl_domain["domain"]
    domain = ssl_domain["domain"]
    self.update_state(state="SCANNING")
    print(f'Scanning {domain} for ssl info...')
    #_install_req = subprocess.check_output("")

    try:
        server_tester = ServerConnectivityTester(
            hostname=ssl_domain["domain"],
            port=443,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.PLAIN_TLS)
        print(
            f'\nTesting connectivity with {server_tester.hostname}:{server_tester.port}...'
        )
        server_info = server_tester.perform()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        raise RuntimeError(
            f'Could not connect to {e.server_info.hostname}: {e.error_message}'
        )

    _cmd = Tlsv10ScanCommand()

    synchronous_scanner = SynchronousScanner()

    scan_result = synchronous_scanner.run_scan_command(server_info, _cmd)

    i = 0
    used_ciphers = []
    for cipher in scan_result.accepted_cipher_list:
        print(f'    {cipher.name}')
        used_ciphers.append(cipher.name)
        i = i + 1

    print(f'Finished scanning {domain} for ssl info')

    bad_ciphers = []
    for cipher in used_ciphers:
        if cipher not in cipher_list:
            bad_ciphers.append(cipher)

    ssl_dict['accepted_ciphers'] = used_ciphers
    ssl_dict['bad_ciphers'] = bad_ciphers

    return ssl_dict, "ssl", scan_id
Exemple #11
0
class WorkerProcess(Process):
    """The main process class responsible for instantiating and running the plugins.
    """

    def __init__(
        self,
        priority_queue_in: JoinableQueue,
        queue_in: JoinableQueue,
        queue_out: JoinableQueue,
        network_retries: int,
        network_timeout: int,
    ) -> None:
        Process.__init__(self)
        self.priority_queue_in = priority_queue_in
        self.queue_in = queue_in
        self.queue_out = queue_out

        # The object that will actually run the scan commands
        self._synchronous_scanner = SynchronousScanner(network_retries, network_timeout)

    def run(self) -> None:
        """The process will first complete tasks it gets from self.queue_in.
        Once it gets notified that all the tasks have been completed, it terminates.
        """
        from sslyze.concurrent_scanner import PluginRaisedExceptionScanResult

        # Start processing task in the priority queue first
        current_queue_in = self.priority_queue_in
        while True:

            task = current_queue_in.get()  # Grab a task from queue_in
            if task is None:  # All tasks have been completed
                current_queue_in.task_done()

                if current_queue_in == self.priority_queue_in:
                    # All high priority tasks have been completed; switch to low priority tasks
                    current_queue_in = self.queue_in
                    continue
                else:
                    # All the tasks have been completed; pass on the sentinel to result_queue and exit
                    self.queue_out.put(None)
                    break

            server_info, scan_command = task
            try:
                result = self._synchronous_scanner.run_scan_command(server_info, scan_command)
            except Exception as e:
                # raise
                result = PluginRaisedExceptionScanResult(server_info, scan_command, e)

            # Send the result to queue_out
            self.queue_out.put(result)
            current_queue_in.task_done()
Exemple #12
0
    def executePerDomainAction(self, domain):
        """
        This plugin's per-domain action is to run a barrage of sslyze certificate tests

        Params:
            domain (str)
        """
        from sslyze.plugins.certificate_info_plugin import CertificateInfoScanCommand
        from sslyze.plugins.compression_plugin import CompressionScanCommand
        from sslyze.plugins.session_renegotiation_plugin import SessionRenegotiationScanCommand
        from sslyze.server_connectivity_tester import ServerConnectivityTester
        from sslyze.server_connectivity_tester import ServerConnectivityError
        from sslyze.synchronous_scanner import SynchronousScanner
        try:
            sslyze_conn_test = ServerConnectivityTester(hostname=domain)
            sslyze_server_info = sslyze_conn_test.perform()
            sslyze_scanner = SynchronousScanner()
            sslyze_results = sslyze_scanner.run_scan_command(
                sslyze_server_info, CertificateInfoScanCommand())
            sslyze_result_lines = sslyze_results.as_text()
            for line in sslyze_result_lines:
                self.logger.info(line)
            sslyze_results = sslyze_scanner.run_scan_command(
                sslyze_server_info, SessionRenegotiationScanCommand())
            sslyze_result_lines = sslyze_results.as_text()
            for line in sslyze_result_lines:
                self.logger.info(line)
            sslyze_results = sslyze_scanner.run_scan_command(
                sslyze_server_info, CompressionScanCommand())
            sslyze_result_lines = sslyze_results.as_text()
            for line in sslyze_result_lines:
                self.logger.info(line)
        except ServerConnectivityError as e:
            # Could not establish a TLS/SSL connection to the server
            self.logger.error(
                f'sslyze ended early, could not connect to {e.server_info.hostname}: {e.error_message}'
            )
            print(
                f'sslyze ended early, could not connect to {e.server_info.hostname}: {e.error_message}'
            )
Exemple #13
0
class WorkerProcess(Process):
    """The main process class responsible for instantiating and running the plugins.
    """

    def __init__(
            self,
            priority_queue_in: JoinableQueue,
            queue_in: JoinableQueue, queue_out: JoinableQueue,
            network_retries: int,
            network_timeout: int
    ) -> None:
        Process.__init__(self)
        self.priority_queue_in = priority_queue_in
        self.queue_in = queue_in
        self.queue_out = queue_out

        # The object that will actually run the scan commands
        self._synchronous_scanner = SynchronousScanner(network_retries, network_timeout)

    def run(self) -> None:
        """The process will first complete tasks it gets from self.queue_in.
        Once it gets notified that all the tasks have been completed, it terminates.
        """
        from sslyze.concurrent_scanner import PluginRaisedExceptionScanResult

        # Start processing task in the priority queue first
        current_queue_in = self.priority_queue_in
        while True:

            task = current_queue_in.get()  # Grab a task from queue_in
            if task is None:  # All tasks have been completed
                current_queue_in.task_done()

                if current_queue_in == self.priority_queue_in:
                    # All high priority tasks have been completed; switch to low priority tasks
                    current_queue_in = self.queue_in
                    continue
                else:
                    # All the tasks have been completed; pass on the sentinel to result_queue and exit
                    self.queue_out.put(None)
                    break

            server_info, scan_command = task
            try:
                result = self._synchronous_scanner.run_scan_command(server_info, scan_command)
            except Exception as e:
                # raise
                result = PluginRaisedExceptionScanResult(server_info, scan_command, e)

            # Send the result to queue_out
            self.queue_out.put(result)
            current_queue_in.task_done()
Exemple #14
0
def check_tcp_service_for_ssl_protocol_cipher_support(
        self,
        org_uuid=None,
        service_uuid=None,
        ssl_protocol=None,
        scan_uuid=None
):
    """
    Check to see what ciphers this TCP service supports for the given SSL protocol.
    :param org_uuid: The UUID of the organization to collect information on behalf of.
    :param service_uuid: The UUID of the network service to check for SSL support.
    :param ssl_protocol: The SSL protocol to check
    :param scan_uuid: The UUID of the network service scan that this SSL version check is associated
    with.
    :return: None
    """
    ip_address, port, protocol = self.get_endpoint_information(service_uuid)
    server_info = ServerConnectivityInfo(hostname=ip_address, ip_address=ip_address, port=port)
    synchronous_scanner = SynchronousScanner()
    command = None
    if ssl_protocol == 'PROTOCOL_TLSv1' or ssl_protocol == 'PROTOCOL_TLS':
        command = Tlsv10ScanCommand()
    elif ssl_protocol == 'PROTOCOL_TLSv1_1':
        command = Tlsv11ScanCommand()
    elif ssl_protocol == 'PROTOCOL_TLSv1_2':
        command = Tlsv12ScanCommand()
    elif ssl_protocol == 'PROTOCOL_SSLv23':
        command = Sslv20ScanCommand()
    elif ssl_protocol == 'PROTOCOL_SSLv3':
        command = Sslv30ScanCommand()
    else:
        raise ValueError(
            "Not sure how to run scan command for SSL protocol of %s."
            % (ssl_protocol,)
        )
    cipher_scan_result = synchronous_scanner.run_scan_command(server_info, command)
    ssl_support_record = SslSupportModel.from_database_model_uuid(
        uuid=scan_uuid,
        db_session=self.db_session,
        ssl_version=ssl_protocol,
        supported=True,
    )
    ssl_support_record.accepted_ciphers = [ cipher.name for cipher in cipher_scan_result.accepted_cipher_list ]
    ssl_support_record.rejected_ciphers = [ cipher.name for cipher in cipher_scan_result.rejected_cipher_list ]
    ssl_support_record.errored_ciphers = [ cipher.name for cipher in cipher_scan_result.errored_cipher_list ]
    if cipher_scan_result.preferred_cipher is not None:
        ssl_support_record.preferred_cipher = cipher_scan_result.preferred_cipher.name
    else:
        ssl_support_record.preferred_cipher = None
    ssl_support_record.save(org_uuid)
def get_cert(site_json):

    hostname = get_hostname(site_json['hostname'])

    try:
        server_info = ServerConnectivityInfo(hostname=hostname)
        server_info.test_connectivity_to_server()
        command = CertificateInfoScanCommand()
        synchronous_scanner = SynchronousScanner()
        scan_result = synchronous_scanner.run_scan_command(server_info, command)
    except ServerConnectivityError:
        return None

    return scan_result
Exemple #16
0
def get_cert_alt_names(host, port=443):
    try:
        server_tester = ServerConnectivityTester(hostname=host, port=port,
                            tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS)
        server_info = server_tester.perform()
    except ServerConnectivityError:
        print("Impossible to connect")
        sys.exit(1)

    command = CertificateInfoScanCommand()
    synchronous_scanner = SynchronousScanner()
    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    cert = scan_result.verified_certificate_chain[0]
    subj_alt_names = []
    san_ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
    subj_alt_names = san_ext.value.get_values_for_type(DNSName)
    return subj_alt_names
Exemple #17
0
def scan(name, ip, view, suite):
    """ Two inputs: web site name and ip
    TODO: extend this to include port """

    try:
        server_tester = ServerConnectivityTester(
            hostname=name,
            ip_address=ip,
            port=443,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS)
        # This line checks to see if the host is online
        server_info = server_tester.perform()
        ip = server_info.ip_address
    # Could not establish an SSL connection to the server
    except ConnectionToServerTimedOut:
        error = results.set_error(f'{name}', "Connection to TCP 443 timed-out")
        return error
    except ServerConnectivityError:
        error = results.set_error(f'{name}', "Unknown Error")
        return error

    # Create a new results dictionary
    scan_output = results.new()

    # I hash the combination of hostname and ip for tracking
    key = md5((f'{name}' + ip).encode("utf-8")).hexdigest()
    results.set_result(scan_output, "MD5", key)
    results.set_result(scan_output, "Hostname", f'{name}')
    results.set_result(scan_output, "IP", ip)
    results.set_result(scan_output, "View", view)

    for suite in ciphersuites.get(suite):
        synchronous_scanner = SynchronousScanner()
        scan_result = synchronous_scanner.run_scan_command(server_info, suite)

        for cipher in scan_result.accepted_cipher_list:
            results.set_ciphers(scan_output, {
                "Version": cipher.ssl_version.name,
                "Cipher": cipher.name
            })

    if len(scan_output["Results"]) == 0:
        results.set_result(scan_output, "Results", "No Policy Violations")

    return scan_output
Exemple #18
0
def scan(name, ip, port, view, suite):
    """ Five inputs: web site name, ip, port
    split-dns view, and cipher suite """

    try:
        server_tester = ServerConnectivityTester(
            hostname=name,
            ip_address=ip,
            port=port,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS)
        # This line checks to see if the host is online
        server_info = server_tester.perform()
        ip = server_info.ip_address
    # Could not establish an SSL connection to the server
    except ConnectionToServerTimedOut:
        raise ConnectionError('Connection Timeout',
                              ERROR_MSG_CONNECTION_TIMEOUT(name, port))
    except ServerConnectivityError:
        raise ConnectionError('Unknow Connection Error',
                              ERROR_MSG_UNKNOWN_CONNECTION(name, port))

    # Create a new results dictionary
    scan_output = results.new()

    # I hash the combination of hostname and ip for tracking
    key = md5((f'{name}' + ip).encode("utf-8")).hexdigest()
    results.set_result(scan_output, "MD5", key)
    results.set_result(scan_output, "Hostname", f'{name}')
    results.set_result(scan_output, "IP", ip)
    results.set_result(scan_output, "View", view)

    for suite in ciphersuites.get(suite):
        synchronous_scanner = SynchronousScanner()
        scan_result = synchronous_scanner.run_scan_command(server_info, suite)

        for cipher in scan_result.accepted_cipher_list:
            results.set_ciphers(scan_output, {
                "Version": cipher.ssl_version.name,
                "Cipher": cipher.name
            })

    if len(scan_output["Results"]) == 0:
        results.set_result(scan_output, "Results", "No Policy Violations")

    return scan_output
Exemple #19
0
def search_subject_alt_name(self, target):
  print("Searching for Subject Alt Names")
  try:
    server_info = ServerConnectivityInfo(hostname=target)
    server_info.test_connectivity_to_server()
    synchronous_scanner = SynchronousScanner()

    # Certificate information
    command = CertificateInfoScanCommand()
    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    # Direct object reference is pretty bad, but then again so is the crypto.x509 object implementation, so...
    extensions = scan_result.certificate_chain[0].extensions[6]
    for entry in extensions.value:
      if entry.value.strip() not in self.domains:
        self.domains.append(entry.value.strip())

  except Exception as e:
    self.handle_exception(e)
Exemple #20
0
def concurrent_scan(site_info):
    # Setup the server to scan and ensure it is online/reachable
    server_info = server_connectivity_tester(site_info.name)

    if server_info:
        synchronous_scanner = SynchronousScanner()

        cert_info_plugin = CertificateInfoPlugin()
        plugin_result = cert_info_plugin.process_task(
            server_info, CertificateInfoScanCommand())
        #not plugin_result.verified_certificate_chain or
        #not plugin_result.leaf_certificate_subject_matches_hostname some sites' certs CN is with "www." so the result here is false
        if plugin_result.verified_certificate_chain and site_info.name not in str(
                plugin_result.verified_certificate_chain[0].subject):
            site_info.cert_trusted = "False"
            print("not trusted: " + site_info.name)
            print(plugin_result.__dict__)
        # elif not plugin_result.verified_certificate_chain:
        #     site_info.cert_trusted = "False"
        #     print("not trusted: " + site_info.name)
        #     print(plugin_result.__dict__)
        else:
            site_info.cert_trusted = "True"
            scan_result1 = synchronous_scanner.run_scan_command(
                server_info, Sslv20ScanCommand())
            if len(scan_result1.accepted_cipher_list) > 0:
                site_info.sslv2 = "True"
            scan_result2 = synchronous_scanner.run_scan_command(
                server_info, Sslv30ScanCommand())
            if len(scan_result2.accepted_cipher_list) > 0:
                site_info.sslv3 = "True"
            scan_result3 = synchronous_scanner.run_scan_command(
                server_info, Tlsv10ScanCommand())
            if len(scan_result3.accepted_cipher_list) > 0:
                site_info.tlsv1 = "True"
            scan_result4 = synchronous_scanner.run_scan_command(
                server_info, Tlsv11ScanCommand())
            if len(scan_result4.accepted_cipher_list) > 0:
                site_info.tlsv11 = "True"
            scan_result5 = synchronous_scanner.run_scan_command(
                server_info, Tlsv12ScanCommand())
            if len(scan_result5.accepted_cipher_list) > 0:
                site_info.tlsv12 = "True"
            scan_result6 = synchronous_scanner.run_scan_command(
                server_info, Tlsv13ScanCommand())
            if len(scan_result6.accepted_cipher_list) > 0:
                site_info.tlsv13 = "True"

            recheck_cert(site_info)
Exemple #21
0
def demo_synchronous_scanner():
    # Run one scan command to list the server's TLS 1.0 cipher suites
    try:
        server_tester = ServerConnectivityTester(
            hostname='smtp.gmail.com',
            port=587,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP
        )
        print(f'\nTesting connectivity with {server_tester.hostname}:{server_tester.port}...')
        server_info = server_tester.perform()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        raise RuntimeError(f'Could not connect to {e.server_info.hostname}: {e.error_message}')

    command = Tlsv10ScanCommand()

    synchronous_scanner = SynchronousScanner()

    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    for cipher in scan_result.accepted_cipher_list:
        print(f'    {cipher.name}')
Exemple #22
0
    def test_ssl_server_headers(self, hostname, port=443):
        '''
                Uses the ServerConnectivityTester to identify host headers specific to TLS/SSL implementations to identify
                apparent security flaws with SSL/TLS implementations at the web server level.

                Currently, we can enumerate HSTS and Expect-CT Headers. HPKP is available, but is not being included because
                its being deprecated by Chrome.

                | test ssl server headers  | hostname  | port (optional |

        '''
        try:
            tester = ServerConnectivityTester(hostname=hostname, port=port)
            server_info = tester.perform()

            scanner = SynchronousScanner()
            result = scanner.run_scan_command(server_info,
                                              HttpHeadersScanCommand())
            logger.info("Test for HSTS Header")

            if result.hsts_header:
                preload = result.hsts_header.preload
                include_subdomains = result.hsts_header.include_subdomains
                max_age = result.hsts_header.max_age
                logger.info(
                    "\tHSTS Header with Preload: {}, Include Subdomains: {} and max_age: {}"
                    .format(preload, include_subdomains, max_age))
            else:
                logger.warn("\tNo HSTS Header found")

            logger.info("Test for Expect-CT Header")
            if result.expect_ct_header:
                logger.info("\tExpect-CT Header found: {}".format(
                    result.expect_ct_header))
            else:
                logger.warn("\tNo Expect-CT Header found.")

        except ServerConnectivityError as e:
            logger.error('Error when trying to connect to {}: {}'.format(
                e.server_info.hostname, e.error_message))
Exemple #23
0
def get_cert(site_json):

    hostname = get_hostname(site_json['hostname'])

    try:
        server_tester = ServerConnectivityTester(hostname=hostname)
        server_info = server_tester.perform()
        command = CertificateInfoScanCommand()
        synchronous_scanner = SynchronousScanner()
        scan_result = synchronous_scanner.run_scan_command(
            server_info, command)
    except sslyze.server_connectivity_tester.ServerNotReachableError:
        return None
    except sslyze.server_connectivity_tester.ServerConnectivityError:  # plugin has very little documentation, keeping this here for now
        return None
    except OSError:
        return None
    except RuntimeError:
        return None
    except ValueError:
        return None

    return scan_result
def handler(event, context):
    """Handler for all Lambda events

    The event parameter is a dictionary containing the following keys
    and value types:
    * hostname - A string containing the hostname to be scanned.  For
      example, "dhs.gov".
    * port - An integer specifying the port to be scanned.  If omitted
      then the default value of 443 is used.
    * timeout - An integer denoting the number of seconds to wait when
      creating a connection.  If omitted then the default value of 5
      is used.
    * starttls_smtp - A boolean value denoting whether to try to use
      STARTTLS after connecting to an SMTP server.  This option should
      be True is connecting to an SMTP host and otherwise False.  If
      omitted then the default value of False is used.
    * scan_tlsv10 - A boolean value denoting whether to scan for TLS
      version 1.0 ciphers. If omitted then the default value of False
      is used.
    * scan_tlsv11 - A boolean value denoting whether to scan for TLS
      version 1.1 ciphers. If omitted then the default value of False
      is used.
    * scan_tlsv12 - A boolean value denoting whether to scan for TLS
      version 1.2 ciphers. If omitted then the default value of False
      is used.
    * scan_tlsv13 - A boolean value denoting whether to scan for TLS
      version 1.3 ciphers. If omitted then the default value of False
      is used.
    * scan_sslv20 - A boolean value denoting whether to scan for SSL
      version 2.0 ciphers. If omitted then the default value of False
      is used.
    * scan_sslv30 - A boolean value denoting whether to scan for SSL
      version 3.0 ciphers. If omitted then the default value of False
      is used.
    * scan_cert_info - A boolean value denoting whether to return
      certificate information. If omitted then the default value of
      False is used.

    Parameters
    ----------
    event : dict
        A dictionary containing the scan parameters, as described
        above.

    context : LambdaContext
        The context for the Lambda function.  See the corresponding
        AWS documentation for more details:
        https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    -------
    OrderedDict
        An OrderedDict specifying the fields of the
        trustymail.domain.Domain object resulting from the scan
        activity.
    """
    logging.info('AWS Event was: {}'.format(event))

    # Extract some variables from the event dictionary
    hostname = event['hostname']
    port = event.get('port', 443)
    timeout = event.get('timeout', 5)
    starttls_smtp = event.get('starttls_smtp', False)
    scan_tlsv10 = event.get('scan_tlsv10', False)
    scan_tlsv11 = event.get('scan_tlsv11', False)
    scan_tlsv12 = event.get('scan_tlsv12', False)
    scan_tlsv13 = event.get('scan_tlsv13', False)
    scan_sslv20 = event.get('scan_sslv20', False)
    scan_sslv30 = event.get('scan_sslv30', False)
    scan_cert_info = event.get('scan_cert_info', False)

    # Initialize sslyze
    protocol = TlsWrappedProtocolEnum.PLAIN_TLS
    if starttls_smtp:
        protocol = TlsWrappedProtocolEnum.STARTTLS_SMTP

    try:
        server_tester = ServerConnectivityTester(hostname=hostname,
                                                 port=port,
                                                 tls_wrapped_protocol=protocol)
        server_info = server_tester.perform(network_timeout=timeout)
    except ServerConnectivityError:
        logging.error('Unable to connect to {}:{}'.format(hostname, port),
                      exc_info=True, stack_info=True)
        return None
    except Exception:
        logging.error('Unable to connect to {}:{}'.format(hostname, port),
                      exc_info=True, stack_info=True)
        return None

    scanner = SynchronousScanner(network_timeout=timeout)

    # Perform the scans
    if scan_tlsv10:
        logging.debug('Performing TLSv1.0 scan')
        tlsv10 = scanner.run_scan_command(server_info, Tlsv10ScanCommand())
        logging.debug('tlsv10 = {}'.format(tlsv10))
    if scan_tlsv11:
        logging.debug('Performing TLSv1.1 scan')
        tlsv11 = scanner.run_scan_command(server_info, Tlsv11ScanCommand())
        logging.debug('tlsv11 = {}'.format(tlsv11))
    if scan_tlsv12:
        logging.debug('Performing TLSv1.2 scan')
        tlsv12 = scanner.run_scan_command(server_info, Tlsv12ScanCommand())
        logging.debug('tlsv12 = {}'.format(tlsv12))
    if scan_tlsv13:
        logging.debug('Performing TLSv1.3 scan')
        tlsv13 = scanner.run_scan_command(server_info, Tlsv13ScanCommand())
        logging.debug('tlsv13 = {}'.format(tlsv13))
    if scan_sslv20:
        logging.debug('Performing SSLv2 scan')
        sslv20 = scanner.run_scan_command(server_info, Sslv20ScanCommand())
        logging.debug('sslv20 = {}'.format(sslv20))
    if scan_sslv30:
        logging.debug('Performing SSLv3 scan')
        sslv30 = scanner.run_scan_command(server_info, Sslv30ScanCommand())
        logging.debug('sslv30 = {}'.format(sslv30))
    if scan_cert_info:
        logging.debug('Performing certificate scan')
        cert_info = scanner.run_scan_command(server_info,
                                             CertificateInfoScanCommand())
        logging.debug('cert_info = {}'.format(cert_info))

    return None
    def run_tests(self, tests):
        """
        Run all enabled tests against the target url with the http or https port specified in the connect function.
        The Test objects within the tests array describes each test. The array should be changed according to the
        outcome, and if both ports have been specified and are supported the results must be merged before returned.

        :param tests: Array of Test objects
        :type tests: Array[Test...]
        :param targetURL: The target including address and port
        :type targetURL: str
        :return: Array of test objects
        :rtype: Array[Test...]
        """

        # Perform tests

        synchronous_scanner = SynchronousScanner()

        # Run all plugins and do individual checks to see if the tests passed
        for i, plugin in enumerate(PluginsRepository._PLUGIN_CLASSES):
            if tests[i].enabled is True:
                plugin_passed = True

                # Individual check for cipher suites
                if plugin is OpenSslCipherSuitesPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_open_ssl_cihper_suites_secure(
                                command, scan_result):
                            # If one command fails the plugin fails as a whole
                            plugin_passed = False
                            break

                # Individual check for certificates
                elif plugin is CertificateInfoPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_certificate_info_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for compression
                elif plugin is CompressionPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_compression_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for fallback scsv
                elif plugin is FallbackScsvPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_fallback_scsv_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for heartbleed
                elif plugin is HeartbleedPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_heartbleed_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for secure http headers
                elif plugin is HttpHeadersPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_http_headers_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for openssl css injection
                elif plugin is OpenSslCcsInjectionPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_openssl_css_injection_secure(
                                scan_result):
                            plugin_passed = False
                            break

                # Individual check for session renegotiation
                elif plugin is SessionRenegotiationPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_session_renegotiation_secure(
                                scan_result):
                            plugin_passed = False
                            break

                # Individual check for session resumption
                elif plugin is SessionResumptionPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_session_resumption_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for the robot attack
                elif plugin is RobotPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_robot_secure(scan_result):
                            plugin_passed = False
                            break

                # Individual check for early data support
                elif plugin is EarlyDataPlugin:
                    for command in plugin.get_available_commands():
                        scan_result = synchronous_scanner.run_scan_command(
                            self.server_info, command())
                        if not self.is_early_data_secure(scan_result):
                            plugin_passed = False
                            break

            else:
                plugin_passed = None
            tests[i].passed = plugin_passed

        return tests
Exemple #26
0
def attack_host(results, host_name):
    print("Running forward_secrecy_attack.py host name: {}".format(host_name))
    expected_tls1_2_suites = ['TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256']

    try:
        server_tester = ServerConnectivityTester(
            hostname=host_name,
            port=443,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS)

        server_info = server_tester.perform()
    except ServerConnectivityError as e:
        results.append(AttackResult('10.13', host_name, False, e))
        return

    synchronous_scanner = SynchronousScanner()

    tls1_3_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv13ScanCommand())
    tls1_3_accepted_ciphers = tls1_3_scan_result.accepted_cipher_list

    tls1_2_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv12ScanCommand())
    tls1_2_accepted_ciphers = tls1_2_scan_result.accepted_cipher_list
    tls1_2_accepted_cipher_names = cipher_list_to_string_list(tls1_2_accepted_ciphers)

    tls1_1_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv11ScanCommand())
    tls1_1_accepted_ciphers = tls1_1_scan_result.accepted_cipher_list

    tls1_0_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv10ScanCommand())
    tls1_0_accepted_ciphers = tls1_0_scan_result.accepted_cipher_list

    ssl3_0_scan_result = synchronous_scanner.run_scan_command(server_info, Sslv30ScanCommand())
    ssl3_0_accepted_ciphers = ssl3_0_scan_result.accepted_cipher_list

    ssl2_0_scan_result = synchronous_scanner.run_scan_command(server_info, Sslv20ScanCommand())
    ssl2_0_accepted_ciphers = ssl2_0_scan_result.accepted_cipher_list

    # Determine result
    result = list_equal(tls1_2_accepted_cipher_names, expected_tls1_2_suites) and \
        len(tls1_3_accepted_ciphers) == 0 and \
        len(tls1_1_accepted_ciphers) == 0 and \
        len(tls1_0_accepted_ciphers) == 0 and \
        len(ssl3_0_accepted_ciphers) == 0 and \
        len(ssl3_0_accepted_ciphers) == 0

    # Build details
    details = ''
    if len(tls1_3_accepted_ciphers) != 0:
        details += 'TLS 1.3 should not be supported '

    if not list_equal(tls1_2_accepted_cipher_names, expected_tls1_2_suites):
        details += ' TLS 1.2 supporting ' + str(tls1_2_accepted_cipher_names) \
                   + ', should only be ' + str(expected_tls1_2_suites) + ' '

    if len(tls1_1_accepted_ciphers) != 0:
        details += 'TLS 1.1 should not be supported '

    if len(tls1_0_accepted_ciphers) != 0:
        details += 'TLS 1.0 should not be supported '

    if len(ssl3_0_accepted_ciphers) != 0:
        details += 'SSL 3.0 should not be supported '

    if len(ssl2_0_accepted_ciphers) != 0:
        details += 'SSL 2.0 should not be supported '

    if details == '':
        details = 'Cipher suites are correct'

    results.append(AttackResult('forward_secrecy', host_name, result, details))


# attack_host([], 'google.co.uk')
Exemple #27
0
def ssltlsscan(web):

    target = web.split('//')[1]
    print(R + '\n    ===============================')
    print(R + '     S S L   E N U M E R A T I O N')
    print(R + '    ===============================\n')
    print(GR + ' [*] Testing server SSL status...')
    try:
        req = requests.get('https://' + target)
        print(G + ' [+] SSL Working Properly...')
        time.sleep(0.6)
        print(O + " [!] Running SSL Enumeration...\n")
        try:
            server_tester = ServerConnectivityTester(hostname=target)
            server_info = server_tester.perform()
            scanner = SynchronousScanner()

            command = Tlsv10ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.0 Ciphers:")
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')

            command = Tlsv11ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.1 Ciphers:")
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')

            command = Tlsv12ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.2 Ciphers:")
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')

            command = CertificateInfoScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + ' [+] Certificate Information:')
            for entry in scan_result.as_text():
                if entry != '':
                    if 'certificate information' in entry.lower():
                        pass
                    elif ':' in entry:
                        print(GR + '    [+] ' +
                              entry.strip().split(':', 1)[0].strip() + ' : ' +
                              C + entry.strip().split(':', 1)[1].strip())
                    else:
                        print(O + '\n  [+] ' + entry.strip())
            print('')

            command = HttpHeadersScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + ' [+] HTTP Results:')
            for entry in scan_result.as_text():
                if 'http security' not in entry.strip().lower(
                ) and entry != '':
                    if '-' in entry:
                        print(GR + '    [+] ' +
                              entry.split('-', 1)[0].strip() + ' - ' + C +
                              entry.split('-', 1)[1].strip())
                    elif ':' in entry:
                        print(GR + '    [+] ' +
                              entry.strip().split(':', 1)[0].strip() + ' : ' +
                              C + entry.strip().split(':', 1)[1].strip())
                    else:
                        print(O + '\n  [+] ' + entry.strip())
            print('')

        except Exception as e:
            print(R + ' [-] Unhandled SSL Runtime Exception : ' + str(e))
            pass

    except requests.exceptions.SSLError as e:
        print(R + ' [-] Distant Server SSL not working : ' + str(e))

    print(G + ' [+] SSlScan Module Completed!')
Exemple #28
0
def main():

    if len(sys.argv) < 2:
        print("Error: please provide a domain")
        exit(-1)

    hostname = sys.argv[1]
    """
    Testing connectivity to the server
    """
    try:
        server_info = ServerConnectivityInfo(hostname)
        server_info.test_connectivity_to_server()
        print(
            "[*] Connection established. \n[.] Starting tests on {} \n".format(
                hostname))
    except ServerConnectivityError as e:
        raise RuntimeError("Error when connecting to {}: {}".format(
            hostname, e.error_msg))

    scanner = SynchronousScanner()
    """
    Creating an output file
    """
    output = open("/root/PycharmProjects/SSL-TLS-Tool/output/" + hostname, "w")
    output.write("##############################################\n")
    output.write("Output result for host: {}\n".format(hostname))
    output.write("Start {}\n".format(datetime.datetime.now()))
    output.write("##############################################\n\n")
    """
    Certificate:
    """
    scan_result = scanner.run_scan_command(server_info,
                                           CertificateInfoScanCommand())
    for e in scan_result.as_text():
        output.write(e + "\n")
    """
    Protocols and Ciphers Suits:
    """
    run_command(scanner, server_info, Tlsv10ScanCommand(), output)
    run_command(scanner, server_info, Tlsv11ScanCommand(), output)
    run_command(scanner, server_info, Tlsv12ScanCommand(), output)
    run_command(scanner, server_info, Sslv20ScanCommand(), output)
    run_command(scanner, server_info, Sslv30ScanCommand(), output)
    """
    Testing vulnerabilities:
    """
    run_command(scanner, server_info, DrownScanCommand(), output)
    run_command(scanner, server_info, PoodleSslScanCommand(), output)
    run_command(scanner, server_info, HeartbleedScanCommand(), output)
    run_command(scanner, server_info, OpenSslCcsInjectionScanCommand(), output)
    run_command(scanner, server_info, CompressionScanCommand(), output)
    run_command(scanner, server_info, FallbackScsvScanCommand(), output)
    run_command(scanner, server_info, SessionRenegotiationScanCommand(),
                output)
    run_command(scanner, server_info, SessionResumptionSupportScanCommand(),
                output)
    run_command(scanner, server_info, SessionResumptionRateScanCommand(),
                output)
    """
    Closing
    """
    output.close()
    print("\n[*] Check output file for more details")
    print("[*] Test completed!")
Exemple #29
0
class TLSProfiler:
    PROFILES_URL = "https://ssl-config.mozilla.org/guidelines/5.4.json"
    PROFILES = None
    SCT_REQUIRED_DATE = datetime(
        year=2018, month=4, day=1
    )  # SCTs are required after this date, see https://groups.google.com/a/chromium.org/forum/#!msg/ct-policy/sz_3W_xKBNY/6jq2ghJXBAAJ

    SCAN_COMMANDS = {
        "SSLv2": Sslv20ScanCommand,
        "SSLv3": Sslv30ScanCommand,
        "TLSv1": Tlsv10ScanCommand,
        "TLSv1.1": Tlsv11ScanCommand,
        "TLSv1.2": Tlsv12ScanCommand,
        "TLSv1.3": Tlsv13ScanCommand,
    }

    def __init__(
        self,
        domain: str,
        target_profile: str,
        ca_file: Optional[str] = None,
        cert_expire_warning: int = 15,
    ) -> None:
        """
        :param domain:
        :param target_profile: One of [old|intermediate|modern]
        :param ca_file: Path to a trusted custom root certificates in PEM format.
        :param cert_expire_warning: A warning is issued if the certificate expires in less days than specified.
        """
        self.ca_file = ca_file
        self.cert_expire_warning = cert_expire_warning

        if TLSProfiler.PROFILES is None:
            TLSProfiler.PROFILES = requests.get(self.PROFILES_URL).json()
            log.info(
                f"Loaded version {TLSProfiler.PROFILES['version']} of the Mozilla TLS configuration recommendations."
            )

        self.target_profile = TLSProfiler.PROFILES["configurations"][
            target_profile]
        self.target_profile["tls_curves"] = self._get_equivalent_curves(
            self.target_profile["tls_curves"])
        self.target_profile[
            "certificate_curves"] = self._get_equivalent_curves(
                self.target_profile["certificate_curves"])

        self.scanner = SynchronousScanner()
        try:
            server_tester = ServerConnectivityTester(hostname=domain, )
            log.info(
                f"Testing connectivity with {server_tester.hostname}:{server_tester.port}..."
            )
            self.server_info = server_tester.perform()
            self.server_error = None
        except ServerConnectivityError as e:
            # Could not establish an SSL connection to the server
            log.warning(
                f"Could not connect to {e.server_info.hostname}: {e.error_message}"
            )
            self.server_error = e.error_message
            self.server_info = None

    def _get_equivalent_curves(self, curves: List[str]) -> Optional[List[str]]:
        if not curves:
            return None

        curves_tmp = curves.copy()
        for curve in curves:
            for curve_tuple in _EQUIVALENT_CURVES:
                if curve == curve_tuple[0]:
                    curves_tmp.append(curve_tuple[1])
                elif curve == curve_tuple[1]:
                    curves_tmp.append(curve_tuple[0])
        return curves_tmp

    def run(self) -> TLSProfilerResult:
        if self.server_info is None:
            return

        (
            validation_errors,
            cert_profile_error,
            cert_warnings,
            pub_key_type,
        ) = self._check_certificate()
        hsts_errors = self._check_hsts_age()
        self._scan_supported_ciphers_and_protocols()
        profile_errors = self._check_server_matches_profile(pub_key_type)
        vulnerability_errors = self._check_vulnerabilities()

        return TLSProfilerResult(
            validation_errors,
            cert_warnings,
            profile_errors + hsts_errors + cert_profile_error,
            vulnerability_errors,
        )

    def _scan(self, command: PluginScanCommand):
        return self.scanner.run_scan_command(self.server_info, command)

    def _scan_supported_ciphers_and_protocols(self):
        supported_ciphers = dict()
        supported_protocols = []
        supported_key_exchange = []
        supported_curves = []
        server_preferred_order = dict()
        for name, command in self.SCAN_COMMANDS.items():
            log.debug(f"Testing protocol {name}")
            result = self._scan(command())  # type: CipherSuiteScanResult
            ciphers = [
                cipher.openssl_name for cipher in result.accepted_cipher_list
            ]
            supported_ciphers[name] = ciphers
            key_exchange = [(cipher.dh_info, cipher.openssl_name)
                            for cipher in result.accepted_cipher_list
                            if cipher.dh_info]
            supported_key_exchange.extend(key_exchange)
            supported_curves.extend(result.supported_curves)
            server_preferred_order[name] = result.server_cipher_preference
            if len(ciphers):
                supported_protocols.append(name)

        self.supported_ciphers = supported_ciphers
        self.supported_protocols = set(supported_protocols)
        self.supported_key_exchange = (supported_key_exchange
                                       )  # type: List[(KeyExchangeInfo, str)]
        self.supported_curves = set(supported_curves)
        self.server_preferred_order = server_preferred_order

    def _check_pub_key_supports_cipher(self, cipher: str,
                                       pub_key_type: str) -> bool:
        """
        Checks if cipher suite works with the servers certificate (for TLS 1.2 and older).
        Source: https://wiki.mozilla.org/Security/Server_Side_TLS, https://tools.ietf.org/html/rfc5246#appendix-A.5
        :param cipher: OpenSSL cipher name
        :param pub_key_type:
        :return:
        """
        if "anon" in cipher:
            return True
        elif pub_key_type in cipher:
            return True
        elif pub_key_type == "RSA" and "ECDSA" not in cipher and "DSS" not in cipher:
            return True

        return False

    def _check_protocols(self) -> List[str]:
        errors = []

        # match supported TLS versions
        allowed_protocols = set(self.target_profile["tls_versions"])
        illegal_protocols = self.supported_protocols - allowed_protocols
        missing_protocols = allowed_protocols - self.supported_protocols

        for protocol in illegal_protocols:
            errors.append(f"must not support {protocol}")

        for protocol in missing_protocols:
            errors.append(f"must support {protocol}")

        return errors

    def _check_cipher_suites_and_order(self, pub_key_type: str) -> List[str]:
        errors = []

        # match supported cipher suite order for each supported protocol
        all_supported_ciphers = []
        for protocol, supported_ciphers in self.supported_ciphers.items():
            all_supported_ciphers.extend(supported_ciphers)

            if protocol in self.supported_protocols:
                allowed_ciphers = self.target_profile["ciphers"]["openssl"]

                # check if the server chooses the cipher suite
                if (self.target_profile["server_preferred_order"]
                        and not self.server_preferred_order[protocol]):
                    errors.append(
                        f"server must choose the cipher suite, not the client (Protocol {protocol})"
                    )

                # check if the client chooses the cipher suite
                if (not self.target_profile["server_preferred_order"]
                        and self.server_preferred_order[protocol]):
                    errors.append(
                        f"client must choose the cipher suite, not the server (Protocol {protocol})"
                    )

                # check whether the servers preferred cipher suite preference is correct
                if (self.target_profile["server_preferred_order"]
                        and self.server_preferred_order[protocol]
                        and not utils.check_cipher_order(
                            allowed_ciphers, supported_ciphers)):
                    errors.append(
                        f"server has the wrong cipher suites order (Protocol {protocol})"
                    )

        # find cipher suites that should not be supported
        allowed_ciphers = (self.target_profile["ciphersuites"] +
                           self.target_profile["ciphers"]["openssl"])
        illegal_ciphers = set(all_supported_ciphers) - set(allowed_ciphers)
        for cipher in illegal_ciphers:
            errors.append(f"must not support {cipher}")

        # find missing cipher suites
        missing_ciphers = set(allowed_ciphers) - set(all_supported_ciphers)
        for cipher in missing_ciphers:
            if self._check_pub_key_supports_cipher(cipher, pub_key_type):
                errors.append(f"must support {cipher}")

        return errors

    def _check_ecdh_and_dh(self) -> List[str]:
        errors = []

        # match DHE and ECDHE parameters
        for (key_info, cipher) in self.supported_key_exchange:
            if (isinstance(key_info, DhKeyExchangeInfo)
                    and not self.target_profile["dh_param_size"]):
                errors.append(f"must not support finite field DH key exchange")
                break
            elif (isinstance(key_info, DhKeyExchangeInfo) and
                  key_info.key_size != self.target_profile["dh_param_size"]):
                errors.append(
                    f"wrong DHE parameter size {key_info.key_size} for cipher {cipher}"
                    f", should be {self.target_profile['dh_param_size']}")

        # match ECDH curves used for key exchange
        allowed_curves = self.target_profile["tls_curves"]
        for curve in self.supported_curves:
            if curve not in allowed_curves:
                errors.append(
                    f"must not support ECDH curve {curve} for key exchange")

        return errors

    def _check_server_matches_profile(self, pub_key_type: str):
        errors = []

        errors.extend(self._check_protocols())

        errors.extend(self._check_cipher_suites_and_order(pub_key_type))

        errors.extend(self._check_ecdh_and_dh())

        return errors

    def _cert_type_string(self, pub_key) -> str:
        if isinstance(pub_key, rsa.RSAPublicKey):
            return "RSA"
        elif isinstance(pub_key, ec.EllipticCurvePublicKey):
            return "ECDSA"
        elif isinstance(pub_key, ed25519.Ed25519PublicKey):
            return "ED25519"
        elif isinstance(pub_key, ed448.Ed448PublicKey):
            return "ED448"
        elif isinstance(pub_key, dsa.DSAPublicKey):
            return "DSA"

        return ""

    def _check_certificate_properties(
            self, certificate: Certificate,
            ocsp_stapling: bool) -> Tuple[List[str], List[str], str]:
        errors = []
        warnings = []

        # check certificate lifespan
        lifespan = certificate.not_valid_after - certificate.not_valid_before
        if self.target_profile["maximum_certificate_lifespan"] < lifespan.days:
            errors.append(
                f"certificate lifespan too long (is {lifespan.days}, "
                f"should be less than {self.target_profile['maximum_certificate_lifespan']})"
            )

        current_time = datetime.now()
        days_before_expire = certificate.not_valid_after - current_time
        if days_before_expire.days < self.cert_expire_warning:
            warnings.append(
                f"Certificate expires in {days_before_expire.days} days")

        # check certificate public key type
        pub_key_type = self._cert_type_string(certificate.public_key())
        if pub_key_type.lower(
        ) not in self.target_profile["certificate_types"]:
            errors.append(f"wrong certificate type ({pub_key_type})")

        # check key property
        pub_key = certificate.public_key()
        if (isinstance(pub_key, rsa.RSAPublicKey)
                and self.target_profile["rsa_key_size"]
                and pub_key.key_size != self.target_profile["rsa_key_size"]):
            errors.append(
                f"RSA certificate has wrong key size (is {pub_key.key_size}, "
                f"should be {self.target_profile['rsa_key_size']})")
        elif (isinstance(pub_key, ec.EllipticCurvePublicKey)
              and self.target_profile["certificate_curves"]
              and pub_key.curve.name
              not in self.target_profile["certificate_curves"]):
            errors.append(
                f"ECDSA certificate uses wrong curve "
                f"(is {pub_key.curve.name}, should be one of {self.target_profile['certificate_curves']})"
            )

        # check certificate signature
        if (certificate.signature_algorithm_oid._name
                not in self.target_profile["certificate_signatures"]):
            errors.append(f"certificate has a wrong signature")

        # check if ocsp stabling is supported
        if ocsp_stapling != self.target_profile["ocsp_staple"]:
            errors.append(f"OCSP stapling must be supported")

        return errors, warnings, pub_key_type

    def _check_certificate(
            self) -> Tuple[List[str], List[str], List[str], str]:
        result = self._scan(CertificateInfoScanCommand(
            ca_file=self.ca_file))  # type: CertificateInfoScanResult

        validation_errors = []

        certificate = result.received_certificate_chain[0]
        (
            profile_errors,
            cert_warnings,
            pub_key_type,
        ) = self._check_certificate_properties(certificate,
                                               result.ocsp_response_is_trusted)

        for r in result.path_validation_result_list:
            if not r.was_validation_successful:
                validation_errors.append(
                    f"validation not successful: {r.verify_string} (trust store {r.trust_store.name})"
                )

        if result.path_validation_error_list:
            validation_errors = (fail.error_message
                                 for fail in result.path_validation_error_list)
            validation_errors.append(
                f'Validation failed: {", ".join(validation_errors)}')

        if not result.leaf_certificate_subject_matches_hostname:
            validation_errors.append(
                f"Leaf certificate subject does not match hostname!")

        if not result.received_chain_has_valid_order:
            validation_errors.append(f"Certificate chain has wrong order.")

        if result.verified_chain_has_sha1_signature:
            validation_errors.append(f"SHA1 signature found in chain.")

        if result.verified_chain_has_legacy_symantec_anchor:
            validation_errors.append(
                f"Symantec legacy certificate found in chain.")

        sct_count = result.leaf_certificate_signed_certificate_timestamps_count
        if sct_count < 2 and certificate.not_valid_before >= self.SCT_REQUIRED_DATE:
            validation_errors.append(
                f"Certificates issued on or after 2018-04-01 need certificate transparency, "
                f"i.e., two signed SCTs in certificate. Leaf certificate only has {sct_count}."
            )

        if len(validation_errors) == 0:
            log.debug(f"Certificate is ok")
        else:
            log.debug(f"Error validating certificate")
            for error in validation_errors:
                log.debug(f"  → {error}")

        return validation_errors, profile_errors, cert_warnings, pub_key_type

    def _check_vulnerabilities(self):
        errors = []

        result = self._scan(
            HeartbleedScanCommand())  # type: HeartbleedScanResult

        if result.is_vulnerable_to_heartbleed:
            errors.append(f"Server is vulnerable to Heartbleed attack")

        result = self._scan(OpenSslCcsInjectionScanCommand()
                            )  # type: OpenSslCcsInjectionScanResult

        if result.is_vulnerable_to_ccs_injection:
            errors.append(
                f"Server is vulnerable to OpenSSL CCS Injection (CVE-2014-0224)"
            )

        result = self._scan(RobotScanCommand())  # type: RobotScanResult

        if result.robot_result_enum in [
                RobotScanResultEnum.VULNERABLE_WEAK_ORACLE,
                RobotScanResultEnum.VULNERABLE_STRONG_ORACLE,
        ]:
            errors.append(f"Server is vulnerable to ROBOT attack.")

        return errors

    def _check_hsts_age(self) -> List[str]:
        result = self._scan(
            HttpHeadersScanCommand())  # type: HttpHeadersScanResult

        errors = []

        if result.strict_transport_security_header:
            if (result.strict_transport_security_header.max_age <
                    self.target_profile["hsts_min_age"]):
                errors.append(
                    f"wrong HSTS age (is {result.strict_transport_security_header.max_age}, "
                    f"should be at least {self.target_profile['hsts_min_age']})"
                )
        else:
            errors.append(f"HSTS header not set")

        return errors
Exemple #30
0
    def scan(self, protocol='all'):
        """
        Scans target for all policy checks

        :param protocol: (str) Default: all; It can also be one of the keys of
                         self.policies

        :returns: (array) of (dict)
            [] empty array when there is no service running in host:port.
            [
                {
                    "protocol":"ssl2.0",
                    "is_supported":True,
                    "is_allowed":False,
                    "has_passed":False,
                    "ciphers_supported":[TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA]
                    "problematic_ciphers":[TLS_RSA_WITH_AES_128_CBC_SHA]
                },
                ...
            ]
        """
        # Define if one of all policies will be checked
        policies = {}
        if protocol == 'all':
            policies = self.policies
        elif protocol in list(self.policies):
            policies[protocol] = self.policies[protocol]
        else:
            raise ValueError('Invalid protocol selected for scanning.')

        # Try handshake
        server_info = self.connect()
        if server_info is None:
            message = "SSL Handshake failed. Checking service port for "\
                "unencrypted channels."
            print(message)
            if self.check_service():
                return [{
                    "protocol": 'unencrypted',
                    "is_supported": True,
                    "is_allowed": False,
                    "has_passed": False,
                    "ciphers_supported": [],
                    "problematic_ciphers": [],
                }]
            else:
                return []

        # Retrieve definitions from class to iterate
        print("Initializing synchronous SSL scan.")
        synchronous_scanner = SynchronousScanner()
        results = []
        for protocol, policy in policies.items():
            print("Checking {} protocol support".format(protocol))
            is_allowed = policy['allowed']
            command = policy['command']
            scan_result = synchronous_scanner.run_scan_command(
                server_info, command)

            # Detect cipher support
            ciphers = scan_result.accepted_cipher_list
            ciphers_supported = []
            problematic_ciphers = []
            for cipher in ciphers:
                name = cipher.name
                ciphers_supported.append(name)
                if re.match(r'.*(SHA)$', name) or \
                    (name.find('RSA_WITH_AES') == -1):
                    problematic_ciphers.append(name)

            # Determine support based on ciphers
            if len(ciphers_supported) > 0:
                is_supported = True
            else:
                is_supported = False

            # Determine if the check has passed for this protocol
            if not is_allowed and not is_supported:
                has_passed = True  # don't care about cipher suite list
            elif not is_allowed and is_supported:
                has_passed = False
            elif is_allowed and not is_supported:
                has_passed = True
            elif is_allowed and is_supported:
                if len(problematic_ciphers) > 0:
                    has_passed = False
                else:
                    has_passed = True
            else:
                print('One should never get here!')
                raise OSError('Program logic error.')

            result = {
                "protocol": protocol,
                "is_supported": is_supported,
                "is_allowed": is_allowed,
                "has_passed": has_passed,
                "ciphers_supported": ciphers_supported,
                "problematic_ciphers": problematic_ciphers,
            }
            results.insert(0, result)
        print("SSL scan completed.")
        return results
def ssltlsscan(web):
    global name
    name = targetname(web)
    global lvl2
    lvl2 = inspect.stack()[0][3]
    global module
    module = "ScanANDEnum"
    global lvl1
    lvl1 = "Scanning & Enumeration"
    global lvl3
    lvl3 = ""
    target = web.split('//')[1]
    #print(R+'\n    ===============================')
    #print(R+'     S S L   E N U M E R A T I O N')
    #print(R+'    ===============================\n')
    from core.methods.print import pscan
    pscan("ssl enumeration")
    print(GR + ' [*] Testing server SSL status...')
    try:
        req = requests.get('https://' + target)
        print(G + ' [+] SSL Working Properly...' + color.TR2 + C)
        time.sleep(0.6)
        print(C + " [!] Running SSL Enumeration...\n")
        try:
            server_tester = ServerConnectivityTester(hostname=target)
            server_info = server_tester.perform()
            scanner = SynchronousScanner()

            command = Tlsv10ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.0 Ciphers:" + color.TR2 + C)
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')
            data = "TLS 1.0 :> " + str(scan_result.accepted_cipher_list)
            save_data(database, module, lvl1, lvl2, lvl3, name, data)

            command = Tlsv11ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.1 Ciphers:" + color.TR2 + C)
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')
            data = "TLS 1.1 :> " + str(scan_result.accepted_cipher_list)
            save_data(database, module, lvl1, lvl2, lvl3, name, data)

            command = Tlsv12ScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + " [+] Available TLS v1.2 Ciphers:" + color.TR2 + C)
            for cipher in scan_result.accepted_cipher_list:
                print(C + '    {}'.format(cipher.name))
            print('')
            data = "TLS 1.2 :> " + str(scan_result.accepted_cipher_list)
            save_data(database, module, lvl1, lvl2, lvl3, name, data)

            command = CertificateInfoScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + ' [+] Certificate Information:' + color.TR2 + C)
            for entry in scan_result.as_text():
                if entry != '':
                    if 'certificate information' in entry.lower():
                        pass
                    elif ':' in entry:
                        print(GR + '    [+] ' +
                              entry.strip().split(':', 1)[0].strip() + ' : ' +
                              C + entry.strip().split(':', 1)[1].strip())
                    else:
                        print(C + '\n  [+] ' + entry.strip())
            print('')
            data = "Cert Info :> " + str(scan_result.as_text())
            save_data(database, module, lvl1, lvl2, lvl3, name, data)

            command = HttpHeadersScanCommand()
            scan_result = scanner.run_scan_command(server_info, command)
            print(G + ' [+] HTTP Results:' + C + color.TR2 + C)
            for entry in scan_result.as_text():
                if 'http security' not in entry.strip().lower(
                ) and entry != '':
                    if '-' in entry:
                        print(GR + '    [+] ' +
                              entry.split('-', 1)[0].strip() + ' - ' + C +
                              entry.split('-', 1)[1].strip())
                    elif ':' in entry:
                        print(GR + '    [+] ' +
                              entry.strip().split(':', 1)[0].strip() + ' : ' +
                              C + entry.strip().split(':', 1)[1].strip())
                    else:
                        print(C + '\n  [+] ' + entry.strip())
            print('')
            data = "HTTP :> " + str(scan_result.as_text())
            save_data(database, module, lvl1, lvl2, lvl3, name, data)

        except Exception as e:
            print(R + ' [-] Unhandled SSL Runtime Exception : ' + str(e))
            pass

    except requests.exceptions.SSLError as e:
        print(R + ' [-] Distant Server SSL not working : ' + str(e))

    print(G + ' [+] SSlScan Module Completed!' + C + color.TR2 + C)
Exemple #32
0
    try:
        server_info = ServerConnectivityInfo(
            hostname=hostname,
            port=587,
            tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP)
        server_info.test_connectivity_to_server()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        raise RuntimeError('Error when connecting to {}: {}'.format(
            hostname, e.error_msg))

    # Example 1: Run one scan command synchronously to list the server's TLS 1.0 cipher suites
    print('\nRunning one scan command synchronously...')
    synchronous_scanner = SynchronousScanner()
    command = Tlsv10ScanCommand()
    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    for cipher in scan_result.accepted_cipher_list:
        print('    {}'.format(cipher.name))

    # Example 2: Run multiple scan commands concurrently. It is of course much faster than the SynchronousScanner
    concurrent_scanner = ConcurrentScanner()

    # Queue some scan commands
    print('\nQueuing some commands...')
    concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand())
    concurrent_scanner.queue_scan_command(server_info,
                                          SessionRenegotiationScanCommand())
    concurrent_scanner.queue_scan_command(server_info,
                                          CertificateInfoScanCommand())

    # Process the results
Exemple #33
0
    # Setup the server to scan and ensure it is online/reachable
    hostname = 'smtp.gmail.com'
    try:
        server_info = ServerConnectivityInfo(hostname=hostname, port=587,
                                             tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP)
        server_info.test_connectivity_to_server()
    except ServerConnectivityError as e:
        # Could not establish an SSL connection to the server
        raise RuntimeError('Error when connecting to {}: {}'.format(hostname, e.error_msg))


    # Example 1: Run one scan command synchronously to list the server's TLS 1.0 cipher suites
    print('\nRunning one scan command synchronously...')
    synchronous_scanner = SynchronousScanner()
    command = Tlsv10ScanCommand()
    scan_result = synchronous_scanner.run_scan_command(server_info, command)
    for cipher in scan_result.accepted_cipher_list:
        print('    {}'.format(cipher.name))


    # Example 2: Run multiple scan commands concurrently. It is of course much faster than the SynchronousScanner
    concurrent_scanner = ConcurrentScanner()

    # Queue some scan commands
    print('\nQueuing some commands...')
    concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, SessionRenegotiationScanCommand())
    concurrent_scanner.queue_scan_command(server_info, CertificateInfoScanCommand())

    # Process the results
    reneg_result = None