Exemple #1
0
    def test_null_cipher_suites(self):
        # Given a server to scan that supports NULL cipher suites
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("null.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info)

        # And the NULL/Anon cipher suites were detected
        expected_ciphers = {
            "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
            "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
            "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
            "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
            "TLS_DH_anon_WITH_AES_256_CBC_SHA",
            "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
            "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
            "TLS_DH_anon_WITH_AES_128_CBC_SHA",
            "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
            "TLS_DH_anon_WITH_SEED_CBC_SHA",
            "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_NULL_SHA",
            "TLS_ECDH_anon_WITH_NULL_SHA",
            "TLS_RSA_WITH_NULL_SHA256",
            "TLS_RSA_WITH_NULL_SHA",
        }
        assert expected_ciphers == {
            accepted_cipher.cipher_suite.name for accepted_cipher in result.accepted_cipher_suites
        }
Exemple #2
0
    def test_tlsv1_2_enabled(self):
        # Given a server to scan that supports TLS 1.2
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.google.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info)

        # And the result confirms that TLS 1.2 is supported
        expected_ciphers = {
            "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
            "TLS_RSA_WITH_AES_256_GCM_SHA384",
            "TLS_RSA_WITH_AES_256_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_RSA_WITH_AES_128_GCM_SHA256",
            "TLS_RSA_WITH_AES_128_CBC_SHA",
            "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
        }
        assert expected_ciphers == {
            accepted_cipher.cipher_suite.name for accepted_cipher in result.accepted_cipher_suites
        }
    def test_1000_sans_chain(self):
        # Given a server to scan that has a leaf cert with 1000 SANs
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("1000-sans.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        CertificateInfoImplementation.scan_server(server_info)
    def test_invalid_chain(self):
        # Given a server to scan that has a self-signed certificate
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "self-signed.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # A verified chain cannot be built
        assert not plugin_result.certificate_deployments[
            0].verified_certificate_chain
        assert plugin_result.certificate_deployments[
            0].verified_chain_has_sha1_signature is None

        # And the result has other details about the certificate chain
        assert plugin_result.certificate_deployments[0].ocsp_response is None
        assert len(plugin_result.certificate_deployments[0].
                   received_certificate_chain) == 1

        assert len(plugin_result.certificate_deployments[0].
                   path_validation_results) >= 5
        for path_validation_result in plugin_result.certificate_deployments[
                0].path_validation_results:
            assert not path_validation_result.was_validation_successful

        assert plugin_result.certificate_deployments[
            0].leaf_certificate_signed_certificate_timestamps_count == 0

        assert plugin_result.certificate_deployments[
            0].leaf_certificate_subject_matches_hostname
        assert plugin_result.certificate_deployments[
            0].received_chain_has_valid_order
        assert plugin_result.certificate_deployments[
            0].received_chain_contains_anchor_certificate is None
Exemple #5
0
    def test_ecdsa_certificate(self):
        # Given a server to scan that has an ECDSA certificate
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.cloudflare.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        CertificateInfoImplementation.perform(server_info)
    def test_ca_file(self):
        # Given a server to scan
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.hotmail.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # And a valid path to a custom CA file
        ca_file_path = Path(
            __file__
        ).parent / ".." / ".." / "certificates" / "wildcard-self-signed.pem"

        # When running the scan with the custom CA file enabled
        plugin_result = CertificateInfoImplementation.scan_server(
            server_info,
            CertificateInfoExtraArguments(custom_ca_file=ca_file_path))

        # It succeeds
        assert len(plugin_result.certificate_deployments[0].
                   path_validation_results) >= 6
        for path_validation_result in plugin_result.certificate_deployments[
                0].path_validation_results:
            if path_validation_result.trust_store.path == ca_file_path:
                assert not path_validation_result.was_validation_successful
            else:
                assert path_validation_result.was_validation_successful
    def test_valid_chain_with_ev_cert(self):
        # Given a server to scan that has an EV certificate
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.digicert.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # The result returns that the certificate is EV
        assert plugin_result.certificate_deployments[0].leaf_certificate_is_ev

        # And the result has other details about the certificate chain
        assert len(plugin_result.certificate_deployments[0].
                   received_certificate_chain)
        assert len(plugin_result.certificate_deployments[0].
                   verified_certificate_chain)
        assert not plugin_result.certificate_deployments[
            0].received_chain_contains_anchor_certificate

        assert len(plugin_result.certificate_deployments[0].
                   path_validation_results) == 5
        for path_validation_result in plugin_result.certificate_deployments[
                0].path_validation_results:
            assert path_validation_result.was_validation_successful

        assert plugin_result.certificate_deployments[
            0].leaf_certificate_subject_matches_hostname
        assert plugin_result.certificate_deployments[
            0].received_chain_has_valid_order
Exemple #8
0
    def test_json_serializer_functions(self):
        # Given a completed scan for a server with the CERTIFICATE_INFO scan command
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.hotmail.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)
        plugin_result = CertificateInfoImplementation.perform(server_info)
        scan_results = {ScanCommandEnum.CERTIFICATE_INFO: plugin_result}
        scan_result = ServerScanResultFactory.create(
            scan_commands_results=scan_results)

        # When generating the JSON output for this server scan
        with StringIO() as file_out:
            json_generator = JsonOutputGenerator(file_to=file_out)
            json_generator.server_scan_completed(scan_result)

            # We call scans_completed() because this is when the output actually gets written to the file
            json_generator.scans_completed(0.2)
            final_output = file_out.getvalue()

        # It succeeds
        assert final_output

        # And complex object like certificates were properly serialized
        assert "notBefore" in final_output
        assert "issuer" in final_output
        assert "subject" in final_output
