def get_all_cipher_suites(cls, tls_version: TlsVersionEnum) -> Set[CipherSuite]: """Get the list of cipher suites supported by OpenSSL for the given SSL/TLS version. """ if tls_version in [ TlsVersionEnum.SSL_2_0, TlsVersionEnum.SSL_3_0, TlsVersionEnum.TLS_1_0, TlsVersionEnum.TLS_1_1, ]: openssl_cipher_strings = cls._get_all_cipher_suites_with_legacy_openssl( tls_version) elif tls_version == TlsVersionEnum.TLS_1_2: # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites cipher_suites_from_legacy_openssl = cls._get_all_cipher_suites_with_legacy_openssl( tls_version) ssl_client_modern = SslClient( ssl_version=OpenSslVersionEnum(tls_version.value)) ssl_client_modern.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP") ssl_client_modern.set_ciphersuites( "") # Disable TLS 1.3 cipher suites cipher_suites_from_modern_openssl = set( ssl_client_modern.get_cipher_list()) # Combine the two sets of cipher suites openssl_cipher_strings = cipher_suites_from_legacy_openssl.union( cipher_suites_from_modern_openssl) elif tls_version == TlsVersionEnum.TLS_1_3: ssl_client_modern = SslClient( ssl_version=OpenSslVersionEnum(tls_version.value)) ssl_client_modern.set_cipher_list( "") # Disable NON-TLS-1.3 cipher suites ssl_client_modern.set_ciphersuites( "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:" "TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256" ) # Enable all TLS 1.3 cipher suites openssl_cipher_strings = set(ssl_client_modern.get_cipher_list()) else: raise ValueError("Should never happen") return { CipherSuite.from_openssl(cipher_str, tls_version) for cipher_str in openssl_cipher_strings }
def process_task(self, server_info, scan_command): # type: (ServerConnectivityInfo, FallbackScsvScanCommand) -> FallbackScsvScanResult if server_info.highest_ssl_version_supported.value <= OpenSslVersionEnum.SSLV3.value: raise ValueError('Server only supports SSLv3; no downgrade attacks are possible') # Try to connect using a lower TLS version with the fallback cipher suite enabled ssl_version_downgrade = OpenSslVersionEnum(server_info.highest_ssl_version_supported.value - 1) ssl_connection = server_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version_downgrade) ssl_connection.ssl_client.enable_fallback_scsv() supports_fallback_scsv = False try: # Perform the SSL handshake ssl_connection.connect() except _nassl.OpenSSLError as e: # This is the right, specific alert the server should return if 'tlsv1 alert inappropriate fallback' in str(e.args): supports_fallback_scsv = True else: raise except SSLHandshakeRejected: # If the handshake is rejected, we assume downgrade attacks are prevented (this is how F5 balancers do it) # although it could also be because the server does not support this version of TLS # https://github.com/nabla-c0d3/sslyze/issues/119 supports_fallback_scsv = True finally: ssl_connection.close() return FallbackScsvScanResult(server_info, scan_command, supports_fallback_scsv)
def _parse_all_cipher_suites_with_legacy_openssl( tls_version: TlsVersionEnum) -> Set[str]: ssl_client = LegacySslClient( ssl_version=OpenSslVersionEnum(tls_version.value)) # Disable SRP and PSK cipher suites as they need a special setup in the client and are never used ssl_client.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP") return set(ssl_client.get_cipher_list())
def _parse_all_cipher_suites() -> Dict[TlsVersionEnum, Set[CipherSuite]]: tls_version_to_cipher_suites: Dict[TlsVersionEnum, Set[CipherSuite]] = {} for tls_version in [ TlsVersionEnum.SSL_2_0, TlsVersionEnum.SSL_3_0, TlsVersionEnum.TLS_1_0, TlsVersionEnum.TLS_1_1, ]: openssl_cipher_strings = _parse_all_cipher_suites_with_legacy_openssl(tls_version) tls_version_to_cipher_suites[tls_version] = set() for cipher_suite_openssl_name in openssl_cipher_strings: cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[tls_version][cipher_suite_openssl_name] tls_version_to_cipher_suites[tls_version].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites cipher_suites_from_legacy_openssl = _parse_all_cipher_suites_with_legacy_openssl(TlsVersionEnum.TLS_1_2) ssl_client_modern = SslClient(ssl_version=OpenSslVersionEnum(TlsVersionEnum.TLS_1_2.value)) ssl_client_modern.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP") cipher_suites_from_modern_openssl = set(ssl_client_modern.get_cipher_list()) # Combine the two sets of cipher suites openssl_cipher_strings = cipher_suites_from_legacy_openssl.union(cipher_suites_from_modern_openssl) tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2] = set() for cipher_suite_openssl_name in openssl_cipher_strings: # Ignore TLS 1.3 cipher suites if cipher_suite_openssl_name in _TLS_1_3_CIPHER_SUITES: continue cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[TlsVersionEnum.TLS_1_2][cipher_suite_openssl_name] tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # TLS 1.3 - the list is just hardcoded tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_3] = { CipherSuite( # For TLS 1.3 OpenSSL started using the official names name=cipher_suite_name, openssl_name=cipher_suite_name, is_anonymous=False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_name], ) for cipher_suite_name in _TLS_1_3_CIPHER_SUITES } return tls_version_to_cipher_suites
def __init__( self, server_location: ServerNetworkLocation, network_configuration: ServerNetworkConfiguration, tls_version: "TlsVersionEnum", should_ignore_client_auth: bool, should_use_legacy_openssl: Optional[bool] = None, ca_certificates_path: Optional[Path] = None, ) -> None: self._server_location = server_location self._network_configuration = network_configuration # Create the SSL client nassl_tls_version = OpenSslVersionEnum(tls_version.value) self.ssl_client: BaseSslClient # For older versions of TLS/SSL, we have to use a legacy OpenSSL if should_use_legacy_openssl is None: # For older versions of TLS/SSL, we have to use a legacy OpenSSL final_should_use_legacy_openssl = ( False if nassl_tls_version in [OpenSslVersionEnum.TLSV1_2, OpenSslVersionEnum.TLSV1_3] else True ) else: final_should_use_legacy_openssl = should_use_legacy_openssl ssl_client_cls = LegacySslClient if final_should_use_legacy_openssl else SslClient if network_configuration.tls_client_auth_credentials: # A client certificate and private key were provided self.ssl_client = ssl_client_cls( ssl_version=nassl_tls_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ca_certificates_path, client_certificate_chain=network_configuration.tls_client_auth_credentials.certificate_chain_path, client_key=network_configuration.tls_client_auth_credentials.key_path, client_key_type=network_configuration.tls_client_auth_credentials.key_type, client_key_password=network_configuration.tls_client_auth_credentials.key_password, ignore_client_authentication_requests=False, ) else: # No client cert and key self.ssl_client = ssl_client_cls( ssl_version=nassl_tls_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ca_certificates_path, ignore_client_authentication_requests=should_ignore_client_auth, ) # Add Server Name Indication if nassl_tls_version != OpenSslVersionEnum.SSLV2: self.ssl_client.set_tlsext_host_name(network_configuration.tls_server_name_indication) # And a default cipher list to make the client hello smaller so we don't run into # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665452 if nassl_tls_version != OpenSslVersionEnum.TLSV1_3: self.ssl_client.set_cipher_list("HIGH:MEDIUM:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA")
def _test_scsv(server_info: ServerConnectivityInfo) -> bool: # Try with TLS 1.2 even if the server supports TLS 1.3 or higher as there is no downgrade possible with TLS 1.3 if server_info.tls_probing_result.highest_tls_version_supported >= OpenSslVersionEnum.TLSV1_3: ssl_version_to_use = OpenSslVersionEnum.TLSV1_2 else: ssl_version_to_use = server_info.tls_probing_result.highest_tls_version_supported # Try to connect using a lower TLS version with the fallback cipher suite enabled ssl_version_downgrade = OpenSslVersionEnum(ssl_version_to_use.value - 1) # type: ignore ssl_connection = server_info.get_preconfigured_tls_connection( override_tls_version=ssl_version_downgrade, # Only the legacy client has enable_fallback_scsv() should_use_legacy_openssl=True, ) if not isinstance(ssl_connection.ssl_client, LegacySslClient): raise RuntimeError("Should never happen") ssl_connection.ssl_client.enable_fallback_scsv() supports_fallback_scsv = False try: # Perform the SSL handshake ssl_connection.connect() except _nassl.OpenSSLError as e: # This is the right, specific alert the server should return if "tlsv1 alert inappropriate fallback" in str(e.args): supports_fallback_scsv = True else: raise except ServerRejectedTlsHandshake: # If the handshake is rejected, we assume downgrade attacks are prevented (this is how F5 balancers do it) # although it could also be because the server does not support this version of TLS # https://github.com/nabla-c0d3/sslyze/issues/119 supports_fallback_scsv = True finally: ssl_connection.close() return supports_fallback_scsv
def _parse_all_cipher_suites() -> Dict[TlsVersionEnum, Set[CipherSuite]]: tls_version_to_cipher_suites: Dict[TlsVersionEnum, Set[CipherSuite]] = {} for tls_version in [ TlsVersionEnum.SSL_2_0, TlsVersionEnum.SSL_3_0, TlsVersionEnum.TLS_1_0, TlsVersionEnum.TLS_1_1, ]: openssl_cipher_strings = _parse_all_cipher_suites_with_legacy_openssl(tls_version) tls_version_to_cipher_suites[tls_version] = set() for cipher_suite_openssl_name in openssl_cipher_strings: cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[tls_version][cipher_suite_openssl_name] tls_version_to_cipher_suites[tls_version].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites cipher_suites_from_legacy_openssl = _parse_all_cipher_suites_with_legacy_openssl(TlsVersionEnum.TLS_1_2) ssl_client_modern = SslClient(ssl_version=OpenSslVersionEnum(TlsVersionEnum.TLS_1_2.value)) ssl_client_modern.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP") ssl_client_modern.set_ciphersuites("") # Disable TLS 1.3 cipher suites cipher_suites_from_modern_openssl = set(ssl_client_modern.get_cipher_list()) # Combine the two sets of cipher suites openssl_cipher_strings = cipher_suites_from_legacy_openssl.union(cipher_suites_from_modern_openssl) tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2] = set() for cipher_suite_openssl_name in openssl_cipher_strings: cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[TlsVersionEnum.TLS_1_2][cipher_suite_openssl_name] tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # TLS 1.3 ssl_client_modern = SslClient(ssl_version=OpenSslVersionEnum(TlsVersionEnum.TLS_1_3.value)) ssl_client_modern.set_cipher_list("") # Disable NON-TLS-1.3 cipher suites ssl_client_modern.set_ciphersuites( "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:" "TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256" ) # Enable all TLS 1.3 cipher suites openssl_cipher_strings = set(ssl_client_modern.get_cipher_list()) tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_3] = { CipherSuite( # For TLS 1.3 OpenSSL started using the official names name=cipher_suite_openssl_name, openssl_name=cipher_suite_openssl_name, is_anonymous=False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_openssl_name], ) for cipher_suite_openssl_name in openssl_cipher_strings } return tls_version_to_cipher_suites