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 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_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] self.assertEqual({'TLS13-AES-128-GCM-SHA256', 'TLS13-AES-256-GCM-SHA384', 'TLS13-CHACHA20-POLY1305-SHA256'}, set(accepted_cipher_name_list))
def test_tls_1_3_cipher_suites(self): server_test = ServerConnectivityTester(hostname='www.cloudflare.com') server_info = server_test.perform() plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Tlsv13ScanCommand()) accepted_cipher_name_list = [ cipher.name for cipher in plugin_result.accepted_cipher_list ] assert {'TLS_CHACHA20_POLY1305_SHA256', 'TLS_AES_256_GCM_SHA384', 'TLS_AES_128_GCM_SHA256'} == \ set(accepted_cipher_name_list)
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_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))
def test_succeeds_when_client_auth_failed_tls_1_3(self): # Given a TLS 1.3 server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.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, Tlsv13ScanCommand()) assert plugin_result.accepted_cipher_list assert plugin_result.as_text() assert plugin_result.as_xml()
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
# 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 SynchronousScanner.DEFAULT_NETWORK_TIMEOUT = 3 ERROR_MSG_CONNECTION_TIMEOUT = 'TCP connection to {}:{} timed-out'.format ERROR_MSG_UNKNOWN_CONNECTION = \ 'TCP connection to {}:{} encountered unknown error'.format def scan(name, ip, port, view, suite): """ Five inputs: web site name, ip, port split-dns view, and cipher suite """
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 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 }
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 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 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)