Exemple #9
0
    def get_supported_tls(self, highest_supported):

        supported = [highest_supported]

        for version, method in {
                "SSL_2_0": TlsVersionEnum.SSLV2,
                "SSL_3_0": TlsVersionEnum.SSLV3,
                "TLS_1_0": TlsVersionEnum.TLSV1,
                "TLS_1_1": TlsVersionEnum.TLSV1_1,
                "TLS_1_2": TlsVersionEnum.TLSV1_2,
        }.items():

            # Only test SSL/TLS connections with lesser versions
            if highest_supported == version:
                break

            try:
                # Attempt connection
                # If connection fails, exception will be raised, causing the failure to be
                # logged and the version to not be appended to the supported list
                ctx = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
                    self.domain, 443)
                cfg = ServerNetworkConfiguration(self.domain)
                connx = SslConnection(ctx, cfg, method, True)
                connx.connect(self.domain)
                supported.append(version)
            except Exception as e:
                logging.info(
                    f"Failed to connect using %{version}: ({type(e)}) - {e}")

        return supported
Exemple #10
0
    def test_via_direct_connection_but_server_tls_config_not_supported(self):
        # Given a server location for a server that only supports DH settings that SSLyze can't use
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            hostname="dh480.badssl.com", port=443)

        # When testing connectivity, it fails with the right error
        with pytest.raises(ServerTlsConfigurationNotSupported):
            ServerConnectivityTester().perform(server_location)
