def __init__(self, host, port, proxy=None): """ :param proxy: (dict) Default: None; Set this one up if you wish to use a proxy to hit your target host. E.g.: { "server":"someproxy.com", "port":6000, "user":"******", "pass":"******" } """ self.host = host self.port = port self.policies = { "ssl2.0": { "allowed": False, "command": Sslv20ScanCommand() }, "ssl3.0": { "allowed": False, "command": Sslv30ScanCommand() }, "tls1.0": { "allowed": False, "command": Tlsv10ScanCommand() }, "tls1.1": { "allowed": False, "command": Tlsv11ScanCommand() }, "tls1.2": { "allowed": True, "command": Tlsv12ScanCommand() }, "tls1.3": { "allowed": True, "command": Tlsv13ScanCommand() }, } if not isinstance(host, str) or not isinstance(port, int): raise TypeError('EncryptionCheck class not properly initialized.') # Proxy setup if proxy is not None: if not ("server" in proxy and "port" in proxy): raise ValueError('Invalid proxy settings detected.') proxy_server = proxy['server'] proxy_port = proxy['port'] proxy_user = proxy.get('user', None) proxy_pass = proxy.get('pass', None) tunnel_settings = HttpConnectTunnelingSettings( proxy_server, proxy_port, basic_auth_user=proxy_user, basic_auth_password=proxy_pass) else: tunnel_settings = None self.tunnel = tunnel_settings self.proxy = proxy
def scan_serial(scanner, server_info, data, options): logging.debug("\tRunning scans in serial.") logging.debug("\t\tSSLv2 scan.") sslv2 = scanner.run_scan_command(server_info, Sslv20ScanCommand()) logging.debug("\t\tSSLv3 scan.") sslv3 = scanner.run_scan_command(server_info, Sslv30ScanCommand()) logging.debug("\t\tTLSv1.0 scan.") tlsv1 = scanner.run_scan_command(server_info, Tlsv10ScanCommand()) logging.debug("\t\tTLSv1.1 scan.") tlsv1_1 = scanner.run_scan_command(server_info, Tlsv11ScanCommand()) logging.debug("\t\tTLSv1.2 scan.") tlsv1_2 = scanner.run_scan_command(server_info, Tlsv12ScanCommand()) logging.debug("\t\tTLSv1.3 scan.") tlsv1_3 = scanner.run_scan_command(server_info, Tlsv13ScanCommand()) certs = None if options.get("sslyze_certs", True) is True: try: logging.debug("\t\tCertificate information scan.") certs = scanner.run_scan_command(server_info, CertificateInfoScanCommand()) # Let generic exceptions bubble up. except idna.core.InvalidCodepoint: logging.warn(utils.format_last_exception()) data['errors'].append("Invalid certificate/OCSP for this domain.") certs = None else: certs = None logging.debug("\tDone scanning.") return sslv2, sslv3, tlsv1, tlsv1_1, tlsv1_2, tlsv1_3, certs
def test_succeeds_when_client_auth_failed(self): # Given a server that requires client authentication try: with VulnerableOpenSslServer( client_auth_config= ClientAuthenticationServerConfigurationEnum.REQUIRED ) as server: # And the client does NOT provide a client certificate server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # OpenSslCipherSuitesPlugin works even when a client cert was not supplied plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Sslv30ScanCommand()) except NotOnLinux64Error: logging.warning('WARNING: Not on Linux - skipping test') return self.assertTrue(plugin_result.accepted_cipher_list) self.assertTrue(plugin_result.as_text()) self.assertTrue(plugin_result.as_xml())
def test_sslv3_enabled(self): with VulnerableOpenSslServer() as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Sslv30ScanCommand()) # 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( { 'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA', 'TLS_RSA_WITH_3DES_EDE_CBC_SHA', 'TLS_DH_anon_WITH_AES_128_CBC_SHA', 'TLS_ECDH_anon_WITH_AES_128_CBC_SHA', 'TLS_DH_anon_WITH_SEED_CBC_SHA', 'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5', 'TLS_ECDHE_RSA_WITH_NULL_SHA', 'TLS_ECDHE_RSA_WITH_RC4_128_SHA', 'TLS_DH_anon_WITH_AES_256_CBC_SHA', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA', 'TLS_ECDH_anon_WITH_RC4_128_SHA', 'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA', 'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA', 'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5', 'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA', 'TLS_ECDH_anon_WITH_NULL_SHA', 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA', 'TLS_RSA_WITH_RC4_128_SHA', 'TLS_RSA_EXPORT_WITH_RC4_40_MD5', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'TLS_RSA_WITH_NULL_MD5', 'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA', 'TLS_DH_anon_WITH_DES_CBC_SHA', 'TLS_RSA_WITH_SEED_CBC_SHA', 'TLS_RSA_WITH_DES_CBC_SHA', 'TLS_ECDH_anon_WITH_AES_256_CBC_SHA', 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA', 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA', 'TLS_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_RC4_128_MD5', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_NULL_SHA', 'TLS_RSA_WITH_IDEA_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA', 'TLS_DH_anon_WITH_RC4_128_MD5' }, 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()) # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue self.assertTrue(pickle.dumps(plugin_result))
def scan_serial(scanner, server_info, data, options): errors = 0 def run_scan(scan_type, command, errors): if (errors >= 2): return None, errors logging.debug("\t\t{} scan.".format(scan_type)) result = None try: result = scanner.run_scan_command(server_info, command) except Exception as err: logging.warning("{}: Error during {} scan.".format( server_info.hostname, scan_type)) logging.debug("{}: Exception during {} scan: {}".format( server_info.hostname, scan_type, err)) errors = errors + 1 return result, errors logging.debug("\tRunning scans in serial.") sslv2, errors = run_scan("SSLv2", Sslv20ScanCommand(), errors) sslv3, errors = run_scan("SSLv3", Sslv30ScanCommand(), errors) tlsv1, errors = run_scan("TLSv1.0", Tlsv10ScanCommand(), errors) tlsv1_1, errors = run_scan("TLSv1.1", Tlsv11ScanCommand(), errors) tlsv1_2, errors = run_scan("TLSv1.2", Tlsv12ScanCommand(), errors) tlsv1_3, errors = run_scan("TLSv1.3", Tlsv13ScanCommand(), errors) certs = None if errors < 2 and options.get("sslyze_certs", True) is True: try: logging.debug("\t\tCertificate information scan.") certs = scanner.run_scan_command( server_info, CertificateInfoScanCommand(ca_file=CA_FILE)) except idna.core.InvalidCodepoint: logging.warning(utils.format_last_exception()) data['errors'].append("Invalid certificate/OCSP for this domain.") certs = None except Exception as err: logging.warning( "{}: Error during certificate information scan.".format( server_info.hostname)) logging.debug( "{}: Exception during certificate information scan: {}".format( server_info.hostname, err)) else: certs = None reneg = None if options.get("sslyze_reneg", True) is True: reneg, errors = run_scan("Renegotiation", SessionRenegotiationScanCommand(), errors) else: reneg = None logging.debug("\tDone scanning.") return sslv2, sslv3, tlsv1, tlsv1_1, tlsv1_2, tlsv1_3, certs, reneg
def test_sslv3_disabled(self): server_info = ServerConnectivityInfo(hostname=u'www.google.com') server_info.test_connectivity_to_server() plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Sslv30ScanCommand()) 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())
def check_tcp_service_for_ssl_protocol_cipher_support( self, org_uuid=None, service_uuid=None, ssl_protocol=None, scan_uuid=None ): """ Check to see what ciphers this TCP service supports for the given SSL protocol. :param org_uuid: The UUID of the organization to collect information on behalf of. :param service_uuid: The UUID of the network service to check for SSL support. :param ssl_protocol: The SSL protocol to check :param scan_uuid: The UUID of the network service scan that this SSL version check is associated with. :return: None """ ip_address, port, protocol = self.get_endpoint_information(service_uuid) server_info = ServerConnectivityInfo(hostname=ip_address, ip_address=ip_address, port=port) synchronous_scanner = SynchronousScanner() command = None if ssl_protocol == 'PROTOCOL_TLSv1' or ssl_protocol == 'PROTOCOL_TLS': command = Tlsv10ScanCommand() elif ssl_protocol == 'PROTOCOL_TLSv1_1': command = Tlsv11ScanCommand() elif ssl_protocol == 'PROTOCOL_TLSv1_2': command = Tlsv12ScanCommand() elif ssl_protocol == 'PROTOCOL_SSLv23': command = Sslv20ScanCommand() elif ssl_protocol == 'PROTOCOL_SSLv3': command = Sslv30ScanCommand() else: raise ValueError( "Not sure how to run scan command for SSL protocol of %s." % (ssl_protocol,) ) cipher_scan_result = synchronous_scanner.run_scan_command(server_info, command) ssl_support_record = SslSupportModel.from_database_model_uuid( uuid=scan_uuid, db_session=self.db_session, ssl_version=ssl_protocol, supported=True, ) ssl_support_record.accepted_ciphers = [ cipher.name for cipher in cipher_scan_result.accepted_cipher_list ] ssl_support_record.rejected_ciphers = [ cipher.name for cipher in cipher_scan_result.rejected_cipher_list ] ssl_support_record.errored_ciphers = [ cipher.name for cipher in cipher_scan_result.errored_cipher_list ] if cipher_scan_result.preferred_cipher is not None: ssl_support_record.preferred_cipher = cipher_scan_result.preferred_cipher.name else: ssl_support_record.preferred_cipher = None ssl_support_record.save(org_uuid)
def concurrent_scan(site_info): # Setup the server to scan and ensure it is online/reachable server_info = server_connectivity_tester(site_info.name) if server_info: synchronous_scanner = SynchronousScanner() cert_info_plugin = CertificateInfoPlugin() plugin_result = cert_info_plugin.process_task( server_info, CertificateInfoScanCommand()) #not plugin_result.verified_certificate_chain or #not plugin_result.leaf_certificate_subject_matches_hostname some sites' certs CN is with "www." so the result here is false if plugin_result.verified_certificate_chain and site_info.name not in str( plugin_result.verified_certificate_chain[0].subject): site_info.cert_trusted = "False" print("not trusted: " + site_info.name) print(plugin_result.__dict__) # elif not plugin_result.verified_certificate_chain: # site_info.cert_trusted = "False" # print("not trusted: " + site_info.name) # print(plugin_result.__dict__) else: site_info.cert_trusted = "True" scan_result1 = synchronous_scanner.run_scan_command( server_info, Sslv20ScanCommand()) if len(scan_result1.accepted_cipher_list) > 0: site_info.sslv2 = "True" scan_result2 = synchronous_scanner.run_scan_command( server_info, Sslv30ScanCommand()) if len(scan_result2.accepted_cipher_list) > 0: site_info.sslv3 = "True" scan_result3 = synchronous_scanner.run_scan_command( server_info, Tlsv10ScanCommand()) if len(scan_result3.accepted_cipher_list) > 0: site_info.tlsv1 = "True" scan_result4 = synchronous_scanner.run_scan_command( server_info, Tlsv11ScanCommand()) if len(scan_result4.accepted_cipher_list) > 0: site_info.tlsv11 = "True" scan_result5 = synchronous_scanner.run_scan_command( server_info, Tlsv12ScanCommand()) if len(scan_result5.accepted_cipher_list) > 0: site_info.tlsv12 = "True" scan_result6 = synchronous_scanner.run_scan_command( server_info, Tlsv13ScanCommand()) if len(scan_result6.accepted_cipher_list) > 0: site_info.tlsv13 = "True" recheck_cert(site_info)
def test_sslv3_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, Sslv30ScanCommand()) except NotOnLinux64Error: # The test suite only has the vulnerable OpenSSL version compiled for Linux 64 bits logging.warning('WARNING: Not on Linux - skipping test_sslv3_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({'TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA', 'TLS_RSA_WITH_3DES_EDE_CBC_SHA', 'TLS_DH_anon_WITH_AES_128_CBC_SHA', 'TLS_ECDH_anon_WITH_AES_128_CBC_SHA', 'TLS_DH_anon_WITH_SEED_CBC_SHA', 'TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5', 'TLS_ECDHE_RSA_WITH_NULL_SHA', 'TLS_ECDHE_RSA_WITH_RC4_128_SHA', 'TLS_DH_anon_WITH_AES_256_CBC_SHA', 'TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA', 'TLS_ECDH_anon_WITH_RC4_128_SHA', 'TLS_DH_anon_WITH_3DES_EDE_CBC_SHA', 'TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA', 'TLS_DH_anon_EXPORT_WITH_RC4_40_MD5', 'TLS_RSA_EXPORT_WITH_DES40_CBC_SHA', 'TLS_ECDH_anon_WITH_NULL_SHA', 'TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA', 'TLS_RSA_WITH_RC4_128_SHA', 'TLS_RSA_EXPORT_WITH_RC4_40_MD5', 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA', 'TLS_RSA_WITH_NULL_MD5', 'TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA', 'TLS_DH_anon_WITH_DES_CBC_SHA', 'TLS_RSA_WITH_SEED_CBC_SHA', 'TLS_RSA_WITH_DES_CBC_SHA', 'TLS_ECDH_anon_WITH_AES_256_CBC_SHA', 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA', 'TLS_RSA_WITH_CAMELLIA_256_CBC_SHA', 'TLS_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_RC4_128_MD5', 'TLS_RSA_WITH_CAMELLIA_128_CBC_SHA', 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA', 'TLS_RSA_WITH_NULL_SHA', 'TLS_RSA_WITH_IDEA_CBC_SHA', 'TLS_RSA_WITH_AES_128_CBC_SHA', 'TLS_DH_anon_WITH_RC4_128_MD5'}, 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()) # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue self.assertTrue(pickle.dumps(plugin_result))
def test_sslv3_disabled(self): server_info = ServerConnectivityInfo(hostname='www.google.com') server_info.test_connectivity_to_server() plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Sslv30ScanCommand()) 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))
def test_sslv3_disabled(self): server_test = ServerConnectivityTester(hostname='www.google.com') server_info = server_test.perform() plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Sslv30ScanCommand()) assert plugin_result.preferred_cipher is None assert not plugin_result.accepted_cipher_list assert plugin_result.rejected_cipher_list assert not plugin_result.errored_cipher_list assert plugin_result.as_text() assert plugin_result.as_xml() # Ensure the results are pickable so the ConcurrentScanner can receive them via a Queue assert pickle.dumps(plugin_result)
def run_cipher_suite_commands(self, server_info, synchronous_scanner): cipher_scan_results = {} commands = [ Sslv20ScanCommand(), Sslv30ScanCommand(), Tlsv10ScanCommand(), Tlsv11ScanCommand(), Tlsv12ScanCommand(), Tlsv13ScanCommand() ] for command in commands: scan_result = synchronous_scanner.run_scan_command( server_info, command) ciphers = [] for cipher in scan_result.accepted_cipher_list: ciphers.append({ "name": cipher.name, "key_size": cipher.key_size, "is_anonymous": cipher.is_anonymous }) cipher_scan_results[scan_result.scan_command.get_title()] = ciphers #print('ciphers obtained for ',server_info) return cipher_scan_results
def get_supported_tls_cipher_suites(hostname): server_info = get_ssl_server_info(hostname) concurrent_scanner = ConcurrentScanner() concurrent_scanner.queue_scan_command(server_info, Sslv20ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv10ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv11ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv13ScanCommand()) concurrent_scanner.queue_scan_command(server_info, FallbackScsvScanCommand()) for scan_result in concurrent_scanner.get_results(): # A scan command can fail (as a bug); it is returned as a PluginRaisedExceptionResult if isinstance(scan_result, PluginRaisedExceptionScanResult): raise RuntimeError( f'Scan command failed: {scan_result.scan_command.get_title()}') if isinstance(scan_result.scan_command, Sslv20ScanCommand): accepted_ssl2 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_ssl2 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_ssl2 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, Sslv30ScanCommand): accepted_ssl3 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_ssl3 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_ssl3 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, Tlsv10ScanCommand): accepted_tls10 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_tls10 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_tls10 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, Tlsv11ScanCommand): accepted_tls11 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_tls11 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_tls11 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, Tlsv12ScanCommand): accepted_tls12 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_tls12 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_tls12 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, Tlsv13ScanCommand): accepted_tls13 = [ cipher.name for cipher in scan_result.accepted_cipher_list ] denied_tls13 = [ cipher.name for cipher in scan_result.rejected_cipher_list ] errored_tls13 = [ cipher.name for cipher in scan_result.errored_cipher_list ] if isinstance(scan_result.scan_command, FallbackScsvScanCommand): supports_fallback_scsv = scan_result.supports_fallback_scsv return { 'accepted_ssl2': accepted_ssl2, 'denied_ssl2': denied_ssl2, 'errored_ssl2': errored_ssl2, 'accepted_ssl3': accepted_ssl3, 'denied_ssl3': denied_ssl3, 'errored_ssl3': errored_ssl3, 'accepted_tls10': accepted_tls10, 'denied_tls10': denied_tls10, 'errored_tls10': errored_tls10, 'accepted_tls11': accepted_tls11, 'denied_tls11': denied_tls11, 'errored_tls11': errored_tls11, 'accepted_tls12': accepted_tls12, 'denied_tls12': denied_tls12, 'errored_tls12': errored_tls12, 'accepted_tls13': accepted_tls13, 'denied_tls13': denied_tls13, 'errored_tls13': errored_tls13, 'supports_fallback_scsv': supports_fallback_scsv }
from sslyze.server_connectivity_tester import ServerConnectivityTester, \ ServerConnectivityError, ConnectionToServerTimedOut from sslyze.ssl_settings import TlsWrappedProtocolEnum from sslyze.plugins.openssl_cipher_suites_plugin import Sslv20ScanCommand, \ Sslv30ScanCommand, Tlsv10ScanCommand, Tlsv11ScanCommand, \ Tlsv12ScanCommand, Tlsv13ScanCommand from sslyze.synchronous_scanner import SynchronousScanner from . import results from .errors import ConnectionError # Policy prohibits the use of SSL 2.0/3.0 and TLS 1.0 ciphersuites = { "policy": [ Sslv20ScanCommand(), Sslv30ScanCommand(), Tlsv10ScanCommand(), Tlsv11ScanCommand() ], "full": [ Sslv20ScanCommand(), Sslv30ScanCommand(), Tlsv10ScanCommand(), Tlsv11ScanCommand(), Tlsv12ScanCommand(), Tlsv13ScanCommand() ] } # sslyze config SynchronousScanner.DEFAULT_NETWORK_RETRIES = 1
hostname, e.error_msg)) # Example 1: Run one scan command synchronously to list the server's TLS 1.0 cipher suites print(u'\nRunning one scan command synchronously...') synchronous_scanner = SynchronousScanner() command = Tlsv10ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) # Example 2: Run multiple scan commands concurrently. It is of course much faster than the SynchronousScanner concurrent_scanner = ConcurrentScanner() # Queue some scan commands print(u'\nQueuing some commands...') concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand()) concurrent_scanner.queue_scan_command(server_info, SessionRenegotiationScanCommand()) concurrent_scanner.queue_scan_command(server_info, CertificateInfoScanCommand()) # Process the results reneg_result = None print(u'\nProcessing results...') for scan_result in concurrent_scanner.get_results(): # All scan results have the corresponding scan_command and server_info as an attribute print(u'\nReceived scan result for {} on host {}'.format( scan_result.scan_command.__class__.__name__, scan_result.server_info.hostname)) # Sometimes a scan command can unexpectedly fail (as a bug); it is returned as a PluginRaisedExceptionResult
def scan_parallel(scanner, server_info, data, options): logging.debug("\tRunning scans in parallel.") def queue(command): try: return scanner.queue_scan_command(server_info, command) except OSError as err: text = ("OSError - likely too many processes and open files.") data['errors'].append(text) logging.warn("%s\n%s" % (text, utils.format_last_exception())) return None, None, None, None, None, None, None except Exception as err: text = ("Unknown exception queueing sslyze command.\n%s" % utils.format_last_exception()) data['errors'].append(text) logging.warn(text) return None, None, None, None, None, None, None # Initialize commands and result containers sslv2, sslv3, tlsv1, tlsv1_1, tlsv1_2, tlsv1_3, certs = None, None, None, None, None, None # Queue them all up queue(Sslv20ScanCommand()) queue(Sslv30ScanCommand()) queue(Tlsv10ScanCommand()) queue(Tlsv11ScanCommand()) queue(Tlsv12ScanCommand()) queue(Tlsv13ScanCommand()) if options.get("sslyze-certs", True) is True: queue(CertificateInfoScanCommand()) # Reassign them back to predictable places after they're all done was_error = False for result in scanner.get_results(): try: if isinstance(result, PluginRaisedExceptionScanResult): error = ("Scan command failed: %s" % result.as_text()) logging.warn(error) data['errors'].append(error) return None, None, None, None, None, None, None if type(result.scan_command) == Sslv20ScanCommand: sslv2 = result elif type(result.scan_command) == Sslv30ScanCommand: sslv3 = result elif type(result.scan_command) == Tlsv10ScanCommand: tlsv1 = result elif type(result.scan_command) == Tlsv11ScanCommand: tlsv1_1 = result elif type(result.scan_command) == Tlsv12ScanCommand: tlsv1_2 = result elif type(result.scan_command) == Tlsv13ScanCommand: tlsv1_3 = result elif type(result.scan_command) == CertificateInfoScanCommand: certs = result else: error = "Couldn't match scan result with command! %s" % result logging.warn("\t%s" % error) data['errors'].append(error) was_error = True except Exception as err: was_error = True text = ("Exception inside async scanner result processing.\n%s" % utils.format_last_exception()) data['errors'].append(text) logging.warn("\t%s" % text) # There was an error during async processing. if was_error: return None, None, None, None, None, None, None logging.debug("\tDone scanning.") return sslv2, sslv3, tlsv1, tlsv1_1, tlsv1_2, tlsv1_3, certs
def handler(event, context): """Handler for all Lambda events The event parameter is a dictionary containing the following keys and value types: * hostname - A string containing the hostname to be scanned. For example, "dhs.gov". * port - An integer specifying the port to be scanned. If omitted then the default value of 443 is used. * timeout - An integer denoting the number of seconds to wait when creating a connection. If omitted then the default value of 5 is used. * starttls_smtp - A boolean value denoting whether to try to use STARTTLS after connecting to an SMTP server. This option should be True is connecting to an SMTP host and otherwise False. If omitted then the default value of False is used. * scan_tlsv10 - A boolean value denoting whether to scan for TLS version 1.0 ciphers. If omitted then the default value of False is used. * scan_tlsv11 - A boolean value denoting whether to scan for TLS version 1.1 ciphers. If omitted then the default value of False is used. * scan_tlsv12 - A boolean value denoting whether to scan for TLS version 1.2 ciphers. If omitted then the default value of False is used. * scan_tlsv13 - A boolean value denoting whether to scan for TLS version 1.3 ciphers. If omitted then the default value of False is used. * scan_sslv20 - A boolean value denoting whether to scan for SSL version 2.0 ciphers. If omitted then the default value of False is used. * scan_sslv30 - A boolean value denoting whether to scan for SSL version 3.0 ciphers. If omitted then the default value of False is used. * scan_cert_info - A boolean value denoting whether to return certificate information. If omitted then the default value of False is used. Parameters ---------- event : dict A dictionary containing the scan parameters, as described above. context : LambdaContext The context for the Lambda function. See the corresponding AWS documentation for more details: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html Returns ------- OrderedDict An OrderedDict specifying the fields of the trustymail.domain.Domain object resulting from the scan activity. """ logging.info('AWS Event was: {}'.format(event)) # Extract some variables from the event dictionary hostname = event['hostname'] port = event.get('port', 443) timeout = event.get('timeout', 5) starttls_smtp = event.get('starttls_smtp', False) scan_tlsv10 = event.get('scan_tlsv10', False) scan_tlsv11 = event.get('scan_tlsv11', False) scan_tlsv12 = event.get('scan_tlsv12', False) scan_tlsv13 = event.get('scan_tlsv13', False) scan_sslv20 = event.get('scan_sslv20', False) scan_sslv30 = event.get('scan_sslv30', False) scan_cert_info = event.get('scan_cert_info', False) # Initialize sslyze protocol = TlsWrappedProtocolEnum.PLAIN_TLS if starttls_smtp: protocol = TlsWrappedProtocolEnum.STARTTLS_SMTP try: server_tester = ServerConnectivityTester(hostname=hostname, port=port, tls_wrapped_protocol=protocol) server_info = server_tester.perform(network_timeout=timeout) except ServerConnectivityError: logging.error('Unable to connect to {}:{}'.format(hostname, port), exc_info=True, stack_info=True) return None except Exception: logging.error('Unable to connect to {}:{}'.format(hostname, port), exc_info=True, stack_info=True) return None scanner = SynchronousScanner(network_timeout=timeout) # Perform the scans if scan_tlsv10: logging.debug('Performing TLSv1.0 scan') tlsv10 = scanner.run_scan_command(server_info, Tlsv10ScanCommand()) logging.debug('tlsv10 = {}'.format(tlsv10)) if scan_tlsv11: logging.debug('Performing TLSv1.1 scan') tlsv11 = scanner.run_scan_command(server_info, Tlsv11ScanCommand()) logging.debug('tlsv11 = {}'.format(tlsv11)) if scan_tlsv12: logging.debug('Performing TLSv1.2 scan') tlsv12 = scanner.run_scan_command(server_info, Tlsv12ScanCommand()) logging.debug('tlsv12 = {}'.format(tlsv12)) if scan_tlsv13: logging.debug('Performing TLSv1.3 scan') tlsv13 = scanner.run_scan_command(server_info, Tlsv13ScanCommand()) logging.debug('tlsv13 = {}'.format(tlsv13)) if scan_sslv20: logging.debug('Performing SSLv2 scan') sslv20 = scanner.run_scan_command(server_info, Sslv20ScanCommand()) logging.debug('sslv20 = {}'.format(sslv20)) if scan_sslv30: logging.debug('Performing SSLv3 scan') sslv30 = scanner.run_scan_command(server_info, Sslv30ScanCommand()) logging.debug('sslv30 = {}'.format(sslv30)) if scan_cert_info: logging.debug('Performing certificate scan') cert_info = scanner.run_scan_command(server_info, CertificateInfoScanCommand()) logging.debug('cert_info = {}'.format(cert_info)) return None
def check( hostname_user_input): try: print(u'hostname_user_input: '+hostname_user_input) # Strip http(s) m = re.search('^(https?://)?(.*?)(/.*)?$', hostname_user_input) if m.group(2): hostname_user_input = m.group(2) else: raise RuntimeError(u'Please provide non-empty host name!') server_tester = ServerConnectivityTester(hostname_user_input) server_info = server_tester.perform(network_timeout=10) # Could not establish an SSL connection to the server except ServerConnectivityError as e: raise RuntimeError(u'Error when connecting to {}: {}!'.format(hostname_user_input, e.error_message)) # No SSL used except IOError as e: raise RuntimeError(u'Protocol does not use SSL/TLS!') # If the call to test_connectivity_to_server() returns successfully, the server_info is then ready to be used for scanning the server. # The ConcurrentScanner uses a pool of processes to run ScanCommands concurrently. # It is very fast when scanning a large number of servers, and it has a dispatching mechanism to avoid DOS-ing a single server against which multiple ScanCommand are run at the same time. # The commands can be queued using the queue_scan_command() method, and the results can later be retrieved using the get_results() method: # Ref: https://nabla-c0d3.github.io/sslyze/documentation/running-scan-commands.html concurrent_scanner = ConcurrentScanner() # Put scans in queue - Put desired scans here # ROBOT concurrent_scanner.queue_scan_command(server_info, RobotScanCommand()) # Heartbleed concurrent_scanner.queue_scan_command(server_info, HeartbleedScanCommand()) # Detecting deprecated/weak ciphers concurrent_scanner.queue_scan_command(server_info, Sslv20ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv10ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv11ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv13ScanCommand()) concurrent_scanner.queue_scan_command(server_info, CompressionScanCommand()) # Process the results robot_txt = 'Scan could not be executed' heartbleed_txt = 'Scan could not be executed' drown_txt = 'Scan could not be executed' poodle_txt = 'Scan could not be executed' beast_txt = 'Scan could not be executed' compression_text = 'Scan could not be executed' lucky_text = 'Scan could not be executed' potential_weak_ciphers = set() print(u'\nProcessing results...') for scan_result in concurrent_scanner.get_results(): # Sometimes a scan command can unexpectedly fail (as a bug); it is returned as a PluginRaisedExceptionResult if isinstance(scan_result, PluginRaisedExceptionScanResult): raise RuntimeError(u'Scan command failed: Scan could not be executed!') continue # Each scan result has attributes with the information you're looking for, specific to each scan command # All these attributes are documented within each scan command's module if isinstance(scan_result.scan_command, RobotScanCommand): result_enum = scan_result.robot_result_enum if result_enum == RobotScanResultEnum.VULNERABLE_STRONG_ORACLE: robot_txt = 'Vulnerable - Strong oracle, a real attack is possible' elif result_enum == RobotScanResultEnum.VULNERABLE_WEAK_ORACLE: robot_txt = 'Vulnerable - Weak oracle, the attack would take too long' elif result_enum == RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE: robot_txt = 'Not vulnerable' elif result_enum == RobotScanResultEnum.NOT_VULNERABLE_RSA_NOT_SUPPORTED: robot_txt = 'Not vulnerable, RSA cipher suites not supported' elif result_enum == RobotScanResultEnum.UNKNOWN_INCONSISTENT_RESULTS: robot_txt = 'Unknown - Received inconsistent results' # Process CRIME elif isinstance(scan_result.scan_command, CompressionScanCommand): compression_text = "Vulnerable" result_compression = scan_result.compression_name if "None" == str(result_compression): compression_text = "Not vulnerable" # Process Heartbleed elif isinstance(scan_result.scan_command, HeartbleedScanCommand): result_heartbleed = scan_result.is_vulnerable_to_heartbleed heartbleed_txt = 'Not vulnerable' if result_heartbleed == True: heartbleed_txt = 'Vulnerable' # Process POODLE elif isinstance(scan_result.scan_command, Sslv30ScanCommand): poodle_txt = 'Not vulnerable' for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) if 'CBC' in cipher.name: poodle_txt = 'Vulnerable' beast_txt = "Not mitigated on server-side" # Process DROWN (a server is vulnerable to DROWN if it allows SSLv2 connections) Ref = https://drownattack.com/ elif isinstance(scan_result.scan_command, Sslv20ScanCommand): drown_txt = 'Not vulnerable' for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) drown_txt = 'Vulnerable' if 'CBC' in cipher.name: beast_txt = "Not mitigated on server-side" # Collect deprecated/weak ciphers elif isinstance(scan_result.scan_command, Tlsv10ScanCommand): beast_txt = "Not vulnerable" for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) if 'CBC' in cipher.name: beast_txt = "Not mitigated on server-side" ## Check for tls version and ciphers not sufficient to detect lucky13 vulnerability elif isinstance(scan_result.scan_command, Tlsv11ScanCommand): #if lucky_text != 'Vulnerable': # lucky_text = 'Not vulnerable' for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) # if 'CBC' in cipher.name: # lucky_text = 'Vulnerable' elif isinstance(scan_result.scan_command, Tlsv12ScanCommand): #if lucky_text != 'Vulnerable': # lucky_text = 'Not vulnerable' for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) # if 'CBC' in cipher.name: # lucky_text = 'Vulnerable' elif isinstance(scan_result.scan_command, Tlsv13ScanCommand): for cipher in scan_result.accepted_cipher_list: potential_weak_ciphers.add(cipher.name) # Process weak ciphers weak_ciphers = getWeakCiphers(potential_weak_ciphers) print("potential_weak_ciphers:") print(potential_weak_ciphers) print("\nweak_ciphers:") print(weak_ciphers) res = collections.OrderedDict() res["BEAST"] = str(beast_txt) res["CRIME"] = str(compression_text) res["DROWN"] = str(drown_txt) res["HEARTBLEED"] = str(heartbleed_txt) res["POODLE"] = str(poodle_txt) res["ROBOT"] = str(robot_txt) res["WEAKCIPHERS"] = 'Not vulnerable' if len(weak_ciphers) == 0 else '\n'.join(str(s) for s in weak_ciphers) #res["LUCKY13"] = str(lucky_text) details = getCertiDetails(hostname_user_input, potential_weak_ciphers) return (res, details)
def test_endpoint_for_tls_settings(httpEndpoint): # Setup the server to scan and ensure it is online/reachable hostname = httpEndpoint.get_endpoint_url() try: server_info = ServerConnectivityInfo(hostname=hostname) 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)) # Run one scan command synchronously to list the server's SSL 2.0 cipher suites print(u'\nRunning one scan command synchronously...') synchronous_scanner = SynchronousScanner() command = Sslv20ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) print('All the TLS 1.0 Ciphers: \n') print('\n Accepted Cipher List: \n') accepted_cipher_suite_list = [] for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) accepted_cipher_suite_list.append(cipher.name) if len(scan_result.accepted_cipher_list) > 0: httpEndpoint.set_ssl_two_zero_enbled(True) httpEndpoint.set_ssl_two_zero_supported_cipher_list( accepted_cipher_suite_list) else: httpEndpoint.set_ssl_two_zero_enbled(False) #if scan_result.preferred_cipher is not None: # print('\n Preffered Cipher =: \n') # print(u' {}'.format(scan_result.preferred_cipher.name)) # Run one scan command synchronously to list the server's SSL 3.0 cipher suites print(u'\nRunning one scan command synchronously...') synchronous_scanner = SynchronousScanner() command = Sslv30ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) print('All the TLS 1.0 Ciphers: \n') print('\n Accepted Cipher List: \n') accepted_cipher_suite_list = [] for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) accepted_cipher_suite_list.append(cipher.name) if len(scan_result.accepted_cipher_list) > 0: httpEndpoint.set_ssl_three_zero_enbled(True) httpEndpoint.set_ssl_three_zero_supported_cipher_list( accepted_cipher_suite_list) else: httpEndpoint.set_ssl_three_zero_enbled(False) #if scan_result.preferred_cipher is not None: # print('\n Preffered Cipher =: \n') # print(u' {}'.format(scan_result.preferred_cipher.name)) # Run one scan command synchronously to list the server's TLS 1.0 cipher suites print(u'\nRunning one scan command synchronously...') synchronous_scanner = SynchronousScanner() command = Tlsv10ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) print('All the TLS 1.0 Ciphers: \n') print('\n Accepted Cipher List: \n') accepted_cipher_suite_list = [] for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) accepted_cipher_suite_list.append(cipher.name) if len(scan_result.accepted_cipher_list) > 0: httpEndpoint.set_tls_one_zero_enbled(True) httpEndpoint.set_tls_one_zero_supported_cipher_list( accepted_cipher_suite_list) else: httpEndpoint.set_tls_one_zero_enbled(False) #if scan_result.preferred_cipher is not None: # print('\n Preffered Cipher =: \n') # print(u' {}'.format(scan_result.preferred_cipher.name)) command = Tlsv11ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) accepted_cipher_suite_list = [] for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) accepted_cipher_suite_list.append(cipher.name) if len(scan_result.accepted_cipher_list) > 0: httpEndpoint.set_tls_one_one_enbled(True) httpEndpoint.set_tls_one_one_supported_cipher_list( accepted_cipher_suite_list) else: httpEndpoint.set_tls_one_one_enbled(False) #if scan_result.preferred_cipher is not None: # print('\n Preffered Cipher =: \n') # print(u' {}'.format(scan_result.preferred_cipher.name)) command = Tlsv12ScanCommand() scan_result = synchronous_scanner.run_scan_command(server_info, command) accepted_cipher_suite_list = [] for cipher in scan_result.accepted_cipher_list: print(u' {}'.format(cipher.name)) accepted_cipher_suite_list.append(cipher.name) if len(scan_result.accepted_cipher_list) > 0: httpEndpoint.set_tls_one_two_enbled(True) httpEndpoint.set_tls_one_two_supported_cipher_list( accepted_cipher_suite_list) else: httpEndpoint.set_tls_one_two_enbled(False) #if scan_result.preferred_cipher is not None: # print('\n Preffered Cipher =: \n') # print(u' {}'.format(scan_result.preferred_cipher.name)) return httpEndpoint
def attack_host(results, host_name): print("Running forward_secrecy_attack.py host name: {}".format(host_name)) expected_tls1_2_suites = ['TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'] try: server_tester = ServerConnectivityTester( hostname=host_name, port=443, tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS) server_info = server_tester.perform() except ServerConnectivityError as e: results.append(AttackResult('10.13', host_name, False, e)) return synchronous_scanner = SynchronousScanner() tls1_3_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv13ScanCommand()) tls1_3_accepted_ciphers = tls1_3_scan_result.accepted_cipher_list tls1_2_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv12ScanCommand()) tls1_2_accepted_ciphers = tls1_2_scan_result.accepted_cipher_list tls1_2_accepted_cipher_names = cipher_list_to_string_list(tls1_2_accepted_ciphers) tls1_1_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv11ScanCommand()) tls1_1_accepted_ciphers = tls1_1_scan_result.accepted_cipher_list tls1_0_scan_result = synchronous_scanner.run_scan_command(server_info, Tlsv10ScanCommand()) tls1_0_accepted_ciphers = tls1_0_scan_result.accepted_cipher_list ssl3_0_scan_result = synchronous_scanner.run_scan_command(server_info, Sslv30ScanCommand()) ssl3_0_accepted_ciphers = ssl3_0_scan_result.accepted_cipher_list ssl2_0_scan_result = synchronous_scanner.run_scan_command(server_info, Sslv20ScanCommand()) ssl2_0_accepted_ciphers = ssl2_0_scan_result.accepted_cipher_list # Determine result result = list_equal(tls1_2_accepted_cipher_names, expected_tls1_2_suites) and \ len(tls1_3_accepted_ciphers) == 0 and \ len(tls1_1_accepted_ciphers) == 0 and \ len(tls1_0_accepted_ciphers) == 0 and \ len(ssl3_0_accepted_ciphers) == 0 and \ len(ssl3_0_accepted_ciphers) == 0 # Build details details = '' if len(tls1_3_accepted_ciphers) != 0: details += 'TLS 1.3 should not be supported ' if not list_equal(tls1_2_accepted_cipher_names, expected_tls1_2_suites): details += ' TLS 1.2 supporting ' + str(tls1_2_accepted_cipher_names) \ + ', should only be ' + str(expected_tls1_2_suites) + ' ' if len(tls1_1_accepted_ciphers) != 0: details += 'TLS 1.1 should not be supported ' if len(tls1_0_accepted_ciphers) != 0: details += 'TLS 1.0 should not be supported ' if len(ssl3_0_accepted_ciphers) != 0: details += 'SSL 3.0 should not be supported ' if len(ssl2_0_accepted_ciphers) != 0: details += 'SSL 2.0 should not be supported ' if details == '': details = 'Cipher suites are correct' results.append(AttackResult('forward_secrecy', host_name, result, details)) # attack_host([], 'google.co.uk')
def main(): if len(sys.argv) < 2: print("Error: please provide a domain") exit(-1) hostname = sys.argv[1] """ Testing connectivity to the server """ try: server_info = ServerConnectivityInfo(hostname) server_info.test_connectivity_to_server() print( "[*] Connection established. \n[.] Starting tests on {} \n".format( hostname)) except ServerConnectivityError as e: raise RuntimeError("Error when connecting to {}: {}".format( hostname, e.error_msg)) scanner = SynchronousScanner() """ Creating an output file """ output = open("/root/PycharmProjects/SSL-TLS-Tool/output/" + hostname, "w") output.write("##############################################\n") output.write("Output result for host: {}\n".format(hostname)) output.write("Start {}\n".format(datetime.datetime.now())) output.write("##############################################\n\n") """ Certificate: """ scan_result = scanner.run_scan_command(server_info, CertificateInfoScanCommand()) for e in scan_result.as_text(): output.write(e + "\n") """ Protocols and Ciphers Suits: """ run_command(scanner, server_info, Tlsv10ScanCommand(), output) run_command(scanner, server_info, Tlsv11ScanCommand(), output) run_command(scanner, server_info, Tlsv12ScanCommand(), output) run_command(scanner, server_info, Sslv20ScanCommand(), output) run_command(scanner, server_info, Sslv30ScanCommand(), output) """ Testing vulnerabilities: """ run_command(scanner, server_info, DrownScanCommand(), output) run_command(scanner, server_info, PoodleSslScanCommand(), output) run_command(scanner, server_info, HeartbleedScanCommand(), output) run_command(scanner, server_info, OpenSslCcsInjectionScanCommand(), output) run_command(scanner, server_info, CompressionScanCommand(), output) run_command(scanner, server_info, FallbackScsvScanCommand(), output) run_command(scanner, server_info, SessionRenegotiationScanCommand(), output) run_command(scanner, server_info, SessionResumptionSupportScanCommand(), output) run_command(scanner, server_info, SessionResumptionRateScanCommand(), output) """ Closing """ output.close() print("\n[*] Check output file for more details") print("[*] Test completed!")
def test_ssl_basic(self, hostname, port=443): ''' Uses the `ServerConnectivityTester` functionality of SSlyze to perform a basic test. Port defaults to 443 unless provided otherwise hostname is mandatory | test ssl basic | hostname | port (optional | ''' try: tester = ServerConnectivityTester(hostname=hostname, port=port) server_info = tester.perform() scanner = ConcurrentScanner() # scanner.queue_scan_command(info, certificate_info_plugin.CertificateInfoScanCommand()) scanner.queue_scan_command(server_info, Sslv20ScanCommand()) scanner.queue_scan_command(server_info, Sslv30ScanCommand()) scanner.queue_scan_command(server_info, Tlsv10ScanCommand()) scanner.queue_scan_command(server_info, Tlsv10ScanCommand()) scanner.queue_scan_command(server_info, Tlsv11ScanCommand()) scanner.queue_scan_command(server_info, Tlsv12ScanCommand()) scanner.queue_scan_command(server_info, HeartbleedScanCommand()) scanner.queue_scan_command(server_info, RobotScanCommand()) # scanner.queue_scan_command(server_info, CertificateInfoScanCommand()) for scan_result in scanner.get_results(): # logger.info("Scan result for: {} on hostname: {}".format(scan_result.scan_command.__class__.__name__, scan_result.server_info.hostname)) if isinstance(scan_result, PluginRaisedExceptionScanResult): raise Exception("Scan Command Failed: {}".format( scan_result.as_text())) if isinstance(scan_result.scan_command, Sslv20ScanCommand): if scan_result.accepted_cipher_list: logger.warn("SSLv2 ciphersuites accepted") for suite in scan_result.accepted_cipher_list: logger.info("\t{}".format(suite.name)) else: logger.info("SSLv2 ciphersuites not accepted") if isinstance(scan_result.scan_command, Sslv30ScanCommand): if scan_result.accepted_cipher_list: logger.warn("SSLv3 Cipher Suites accepted") for suite in scan_result.accepted_cipher_list: logger.info("\t{}".format(suite.name)) else: logger.info("SSLv3 ciphersuites not accepted") if isinstance(scan_result.scan_command, Tlsv10ScanCommand): if scan_result.accepted_cipher_list: logger.warn("TLSv1 Cipher Suites accepted") for suite in scan_result.accepted_cipher_list: logger.info("\t{}".format(suite.name)) else: logger.info("TLSv1 ciphersuites not accepted") if isinstance(scan_result.scan_command, Tlsv11ScanCommand): if scan_result.accepted_cipher_list: logger.info("TLSv1.1 Cipher Suites accepted") for suite in scan_result.accepted_cipher_list: logger.info("\t{}".format(suite.name)) else: logger.info("TLSv1.1 ciphersuites not accepted") if isinstance(scan_result.scan_command, Tlsv12ScanCommand): if scan_result.accepted_cipher_list: logger.info("TLSv1.2 Cipher Suites accepted") for suite in scan_result.accepted_cipher_list: logger.info("\t{}".format(suite.name)) else: logger.info("TLSv1.2 ciphersuites not accepted") if isinstance(scan_result.scan_command, HeartbleedScanCommand): if scan_result.is_vulnerable_to_heartbleed: logger.warn( "Server TLS implementation is vulnerable to Heartbleed" ) else: logger.info( "Server TLS Implementation not vulnerable to Heartbleed" ) if isinstance(scan_result.scan_command, RobotScanCommand): logger.info("Test for ROBOT Vulnerability") if scan_result.robot_result_enum.NOT_VULNERABLE_NO_ORACLE: logger.info( "\tNot Vulnerable: The server supports RSA cipher suites but does not act as an oracle" ) elif scan_result.robot_result_enum.VULNERABLE_WEAK_ORACLE: logger.warn( "\tVulnerable: The server is vulnerable but the attack would take too long" ) elif scan_result.robot_result_enum.VULNERABLE_STRONG_ORACLE: logger.warn( "\tVulnerable: The server is vulnerable and real attacks are feasible" ) elif scan_result.robot_result_enum.NOT_VULNERABLE_RSA_NOT_SUPPORTED: logger.info( "\tNot Vulnerable: The server does not supports RSA cipher suites" ) else: logger.info( "\tUnable to determine if implementation is vulnerable" ) # if isinstance(scan_result.scan_command, CertificateInfoScanCommand): # logger.info(u'Server Certificate CN: {}'.format( # dict(scan_result.certificate_chain[0])[u'subject'][u'commonName'] # )) except ServerConnectivityError as e: logger.error('Error when trying to connect to {}: {}'.format( e.server_info.hostname, e.error_message))
def process_task(self, analysis_info): # Init target_info = OTG_CRYPST_001.__get_info_from_target_url(analysis_info.get_target()) vulnerabilities = [] if not target_info['https']: vulnerabilities.append('The target is not using HTTPS') else: server_info = ServerConnectivityInfo(hostname=target_info['server'], port=target_info['port']) server_info.test_connectivity_to_server() # -- Check # Checks heartbleed (CVE-2014-0160) plugin = HeartbleedPlugin() heartbleed_vulnerability = plugin.process_task(server_info, 'heartbleed').is_vulnerable_to_heartbleed if heartbleed_vulnerability: vulnerabilities.append('Heartbleed vulnerability (CVE-2014-0160)') # Checks OpenSSL CCS injection vulnerability (CVE-2014-0224) plugin = OpenSslCcsInjectionPlugin() css_injection_vulnerability = plugin.process_task(server_info, 'openssl_ccs').is_vulnerable_to_ccs_injection if css_injection_vulnerability: vulnerabilities.append('OpenSSL CCS injection vulnerability (CVE-2014-0224)') # LOGJAM vulnerability (CVE-2015-4000) plugin = OpenSslCipherSuitesPlugin() ciphersuites_result = plugin.process_task(server_info, Tlsv10ScanCommand()) re_dhe = re.compile('[A-Z]*_DHE_[A-Z]*_EXPORT') accepted_cipher_name_list = [cipher.name for cipher in ciphersuites_result.accepted_cipher_list] logjam_vulnerability = False if not len(re_dhe.findall((",").join(accepted_cipher_name_list))) == 0: logjam_vulnerability = True try: if ciphersuites_result.preferred_cipher and \ not ciphersuites_result.preferred_cipher.dh_info['GroupSize'] >= 2048: logjam_vulnerability = True except Exception: logjam_vulnerability = True if logjam_vulnerability: vulnerabilities.append('LOGJAM vulnerability (CVE-2015-4000)') # CRIME (CVE-2012-4929) & BREACH (CVE-2013-3587) plugin = CompressionPlugin() compression_name_result = plugin.process_task(server_info, 'compression').compression_name if compression_name_result is not None: vulnerabilities.append('CRIME (CVE-2012-4929) & BREACH (CVE-2013-3587)') # SSL client renogotaiation plugin = SessionRenegotiationPlugin() reneg_result = plugin.process_task(server_info, 'reneg') if reneg_result.accepts_client_renegotiation: vulnerabilities.append('SSL client renogotaiation supported') # POODLE (CVE-2014-3566), BEAST (CVE-2011-3389) and weak SSL protocols weak_protocols = [] for protocol in [Sslv20ScanCommand(), Sslv30ScanCommand(), Tlsv10ScanCommand()]: plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, protocol) if len(plugin_result.accepted_cipher_list) > 0: weak_protocols.append(protocol.get_cli_argument()) if len(weak_protocols) > 0: vulnerabilities.append('POODLE (CVE-2014-3566), BEAST (CVE-2011-3389) and weak SSL protocols ' + str(json.dumps(weak_protocols))) # Minimum cipher strength requirements (>= 128 bits) not satisfied weak_ciphers = set() for protocol in [Tlsv11ScanCommand(), Tlsv12ScanCommand()]: plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, protocol) accepted_cipher_key_dict = {cipher.name: cipher.key_size for cipher in plugin_result.accepted_cipher_list} for cipher in accepted_cipher_key_dict: if accepted_cipher_key_dict[cipher] < 128: weak_ciphers.add(cipher) if len(weak_ciphers) > 0: vulnerabilities.append('Minimum cipher strength requirements (>= 128 bits) not satisfied ' + str(list(weak_ciphers))) # SSL/TLS perfect cipher suites not_perfect_ciphers = set() for protocol in [Tlsv11ScanCommand(), Tlsv12ScanCommand()]: plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, protocol) accepted_cipher_key_arr = [cipher.name for cipher in plugin_result.accepted_cipher_list] for accepted_cipher_key in accepted_cipher_key_arr: if accepted_cipher_key not in not_perfect_ciphers and \ accepted_cipher_key not in Analysis.PERFECT_CIPHER_SUITES: not_perfect_ciphers.add(accepted_cipher_key) if len(not_perfect_ciphers) > 0: vulnerabilities.append('SSL/TLS perfect cipher suites not satisfied ' + str(list(not_perfect_ciphers))) # Certificate key is > 1024 bits plugin = CertificateInfoPlugin() plugin_certificate_result = plugin.process_task(server_info, CertificateInfoScanCommand()) key_size = plugin_certificate_result.certificate_chain[0].as_dict['subjectPublicKeyInfo']['publicKeySize'] if int(key_size) <= 1024: vulnerabilities.append('Certificate key is <= 1024 bits') # X509 Hostname validation plugin = CertificateInfoPlugin() plugin_certificate_result = plugin.process_task(server_info, CertificateInfoScanCommand()) if plugin_certificate_result.hostname_validation_result is HostnameValidationResultEnum.NAME_DOES_NOT_MATCH: vulnerabilities.append('X509 Hostname validation failed') # Check wildcard certificates plugin = CertificateInfoPlugin() plugin_certificate_result = plugin.process_task(server_info, CertificateInfoScanCommand()) # Common Name cn = plugin_certificate_result.certificate_chain[0].as_dict['subject']['commonName'] if '*' in cn: vulnerabilities.append('Wildcards are not allowed in CN') # Subject Alternative Name try: san_list = plugin_certificate_result.certificate_chain[0].as_dict['extensions']\ ['X509v3 Subject Alternative Name']['DNS'] san_with_asterisk = [san for san in san_list if '*' in san] if len(san_with_asterisk) > 0: vulnerabilities.append('Wildcards are not allowed in SAN') except KeyError: # Nothing to do because X509v3 Subject Alternative Name not found pass # Check SAN with IP address plugin = CertificateInfoPlugin() plugin_certificate_result = plugin.process_task(server_info, CertificateInfoScanCommand()) # Subject Alternative Name try: san_list = plugin_certificate_result.certificate_chain[0].as_dict['extensions']\ ['X509v3 Subject Alternative Name']['IP'] ip_match_list = [ip for ip in san_list if OTG_CRYPST_001.__isIPv4(ip)] if len(ip_match_list) > 0: vulnerabilities.append('IPs are not allowed in SAN') except KeyError: # Nothing to do because X509v3 Subject Alternative Name not found pass # Prepare output if len(vulnerabilities) > 0: msg = str(vulnerabilities) status = OwaspStepBase.status.error.value else: msg = 'Passed.' status = OwaspStepBase.status.passed.value # Return return OwaspStepBase.StepResult(title=OTG_CRYPST_001.get_interface().title, description=OTG_CRYPST_001.get_interface().description, msg=msg, status=status)
def concurrent_scanner(hn): # Setup the server to scan and ensure it is online/reachable server_info = server_connectivity_tester(hn) if server_info is 'error': return # Run multiple scan commands concurrently. concurrent_scanner = ConcurrentScanner() # Queue some scan commands print('\nQueuing some commands...') concurrent_scanner.queue_scan_command(server_info, CertificateInfoScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv13ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv11ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Tlsv10ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand()) concurrent_scanner.queue_scan_command(server_info, Sslv20ScanCommand()) # Process the results print('\nProcessing results...') for scan_result in concurrent_scanner.get_results(): # これからスキャンする情報(コマンド)を表示 print( f'\nReceived result for "{scan_result.scan_command.get_title()}" ' f'on {scan_result.server_info.hostname}') # A scan command can fail (as a bug); it is returned as a PluginRaisedExceptionResult # スキャンコマンドのエラー if isinstance(scan_result, PluginRaisedExceptionScanResult): ##raise RuntimeError(f'Scan command failed: {scan_result.scan_command.get_title()}') print( f'Scan command failed: {scan_result.scan_command.get_title()}') continue # Each scan result has attributes with the information yo're looking for # All these attributes are documented within each scan command's module if isinstance(scan_result.scan_command, Sslv20ScanCommand): # Cipher suitesリスト(ssl2.0)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, SSL20) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) if isinstance(scan_result.scan_command, Sslv30ScanCommand): # Cipher suitesリスト(ssl3.0)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, SSL30) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) if isinstance(scan_result.scan_command, Tlsv10ScanCommand): # Cipher suitesリスト(tls1.0)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, TLS10) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) if isinstance(scan_result.scan_command, Tlsv11ScanCommand): # Cipher suitesリスト(tls1.1)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, TLS11) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) if isinstance(scan_result.scan_command, Tlsv12ScanCommand): # Cipher suitesリスト(tls1.2)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, TLS12) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) if isinstance(scan_result.scan_command, Tlsv13ScanCommand): # Cipher suitesリスト(tls1.3)を表示 for cipher in scan_result.accepted_cipher_list: print(f' {cipher.name}') sql = "INSERT INTO CipherSuite(hostname, TLS13) values(?, ?)" data = [(hn, cipher.name)] cur.executemany(sql, data) elif isinstance(scan_result.scan_command, CertificateInfoScanCommand): # Print the Common Names within the verified certificate chain # 証明書情報を表示 if not scan_result.verified_certificate_chain: print('Error: certificate chain is not trusted!') cur.execute("INSERT INTO CertInfo(hostname) values(?)", [hn]) else: print('Certificate chain common names:') for cert in scan_result.verified_certificate_chain: cert_common_names_check = cert.subject.get_attributes_for_oid( NameOID.COMMON_NAME) if cert_common_names_check: cert_common_names = cert_common_names_check[0].value else: cert_common_names = '' cert_publickey = CertificateUtils.get_public_key_type(cert) cert_keysize = cert.public_key().key_size cert_sig_algo = cert.signature_algorithm_oid cert_leaf_ev = scan_result.leaf_certificate_is_ev # leafのみ # Policy type 判定未完成↓ """ try: cert_policy = cert.extensions.get_extension_for_oid(ExtensionOID.CERTIFICATE_POLICIES).value except ExtensionNotFound: continue OV = '2.23.140.1.2.2' DV = '2.23.140.1.2.1' if OV in cert_policy: cert_policy_type = 'OV' if DV in cert_policy: cert_policy_type = 'DV' else: cert_policy_type = '' """ cert_ov_check = cert.subject.get_attributes_for_oid( NameOID.ORGANIZATION_NAME) if cert_ov_check: cert_ov = cert_ov_check[0].value else: cert_ov = '' print(f' {cert_common_names}') print(f' {cert_publickey}') print(f' {cert_keysize}') print(f' {cert_sig_algo._name}') # print(f' {cert_policy_type}') print(f' {cert_leaf_ev}') print(f' {cert_ov}') sql = "INSERT INTO CertInfo(hostname, commonname, publickey, keysize, signature, certtype, ov) values(?, ?, ?, ?, ?, ?, ?)" data = [ (hn, cert_common_names, cert_publickey, cert_keysize, cert_sig_algo._name, cert_leaf_ev, cert_ov) ] cur.executemany(sql, data)