def test_valid_chain(self):
        server_info = ServerConnectivityInfo(hostname='www.hotmail.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertTrue(plugin_result.ocsp_response)
        self.assertTrue(plugin_result.is_ocsp_response_trusted)
        self.assertTrue(plugin_result.is_leaf_certificate_ev)

        self.assertEquals(len(plugin_result.certificate_chain), 2)

        self.assertEquals(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            self.assertTrue(path_validation_result.is_certificate_trusted)

        self.assertEquals(len(plugin_result.path_validation_error_list), 0)
        self.assertEquals(plugin_result.hostname_validation_result, X509_NAME_MATCHES_SAN)
        self.assertTrue(plugin_result.is_certificate_chain_order_valid)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Test the --ca_path option
        plugin_result = plugin.process_task(server_info, 'certinfo_basic',
                                            {'ca_file': os.path.join(os.path.dirname(__file__), 'utils',
                                                                     'wildcard-self-signed.pem')})

        self.assertEquals(len(plugin_result.path_validation_result_list), 6)
        for path_validation_result in plugin_result.path_validation_result_list:
            if path_validation_result.trust_store.name == 'Custom --ca_file':
                self.assertFalse(path_validation_result.is_certificate_trusted)
            else:
                self.assertTrue(path_validation_result.is_certificate_trusted)
Exemple #2
0
    def test_https_tunneling(self):
        # Start a local proxy
        proxy_port = 8000
        p = multiprocessing.Process(target=proxy_worker, args=(proxy_port, ))
        p.start()

        try:
            # Run a scan through the proxy
            tunnel_settings = HttpConnectTunnelingSettings('localhost', proxy_port)
            server_info = ServerConnectivityInfo(hostname=u'www.google.com', http_tunneling_settings=tunnel_settings)

            # Try to connect to the proxy - retry if the proxy subprocess wasn't ready
            proxy_connection_attempts = 0
            while True:
                try:
                    server_info.test_connectivity_to_server()
                    break
                except ServerConnectivityError:
                    if proxy_connection_attempts > 3:
                        raise
                    proxy_connection_attempts += 1


            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info, 'certinfo_basic')

            self.assertTrue(plugin_result.certificate_chain)

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
        finally:
            # Kill the local proxy - unclean
            p.terminate()
    def test_valid_chain_with_ev_cert(self):
        server_info = ServerConnectivityInfo(hostname='www.comodo.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, CertificateInfoScanCommand())

        self.assertTrue(plugin_result.is_leaf_certificate_ev)

        self.assertEqual(len(plugin_result.certificate_chain), 3)
        self.assertEqual(len(plugin_result.verified_certificate_chain), 3)
        self.assertFalse(plugin_result.has_anchor_in_certificate_chain)

        self.assertEqual(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            self.assertTrue(path_validation_result.is_certificate_trusted)

        self.assertEqual(len(plugin_result.path_validation_error_list), 0)
        self.assertEqual(plugin_result.certificate_matches_hostname, True)
        self.assertTrue(plugin_result.is_certificate_chain_order_valid)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_ca_file_bad_file(self):
        server_info = ServerConnectivityInfo(hostname='www.hotmail.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        with self.assertRaises(ValueError):
            plugin.process_task(server_info, CertificateInfoScanCommand(ca_file='doesntexist'))
    def test_invalid_chain(self):
        server_info = ServerConnectivityInfo(hostname='self-signed.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, CertificateInfoScanCommand())

        self.assertIsNone(plugin_result.ocsp_response)
        self.assertEqual(len(plugin_result.certificate_chain), 1)

        self.assertEqual(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            self.assertFalse(path_validation_result.is_certificate_trusted)

        self.assertEqual(plugin_result.certificate_included_scts_count, 0)

        self.assertEqual(len(plugin_result.path_validation_error_list), 0)
        self.assertEqual(plugin_result.certificate_matches_hostname, True)
        self.assertTrue(plugin_result.is_certificate_chain_order_valid)
        self.assertIsNone(plugin_result.has_anchor_in_certificate_chain)
        self.assertIsNone(plugin_result.has_sha1_in_certificate_chain)
        self.assertFalse(plugin_result.verified_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_1000_sans_chain(self):
        # Ensure SSLyze can process a leaf cert with 1000 SANs
        server_info = ServerConnectivityInfo(hostname='1000-sans.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin.process_task(server_info, CertificateInfoScanCommand())
    def _get_plugin_result(self, hostname, command=Tlsv12ScanCommand()):
        server_info = ServerConnectivityInfo(hostname=hostname)
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, command)

        return plugin_result
Exemple #8
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())
    def test_smtp_post_handshake_response(self):
        server_info = ServerConnectivityInfo(hostname='smtp.gmail.com', port=587,
                                             tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP)
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, Tlsv12ScanCommand())

        self._test_plugin_outputs(plugin_result)
    def test_1000_sans_chain(self):
        # Ensure SSLyze can process a leaf cert with 1000 SANs
        server_info = ServerConnectivityInfo(hostname='1000-sans.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        san_list = plugin_result.certificate_chain[0].as_dict['extensions']['X509v3 Subject Alternative Name']['DNS']
        self.assertEquals(len(san_list), 1000)
    def test_sha256_chain(self):
        server_info = ServerConnectivityInfo(hostname='sha256.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertIn('OK - No SHA1-signed certificate in the chain', '\n'.join(plugin_result.as_text()))

        self.assertTrue(plugin_result.as_xml())
Exemple #12
0
    def test_dh_info(self):
        server_info = ServerConnectivityInfo(hostname=u'dh480.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, 'tlsv1')

        self.assertTrue(plugin_result.preferred_cipher)
        self.assertEquals(plugin_result.preferred_cipher.dh_info['GroupSize'], '480')
        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #13
0
    def test_sha1_chain(self):
        server_info = ServerConnectivityInfo(hostname=u'sha1-2017.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertTrue(plugin_result.has_sha1_in_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #14
0
    def test_chain_with_anchor(self):
        server_info = ServerConnectivityInfo(hostname=u'www.verizon.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertTrue(plugin_result.has_anchor_in_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_hsts_enabled(self):
        server_info = ServerConnectivityInfo(hostname='hsts.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = HstsPlugin()
        plugin_result = plugin.process_task(server_info, 'hsts')

        self.assertTrue(plugin_result.hsts_header)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_ccs_injection_good(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCcsInjectionPlugin()
        plugin_result = plugin.process_task(server_info, OpenSslCcsInjectionScanCommand())

        self.assertFalse(plugin_result.is_vulnerable_to_ccs_injection)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_fallback_good(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = FallbackScsvPlugin()
        plugin_result = plugin.process_task(server_info, 'fallback')

        self.assertTrue(plugin_result.supports_fallback_scsv)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_sha1_chain(self):
        server_info = ServerConnectivityInfo(hostname='sha1-intermediate.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, CertificateInfoScanCommand())

        self.assertTrue(plugin_result.has_sha1_in_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_robot_attack_good(self):
        server_info = ServerConnectivityInfo(hostname='www.facebook.com')
        server_info.test_connectivity_to_server()

        plugin = RobotPlugin()
        plugin_result = plugin.process_task(server_info, RobotScanCommand())

        self.assertEqual(plugin_result.robot_result_enum, RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_optional_client_authentication(self):
        for hostname in ['auth.startssl.com', 'xnet-eu.intellij.net']:
            server_info = ServerConnectivityInfo(hostname=hostname)
            server_info.test_connectivity_to_server()
            self.assertEquals(server_info.client_auth_requirement, ClientAuthenticationServerConfigurationEnum.OPTIONAL)

            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info, 'certinfo_basic')

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
    def test_sha1_chain(self):
        server_info = ServerConnectivityInfo(hostname='sha1-2017.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        # TODO: Expose has_sha1 as an attribute
        self.assertIn('INSECURE - SHA1-signed certificate in the chain', '\n'.join(plugin_result.as_text()))

        self.assertTrue(plugin_result.as_xml())
    def test_heartbleed_good(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = HeartbleedPlugin()
        plugin_result = plugin.process_task(server_info, 'heartbleed')

        self.assertFalse(plugin_result.is_vulnerable_to_heartbleed)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_unicode_certificate(self):
        server_info = ServerConnectivityInfo(hostname=u'www.főgáz.hu')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertTrue(plugin_result.certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_compression_disabled(self):
        server_info = ServerConnectivityInfo(hostname=u"www.google.com")
        server_info.test_connectivity_to_server()

        plugin = CompressionPlugin()
        plugin_result = plugin.process_task(server_info, "compression")

        self.assertFalse(plugin_result.compression_name)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #25
0
    def test_international_names(self):
        server_info = ServerConnectivityInfo(hostname=u'www.sociétégénérale.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, u'certinfo_basic')

        self.assertEquals(len(plugin_result.certificate_chain), 3)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_ccs_injection_bad(self):
        with VulnerableOpenSslServer() as server:
            server_info = ServerConnectivityInfo(hostname=server.hostname, ip_address=server.ip_address,
                                                 port=server.port)
            server_info.test_connectivity_to_server()

            plugin = OpenSslCcsInjectionPlugin()
            plugin_result = plugin.process_task(server_info, OpenSslCcsInjectionScanCommand())

        self.assertTrue(plugin_result.is_vulnerable_to_ccs_injection)
        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_renegotiation_good(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = SessionRenegotiationPlugin()
        plugin_result = plugin.process_task(server_info, 'reneg')

        self.assertFalse(plugin_result.accepts_client_renegotiation)
        self.assertTrue(plugin_result.supports_secure_renegotiation)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_smtp_custom_port(self):
        server_info = ServerConnectivityInfo(hostname='smtp.gmail.com', port=587,
                                             tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_SMTP)
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertEquals(len(plugin_result.certificate_chain), 3)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_robot_attack_good_boringssl(self):
        # Validate the bug fix for https://github.com/nabla-c0d3/sslyze/issues/282
        server_info = ServerConnectivityInfo(hostname='guide.duo.com')
        server_info.test_connectivity_to_server()

        plugin = RobotPlugin()
        plugin_result = plugin.process_task(server_info, RobotScanCommand())

        self.assertEqual(plugin_result.robot_result_enum, RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_xmpp_to(self):
        server_info = ServerConnectivityInfo(hostname='talk.google.com',
                                             tls_wrapped_protocol=TlsWrappedProtocolEnum.STARTTLS_XMPP,
                                             xmpp_to_hostname='gmail.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertEquals(len(plugin_result.certificate_chain), 3)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #31
0
    def test_resumption_rate(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = SessionResumptionPlugin()
        plugin_result = plugin.process_task(server_info,
                                            SessionResumptionRateScanCommand())

        self.assertTrue(plugin_result.attempted_resumptions_nb)
        self.assertTrue(plugin_result.successful_resumptions_nb)
        self.assertFalse(plugin_result.errored_resumptions_list)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_sslv2_disabled(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, Sslv20ScanCommand())

        self.assertIsNone(plugin_result.preferred_cipher)
        self.assertFalse(plugin_result.accepted_cipher_list)
        self.assertTrue(plugin_result.rejected_cipher_list)
        self.assertFalse(plugin_result.errored_cipher_list)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #33
0
    def test_not_trusted_by_mozilla_but_trusted_by_microsoft(self):
        server_info = ServerConnectivityInfo(
            hostname='webmail.russia.nasa.gov')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info,
                                            CertificateInfoScanCommand())

        self.assertEqual(plugin_result.successful_trust_store.name,
                         'Microsoft')

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #34
0
    def test_ipv6(self):
        if not self._is_ipv6_available():
            logging.warning(u'WARNING: IPv6 not available - skipping test')
            return

        server_info = ServerConnectivityInfo(
            hostname=u'www.google.com', ip_address=u'2607:f8b0:4005:804::2004')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info,
                                            CertificateInfoScanCommand())

        self.assertEquals(len(plugin_result.certificate_chain), 3)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
def _test_client_renegotiation(
        server_info: ServerConnectivityInfo,
        tls_version_to_use: TlsVersionEnum) -> Tuple[_ScanJobResultEnum, bool]:
    """Check whether the server honors session renegotiation requests.
    """
    ssl_connection = server_info.get_preconfigured_tls_connection(
        override_tls_version=tls_version_to_use,
        should_use_legacy_openssl=True)
    if not isinstance(ssl_connection.ssl_client, LegacySslClient):
        raise RuntimeError("Should never happen")

    try:
        # Perform the SSL handshake
        ssl_connection.connect()

        try:
            # Let's try to renegotiate
            ssl_connection.ssl_client.do_renegotiate()
            accepts_client_renegotiation = True

        # Errors caused by a server rejecting the renegotiation
        except socket.timeout:
            # This is how Netty rejects a renegotiation - https://github.com/nabla-c0d3/sslyze/issues/114
            accepts_client_renegotiation = False
        except ConnectionError:
            accepts_client_renegotiation = False
        except OSError as e:
            # OSError is the parent of all (non-TLS) socket/connection errors so it should be last
            if "Nassl SSL handshake failed" in e.args[0]:
                # Special error returned by nassl
                accepts_client_renegotiation = False
            else:
                raise
        except OpenSSLError as e:
            if "handshake failure" in e.args[0]:
                accepts_client_renegotiation = False
            elif "no renegotiation" in e.args[0]:
                accepts_client_renegotiation = False
            elif "tlsv1 unrecognized name" in e.args[0]:
                # Yahoo's very own way of rejecting a renegotiation
                accepts_client_renegotiation = False
            elif "tlsv1 alert internal error" in e.args[0]:
                # Jetty server: https://github.com/nabla-c0d3/sslyze/issues/290
                accepts_client_renegotiation = False
            elif "decryption failed or bad record mac" in e.args[0]:
                # Some servers such as reddit.com
                accepts_client_renegotiation = False
            elif "sslv3 alert unexpected message" in e.args[0]:
                # traefik https://github.com/nabla-c0d3/sslyze/issues/422
                accepts_client_renegotiation = False

            else:
                raise

    finally:
        ssl_connection.close()

    return _ScanJobResultEnum.ACCEPTS_CLIENT_RENEG, accepts_client_renegotiation
Exemple #36
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 #37
0
    def test_hsts_and_hpkp_disabled(self):
        server_info = ServerConnectivityInfo(hostname='expired.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = HttpHeadersPlugin()
        plugin_result = plugin.process_task(server_info,
                                            HttpHeadersScanCommand())

        self.assertFalse(plugin_result.hsts_header)
        self.assertFalse(plugin_result.hpkp_header)
        self.assertIsNone(plugin_result.is_valid_pin_configured)
        self.assertIsNone(plugin_result.is_backup_pin_configured)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #38
0
 def test_https_tunneling_bad_arguments(self):
     # Ensure that an IP address cannot be specified when using an HTTP proxy for scans
     tunnel_settings = HttpConnectTunnelingSettings('fakedomain', 443)
     with self.assertRaisesRegexp(
             ValueError,
             'Cannot specify both ip_address and http_tunneling_settings'):
         ServerConnectivityInfo(hostname='www.google.com',
                                ip_address='1.2.3.4',
                                http_tunneling_settings=tunnel_settings)
Exemple #39
0
    def test_hpkp_enabled(self):
        server_info = ServerConnectivityInfo(hostname='github.com')
        server_info.test_connectivity_to_server()

        plugin = HttpHeadersPlugin()
        plugin_result = plugin.process_task(server_info,
                                            HttpHeadersScanCommand())

        self.assertTrue(plugin_result.hpkp_header)
        self.assertTrue(plugin_result.is_valid_pin_configured)
        self.assertTrue(plugin_result.is_backup_pin_configured)
        self.assertTrue(plugin_result.verified_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_tls_1_3_cipher_suites(self):
        server_info = ServerConnectivityInfo(hostname='www.cloudflare.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, Tlsv13ScanCommand())

        accepted_cipher_name_list = [
            cipher.name for cipher in plugin_result.accepted_cipher_list
        ]

        # TODO(AD): Update to TLS 1.3 draft 23 and re-enable this test
        return
        self.assertEqual(
            {
                'TLS_CHACHA20_POLY1305_SHA256', 'TLS_AES_256_GCM_SHA384',
                'TLS_AES_128_GCM_SHA256'
            }, set(accepted_cipher_name_list))
Exemple #41
0
    def test_concurrent_scanner(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        # Queue some scan commands that are quick
        concurrent_scanner = ConcurrentScanner()
        concurrent_scanner.queue_scan_command(server_info, CertificateInfoScanCommand())
        concurrent_scanner.queue_scan_command(server_info, SessionRenegotiationScanCommand())
        concurrent_scanner.queue_scan_command(server_info, CompressionScanCommand())

        # Process the results
        nb_results = 0
        for plugin_result in concurrent_scanner.get_results():
            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
            nb_results +=1

        self.assertEquals(nb_results, 3)
Exemple #42
0
    def test_rc4_md5_cipher_suites(self):
        server_info = ServerConnectivityInfo(hostname='rc4-md5.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, Tlsv12ScanCommand())

        accepted_cipher_name_list = [
            cipher.name for cipher in plugin_result.accepted_cipher_list
        ]
        self.assertEqual({'TLS_RSA_WITH_RC4_128_MD5'},
                         set(accepted_cipher_name_list))

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #43
0
    def test_https_tunneling(self):
        # Start a local proxy
        proxy_port = 8000
        p = multiprocessing.Process(target=proxy_worker, args=(proxy_port, ))
        p.start()

        # On Travis CI, the server sometimes is still not ready to accept connections when we get here
        # Wait a bit more to make the test suite less flaky
        time.sleep(0.5)

        try:
            # Run a scan through the proxy
            tunnel_settings = HttpConnectTunnelingSettings(
                'localhost',
                proxy_port,
                basic_auth_user='******',
                basic_auth_password='******')
            server_info = ServerConnectivityInfo(
                hostname='www.google.com',
                http_tunneling_settings=tunnel_settings)

            # Try to connect to the proxy - retry if the proxy subprocess wasn't ready
            proxy_connection_attempts = 0
            while True:
                try:
                    server_info.test_connectivity_to_server()
                    break
                except ServerConnectivityError:
                    if proxy_connection_attempts > 3:
                        raise
                    proxy_connection_attempts += 1

            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info,
                                                CertificateInfoScanCommand())

            self.assertGreaterEqual(len(plugin_result.certificate_chain), 1)

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
        finally:
            # Kill the local proxy - unclean
            p.terminate()
    def test_only_trusted_by_custom_ca_file(self):
        server_info = ServerConnectivityInfo(hostname='self-signed.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        ca_file_path = os.path.join(os.path.dirname(__file__), '..', 'utils',
                                    'self-signed.badssl.com.pem')
        plugin_result = plugin.process_task(
            server_info, CertificateInfoScanCommand(ca_file=ca_file_path))

        self.assertEqual(plugin_result.successful_trust_store.name,
                         'Custom --ca_file')
        self.assertTrue(plugin_result.verified_certificate_chain)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #45
0
    def test_sslv2_enabled(self):
        try:
            with VulnerableOpenSslServer() as server:
                server_info = ServerConnectivityInfo(
                    hostname=server.hostname,
                    ip_address=server.ip_address,
                    port=server.port)
                server_info.test_connectivity_to_server()

                plugin = OpenSslCipherSuitesPlugin()
                plugin_result = plugin.process_task(server_info,
                                                    Sslv20ScanCommand())
        except NotOnLinux64Error:
            # The test suite only has the vulnerable OpenSSL version compiled for Linux 64 bits
            logging.warning(
                'WARNING: Not on Linux - skipping test_sslv2_enabled() test')
            return

        # The embedded server does not have a preference
        self.assertFalse(plugin_result.preferred_cipher)

        accepted_cipher_name_list = [
            cipher.name for cipher in plugin_result.accepted_cipher_list
        ]
        self.assertEqual(
            {
                'SSL_CK_RC4_128_EXPORT40_WITH_MD5',
                'SSL_CK_IDEA_128_CBC_WITH_MD5',
                'SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5',
                'SSL_CK_DES_192_EDE3_CBC_WITH_MD5',
                'SSL_CK_DES_192_EDE3_CBC_WITH_MD5', 'SSL_CK_RC4_128_WITH_MD5',
                'SSL_CK_RC2_128_CBC_WITH_MD5', 'SSL_CK_DES_64_CBC_WITH_MD5'
            }, set(accepted_cipher_name_list))

        self.assertTrue(plugin_result.accepted_cipher_list)
        self.assertFalse(plugin_result.rejected_cipher_list)
        self.assertFalse(plugin_result.errored_cipher_list)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_valid_chain_with_ocsp_stapling_and_must_staple(self):
        server_info = ServerConnectivityInfo(hostname='www.scotthelme.co.uk')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info,
                                            CertificateInfoScanCommand())

        self.assertTrue(plugin_result.ocsp_response)
        self.assertEqual(plugin_result.ocsp_response_status,
                         OcspResponseStatusEnum.SUCCESSFUL)
        self.assertTrue(plugin_result.is_ocsp_response_trusted)
        self.assertTrue(plugin_result.certificate_has_must_staple_extension)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
    def test_null_cipher_suites(self):
        server_info = ServerConnectivityInfo(hostname=u'null.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, Tlsv12ScanCommand())

        accepted_cipher_name_list = [cipher.name for cipher in plugin_result.accepted_cipher_list]
        self.assertEquals({'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'},
                          set(accepted_cipher_name_list))

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #48
0
def scan(hostname, port):
    # Setup the servers to scan and ensure they are reachable
    try:
        server_info = ServerConnectivityInfo(hostname=hostname, port=port)
        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))

    # Get the list of available plugins
    sslyze_plugins = PluginsFinder()

    # Create a process pool to run scanning commands concurrently
    plugins_process_pool = PluginsProcessPool(sslyze_plugins)

    # Queue some scan commands; the commands are same as what is described in the SSLyze CLI --help text.
    # print '\nQueuing some commands...'
    plugins_process_pool.queue_plugin_task(server_info, 'sslv3')
    plugins_process_pool.queue_plugin_task(server_info, 'certinfo_basic')
    plugins_process_pool.queue_plugin_task(server_info, 'tlsv1')
    plugins_process_pool.queue_plugin_task(server_info, 'tlsv1_1')
    plugins_process_pool.queue_plugin_task(server_info, 'tlsv1_2')
    plugins_process_pool.queue_plugin_task(server_info, 'sslv2')

    result = {}

    result['server_info'] = server_info.__dict__
    for res in plugins_process_pool.get_results():
        if res.plugin_command in ['sslv2','sslv3','tlsv1','tlsv1_1','tlsv1_2']:
            supported = False
            if len(res.accepted_cipher_list) > 0:
                supported = True
            acc_ciphers = []
            rej_ciphers = []
            for cipher in res.accepted_cipher_list:
                acc_ciphers.append(cipher.name)

            result[res.plugin_command] = {'supported': supported, 'accepted_ciphers': acc_ciphers}
        elif res.plugin_command == 'certinfo_basic':
            result['certinfo'] = parse_certinfo(res.__dict__)

    return result
Exemple #49
0
def ssl_scan(self, target):
  print("Running SSL Scan")
  try:
    server_info = ServerConnectivityInfo(hostname=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 #50
0
    def test_robot_attack_good(self):
        # Validate the bug fix for https://github.com/nabla-c0d3/sslyze/issues/282
        server_info = ServerConnectivityInfo(hostname='guide.duo.com')
        server_info.test_connectivity_to_server()

        plugin = RobotPlugin()
        plugin_result = plugin.process_task(server_info, RobotScanCommand())

        # On Travis CI we sometimes get inconsistent results
        if IS_RUNNING_ON_TRAVIS:
            self.assertIn(plugin_result.robot_result_enum, [
                RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE,
                RobotScanResultEnum.UNKNOWN_INCONSISTENT_RESULTS
            ])
        else:
            self.assertEqual(plugin_result.robot_result_enum,
                             RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
    def test_heartbleed_bad(self):
        try:
            with VulnerableOpenSslServer() as server:
                server_info = ServerConnectivityInfo(hostname=server.hostname, ip_address=server.ip_address,
                                                     port=server.port)
                server_info.test_connectivity_to_server()

                plugin = HeartbleedPlugin()
                plugin_result = plugin.process_task(server_info, HeartbleedScanCommand())
        except NotOnLinux64Error:
            # The test suite only has the vulnerable OpenSSL version compiled for Linux 64 bits
            logging.warning('WARNING: Not on Linux - skipping test_heartbleed_bad() test')
            return

        self.assertTrue(plugin_result.is_vulnerable_to_heartbleed)
        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))
Exemple #52
0
    def test_fallback_bad(self):
        try:
            server = VulnerableOpenSslServer(port=8010)
        except NotOnLinux64Error:
            # The test suite only has the vulnerable OpenSSL version compiled for Linux 64 bits
            logging.warning('WARNING: Not on Linux - skipping test_fallback_bad() test')
            return
        server.start()

        server_info = ServerConnectivityInfo(hostname=server.hostname, ip_address=server.ip_address,  port=server.port)
        server_info.test_connectivity_to_server()

        plugin = FallbackScsvPlugin()
        plugin_result = plugin.process_task(server_info, FallbackScsvScanCommand())
        server.terminate()

        self.assertFalse(plugin_result.supports_fallback_scsv)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #53
0
    def test_tlsv1_0_enabled(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        plugin_result = plugin.process_task(server_info, 'tlsv1')

        self.assertTrue(plugin_result.preferred_cipher)
        accepted_cipher_name_list = [cipher.name for cipher in plugin_result.accepted_cipher_list]
        self.assertEquals({'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_AES_256_CBC_SHA',
                           'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA',
                           'TLS_RSA_WITH_3DES_EDE_CBC_SHA'},
                          set(accepted_cipher_name_list))

        self.assertTrue(plugin_result.accepted_cipher_list)
        self.assertTrue(plugin_result.rejected_cipher_list)
        self.assertFalse(plugin_result.errored_cipher_list)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
Exemple #54
0
def _send_robot_payload(
    server_info: ServerConnectivityInfo,
    tls_version_to_use: TlsVersionEnum,
    rsa_cipher_string: str,
    robot_payload_enum: RobotPmsPaddingPayloadEnum,
    robot_should_finish_handshake: bool,
    rsa_modulus: int,
    rsa_exponent: int,
) -> str:
    # Do a handshake which each record and keep track of what the server returned
    ssl_connection = server_info.get_preconfigured_tls_connection(override_tls_version=tls_version_to_use)

    # Replace nassl.sslClient.do_handshake() with a ROBOT checking SSL handshake so that all the SSLyze
    # options (startTLS, proxy, etc.) still work
    ssl_connection.ssl_client.do_handshake = types.MethodType(  # type: ignore
        do_handshake_with_robot, ssl_connection.ssl_client
    )
    ssl_connection.ssl_client.set_cipher_list(rsa_cipher_string)

    # Compute the  payload
    tls_parser_tls_version: tls_parser.tls_version.TlsVersionEnum
    if tls_version_to_use == TlsVersionEnum.SSL_3_0:
        tls_parser_tls_version = tls_parser.tls_version.TlsVersionEnum.SSLV3
    elif tls_version_to_use == TlsVersionEnum.TLS_1_0:
        tls_parser_tls_version = tls_parser.tls_version.TlsVersionEnum.TLSV1
    elif tls_version_to_use == TlsVersionEnum.TLS_1_1:
        tls_parser_tls_version = tls_parser.tls_version.TlsVersionEnum.TLSV1_1
    elif tls_version_to_use == TlsVersionEnum.TLS_1_2:
        tls_parser_tls_version = tls_parser.tls_version.TlsVersionEnum.TLSV1_2
    else:
        raise ValueError("Should never happen")

    cke_payload = _RobotTlsRecordPayloads.get_client_key_exchange_record(
        robot_payload_enum, tls_parser_tls_version, rsa_modulus, rsa_exponent
    )

    # H4ck: we need to pass some arguments to the handshake but there is no simple way to do it; we use an attribute
    ssl_connection.ssl_client._robot_cke_record = cke_payload  # type: ignore
    ssl_connection.ssl_client._robot_should_finish_handshake = robot_should_finish_handshake  # type: ignore

    server_response = ""
    try:
        # Start the SSL handshake
        ssl_connection.connect()
    except ServerResponseToRobot as e:
        # Should always be thrown
        server_response = e.server_response
    except socket.timeout:
        # https://github.com/nabla-c0d3/sslyze/issues/361
        server_response = "Connection timed out"
    finally:
        ssl_connection.close()

    return server_response
Exemple #55
0
    def test_starttls(self):
        for hostname, protocol in [
            ('imap.comcast.net', TlsWrappedProtocolEnum.STARTTLS_IMAP),
            ('pop.comcast.net', TlsWrappedProtocolEnum.STARTTLS_POP3),
            ('ldap.uchicago.edu', TlsWrappedProtocolEnum.STARTTLS_LDAP),
            ('jabber.org', TlsWrappedProtocolEnum.STARTTLS_XMPP_SERVER),
                # Some Heroku Postgres instance I created
            ('ec2-54-75-226-17.eu-west-1.compute.amazonaws.com',
             TlsWrappedProtocolEnum.STARTTLS_POSTGRES)
        ]:
            server_info = ServerConnectivityInfo(hostname=hostname,
                                                 tls_wrapped_protocol=protocol)
            server_info.test_connectivity_to_server()

            plugin = CertificateInfoPlugin()
            plugin_result = plugin.process_task(server_info,
                                                CertificateInfoScanCommand())

            self.assertTrue(plugin_result.as_text())
            self.assertTrue(plugin_result.as_xml())
    def test_valid_chain(self):
        server_info = ServerConnectivityInfo(hostname=u'www.hotmail.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertTrue(plugin_result.ocsp_response)
        self.assertTrue(plugin_result.is_ocsp_response_trusted)
        self.assertTrue(plugin_result.is_leaf_certificate_ev)

        self.assertEquals(len(plugin_result.certificate_chain), 2)
        self.assertEquals(len(plugin_result.verified_certificate_chain), 3)
        self.assertFalse(plugin_result.has_anchor_in_certificate_chain)

        self.assertEquals(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            self.assertTrue(path_validation_result.is_certificate_trusted)

        self.assertEquals(len(plugin_result.path_validation_error_list), 0)
        self.assertEquals(plugin_result.hostname_validation_result,
                          X509_NAME_MATCHES_SAN)
        self.assertTrue(plugin_result.is_certificate_chain_order_valid)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Test the --ca_path option
        plugin_result = plugin.process_task(
            server_info, 'certinfo_basic', {
                'ca_file':
                os.path.join(os.path.dirname(__file__), 'utils',
                             'wildcard-self-signed.pem')
            })

        self.assertEquals(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            if path_validation_result.trust_store.name == 'Custom --ca_file':
                self.assertFalse(path_validation_result.is_certificate_trusted)
            else:
                self.assertTrue(path_validation_result.is_certificate_trusted)
Exemple #57
0
    def test_invalid_chain(self):
        server_info = ServerConnectivityInfo(hostname='self-signed.badssl.com')
        server_info.test_connectivity_to_server()

        plugin = CertificateInfoPlugin()
        plugin_result = plugin.process_task(server_info, 'certinfo_basic')

        self.assertIsNone(plugin_result.ocsp_response)
        self.assertEquals(len(plugin_result.certificate_chain), 1)

        self.assertEquals(len(plugin_result.path_validation_result_list), 5)
        for path_validation_result in plugin_result.path_validation_result_list:
            self.assertFalse(path_validation_result.is_certificate_trusted)


        self.assertEquals(len(plugin_result.path_validation_error_list), 0)
        self.assertEquals(plugin_result.hostname_validation_result, X509_NAME_MATCHES_SAN)
        self.assertTrue(plugin_result.is_certificate_chain_order_valid)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())
def _test_client_renegotiation(
        server_info: ServerConnectivityInfo,
        tls_version_to_use: OpenSslVersionEnum
) -> Tuple[_ScanJobResultEnum, bool]:
    """Check whether the server honors session renegotiation requests.
    """
    ssl_connection = server_info.get_preconfigured_tls_connection(
        override_tls_version=tls_version_to_use,
        should_use_legacy_openssl=True)
    if not isinstance(ssl_connection.ssl_client, LegacySslClient):
        raise RuntimeError("Should never happen")

    try:
        # Perform the SSL handshake
        ssl_connection.connect()

        try:
            # Let's try to renegotiate
            ssl_connection.ssl_client.do_renegotiate()
            accepts_client_renegotiation = True

        # Errors caused by a server rejecting the renegotiation
        except socket.timeout:
            # This is how Netty rejects a renegotiation - https://github.com/nabla-c0d3/sslyze/issues/114
            accepts_client_renegotiation = False
        except socket.error as e:
            if "connection was forcibly closed" in str(e.args):
                accepts_client_renegotiation = False
            elif "reset by peer" in str(e.args):
                accepts_client_renegotiation = False
            elif "Nassl SSL handshake failed" in str(e.args):
                accepts_client_renegotiation = False
            else:
                raise
        except OpenSSLError as e:
            if "handshake failure" in str(e.args):
                accepts_client_renegotiation = False
            elif "no renegotiation" in str(e.args):
                accepts_client_renegotiation = False
            elif "tlsv1 unrecognized name" in str(e.args):
                # Yahoo's very own way of rejecting a renegotiation
                accepts_client_renegotiation = False
            elif "tlsv1 alert internal error" in str(e.args):
                # Jetty server: https://github.com/nabla-c0d3/sslyze/issues/290
                accepts_client_renegotiation = False
            else:
                raise

    finally:
        ssl_connection.close()

    return _ScanJobResultEnum.ACCEPTS_CLIENT_RENEG, accepts_client_renegotiation
Exemple #59
0
def connect_with_cipher_suite(
    server_connectivity_info: ServerConnectivityInfo,
    tls_version: TlsVersionEnum, cipher_suite: CipherSuite
) -> Union[CipherSuiteAcceptedByServer, CipherSuiteRejectedByServer]:
    """Initiates a SSL handshake with the server using the SSL version and the cipher suite specified.
    """
    requires_legacy_openssl = True
    if tls_version == TlsVersionEnum.TLS_1_2:
        # For TLS 1.2, we need to pick the right version of OpenSSL depending on which cipher suite
        requires_legacy_openssl = WorkaroundForTls12ForCipherSuites.requires_legacy_openssl(
            cipher_suite.openssl_name)
    elif tls_version == TlsVersionEnum.TLS_1_3:
        requires_legacy_openssl = False

    ssl_connection = server_connectivity_info.get_preconfigured_tls_connection(
        override_tls_version=tls_version,
        should_use_legacy_openssl=requires_legacy_openssl)
    _set_cipher_suite_string(tls_version, cipher_suite.openssl_name,
                             ssl_connection.ssl_client)

    ephemeral_key = None
    try:
        # Perform the SSL handshake
        ssl_connection.connect()
        ephemeral_key = ssl_connection.ssl_client.get_ephemeral_key()

    except ServerTlsConfigurationNotSupported:
        # SSLyze rejected the handshake because the server's DH config was too insecure; this means the
        # cipher suite is actually supported
        pass

    except ClientCertificateRequested:
        # When the handshake failed due to ClientCertificateRequested
        ephemeral_key = ssl_connection.ssl_client.get_ephemeral_key()
        pass

    except ServerRejectedTlsHandshake as e:
        return CipherSuiteRejectedByServer(cipher_suite=cipher_suite,
                                           error_message=e.error_message)

    except TlsHandshakeTimedOut as e:
        # Sometimes triggered by servers that don't support (at all) a specific version of TLS
        # Amazon Cloudfront does that with TLS 1.3
        # There's no easy way to differentiate this error from a network glitch/timeout
        return CipherSuiteRejectedByServer(cipher_suite=cipher_suite,
                                           error_message=e.error_message)

    finally:
        ssl_connection.close()

    return CipherSuiteAcceptedByServer(cipher_suite=cipher_suite,
                                       ephemeral_key=ephemeral_key)
Exemple #60
0
    def test_tlsv1_2_enabled(self):
        server_info = ServerConnectivityInfo(hostname='www.google.com')
        server_info.test_connectivity_to_server()

        plugin = OpenSslCipherSuitesPlugin()
        # Also do full HTTP connections
        plugin_result = plugin.process_task(server_info,
                                            Tlsv12ScanCommand(http_get=True))

        self.assertTrue(plugin_result.preferred_cipher)
        self.assertTrue(plugin_result.accepted_cipher_list)
        accepted_cipher_name_list = [
            cipher.name for cipher in plugin_result.accepted_cipher_list
        ]

        self.assertEqual(
            {
                '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'
            }, set(accepted_cipher_name_list))

        self.assertTrue(plugin_result.rejected_cipher_list)
        self.assertFalse(plugin_result.errored_cipher_list)

        self.assertTrue(plugin_result.as_text())
        self.assertTrue(plugin_result.as_xml())

        # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue
        self.assertTrue(pickle.dumps(plugin_result))