Exemple #11
0
    def test_via_direct_connection_but_server_rejected_connection(self):
        # Given a server location for a server that's offline
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            hostname="localhost", port=1234)

        # When testing connectivity, it fails with the right error
        with pytest.raises(ServerRejectedConnection):
            ServerConnectivityTester().perform(server_location)
    def test_certificate_with_no_subject(self):
        # Given a server to scan that has a certificate with no Subject
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("no-subject.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        assert plugin_result.certificate_deployments[0].verified_certificate_chain
Exemple #13
0
    def test_robot_attack_good(self):
        # Validate the bug fix for https://github.com/nabla-c0d3/sslyze/issues/282
        # Given a server to scan that is not vulnerable to ROBOT
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "guide.duo.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        result: RobotScanResult = RobotImplementation.perform(server_info)
        assert result.robot_result == RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE
Exemple #14
0
def main() -> None:
    # First validate that we can connect to the servers we want to scan
    servers_to_scan = []
    for hostname in ["cloudflare.com", "google.com"]:
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(hostname, 443)
        try:
            server_info = ServerConnectivityTester().perform(server_location)
            servers_to_scan.append(server_info)
        except ConnectionToServerFailed as e:
            print(f"Error connecting to {server_location.hostname}:{server_location.port}: {e.error_message}")
            return

    scanner = Scanner()

    # Then queue some scan commands for each server
    for server_info in servers_to_scan:
        server_scan_req = ServerScanRequest(
            server_info=server_info,
            scan_commands={
                ScanCommandEnum.TLS_1_0_CIPHER_SUITES,
                ScanCommandEnum.TLS_1_1_CIPHER_SUITES,
                ScanCommandEnum.TLS_1_2_CIPHER_SUITES,
                ScanCommandEnum.CERTIFICATE_INFO,
                ScanCommandEnum.TLS_COMPRESSION,
            },
        )
        scanner.queue_scan(server_scan_req)

    # Then retrieve the result of the scan commands for each server
    for server_scan_result in scanner.get_results():
        print(f"\nResults for {server_scan_result.server_info.server_location.hostname}:")

        # Scan commands that were run with no errors
        for scan_command, result in server_scan_result.scan_commands_results.items():
            if scan_command in [
                ScanCommandEnum.TLS_1_0_CIPHER_SUITES,
                ScanCommandEnum.TLS_1_1_CIPHER_SUITES,
                ScanCommandEnum.TLS_1_2_CIPHER_SUITES,
            ]:
                typed_result = cast(CipherSuitesScanResult, result)
                print(f"\nAccepted cipher suites for {scan_command.name}:")
                for accepted_cipher_suite in typed_result.accepted_cipher_suites:
                    print(f"* {accepted_cipher_suite.cipher_suite.name}")

            elif scan_command == ScanCommandEnum.CERTIFICATE_INFO:
                typed_result = cast(CertificateInfoScanResult, result)
                print("\nCertificate info:")
                for cert_deployment in typed_result.certificate_deployments:
                    print(f"Leaf certificate: \n{cert_deployment.verified_certificate_chain_as_pem[0]}")

            elif scan_command == ScanCommandEnum.TLS_COMPRESSION:
                typed_result = cast(CompressionScanResult, result)
                print(f"\nCompression / CRIME: {typed_result.supports_compression}")

        # Scan commands that were run with errors
        for scan_command, error in server_scan_result.scan_commands_errors.items():
            print(f"\nError when running {scan_command}:\n{error.exception_trace}")
Exemple #15
0
    def test_follows_client_cipher_suite_preference(self):
        # Given a server to scan that follows client cipher suite preference
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.hotmail.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info)

        # And the server is detected as following the client's preference
        assert result.cipher_suite_preferred_by_server
    def test_multiple_certificates(self):
        # Given a server to scan that exposes multiple certificates for maximum compatibility
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.facebook.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # And multiple certificates were detected
        assert len(plugin_result.certificate_deployments) > 1
    def test_certificate_with_scts(self):
        # Given a server to scan that has a certificate with SCTS
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.apple.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # And the SCTS were detected
        assert plugin_result.certificate_deployments[0].leaf_certificate_signed_certificate_timestamps_count > 1
    def test_ca_file_bad_file(self):
        # Given a server to scan
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.hotmail.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When trying to enable a custom CA file but the path is wrong, it fails
        with pytest.raises(ValueError):
            CertificateInfoImplementation.scan_server(
                server_info, CertificateInfoExtraArguments(custom_ca_file=Path("doesntexist"))
            )
    def test_sha256_chain(self):
        # Given a server to scan that has a SHA256-signed certificate
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("sha256.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # No SHA1 signature is detected
        assert not plugin_result.certificate_deployments[0].verified_chain_has_sha1_signature
    def test_chain_with_anchor(self):
        # Given a server to scan that has its anchor certificate returned in its chain
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.verizon.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When running the scan, it succeeds
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # And the anchor certificate was detected
        assert plugin_result.certificate_deployments[0].received_chain_contains_anchor_certificate
    def test(self):
        # Given a completed scan for a cipher suites scan command
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.google.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)
        plugin_result = Tlsv12ScanImplementation.scan_server(server_info)

        # When generating the CLI output for this result, it succeeds
        result_as_txt = Tlsv12ScanImplementation.cli_connector_cls.result_to_console_output(
            plugin_result)
        assert result_as_txt
Exemple #22
0
    def test_sslv3_disabled(self):
        # Given a server to scan that does not support SSL 3.0
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.google.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Sslv30ScanImplementation.scan_server(server_info)

        # And the result confirms that SSL 3.0 is not supported
        assert not result.accepted_cipher_suites
        assert result.rejected_cipher_suites
    def test(self):
        # Given a completed scan for a CERTIFICATE_INFO scan command
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.facebook.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)
        plugin_result = CertificateInfoImplementation.scan_server(server_info)

        # When generating the CLI output for this result, it succeeds
        result_as_txt = CertificateInfoImplementation.cli_connector_cls.result_to_console_output(
            plugin_result)
        assert result_as_txt
Exemple #24
0
    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.scan_commands_extra_args = {}
        if ca_file:
            ca_path = Path(ca_file)
            self.scan_commands_extra_args[
                ScanCommand.CERTIFICATE_INFO] = CertificateInfoExtraArguments(
                    ca_path)

        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_preprocessed"] = self._get_equivalent_curves(
                self.target_profile["certificate_curves"])

        server_location = (
            ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
                domain, 443))
        self.scanner = Scanner()
        try:
            log.info(
                f"Testing connectivity with {server_location.hostname}:{server_location.port}..."
            )
            self.server_info = ServerConnectivityTester().perform(
                server_location)
            self.server_error = None
        except ConnectionToServerFailed as e:
            # Could not establish an SSL connection to the server
            log.warning(
                f"Could not connect to {e.server_location.hostname}: {e.error_message}"
            )
            self.server_error = e.error_message
            self.server_info = None
    def test_heartbleed_good(self):
        # Given a server that is NOT vulnerable to Heartbleed
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.google.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When testing for Heartbleed, it succeeds
        result: HeartbleedScanResult = HeartbleedImplementation.perform(
            server_info)

        # And the server is reported as not vulnerable
        assert not result.is_vulnerable_to_heartbleed
    def test_ccs_injection_good(self):
        # Given a server that is NOT vulnerable to CCS injection
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(
            "www.google.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When testing for CCS injection, it succeeds
        result: OpenSslCcsInjectionScanResult = OpenSslCcsInjectionImplementation.perform(
            server_info)

        # And the server is reported as not vulnerable
        assert not result.is_vulnerable_to_ccs_injection
Exemple #27
0
    def test_tlsv1_0_disabled(self):
        # Given a server to scan that does NOT support TLS 1.0
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("success.trendmicro.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv10ScanImplementation.scan_server(server_info)

        # And the result confirms that TLS 1.0 is not supported
        assert result.cipher_suite_preferred_by_server is None
        assert not result.accepted_cipher_suites
        assert result.rejected_cipher_suites
Exemple #28
0
    def test_tls_1_3_cipher_suites(self):
        # Given a server to scan that supports TLS 1.3
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.cloudflare.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv13ScanImplementation.scan_server(server_info)
        assert result.accepted_cipher_suites

        assert {"TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256"} == {
            accepted_cipher.cipher_suite.name for accepted_cipher in result.accepted_cipher_suites
        }
Exemple #29
0
    def test_smtp(self):
        # Given an SMTP server to scan
        hostname = "smtp.gmail.com"
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(hostname, 587)
        network_configuration = ServerNetworkConfiguration(
            tls_server_name_indication=hostname, tls_opportunistic_encryption=ProtocolWithOpportunisticTlsEnum.SMTP
        )
        server_info = ServerConnectivityTester().perform(server_location, network_configuration)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info)
        assert result.accepted_cipher_suites
Exemple #30
0
    def test_rc4_cipher_suites(self):
        # Given a server to scan that supports RC4 cipher suites
        server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("rc4.badssl.com", 443)
        server_info = ServerConnectivityTester().perform(server_location)

        # When scanning for cipher suites, it succeeds
        result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info)

        # And the RC4 cipher suites were detected
        assert {"TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_RC4_128_SHA"} == {
            accepted_cipher.cipher_suite.name for accepted_cipher in result.accepted_cipher_suites
        }