def basic_example() -> None: # Define the server that you want to scan server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.google.com", 443) # Do connectivity testing to ensure SSLyze is able to connect try: server_info = ServerConnectivityTester().perform(server_location) except ConnectionToServerFailed as e: # Could not connect to the server; abort print(f"Error connecting to {server_location}: {e.error_message}") return # Then queue some scan commands for the server scanner = Scanner() server_scan_req = ServerScanRequest( server_info=server_info, scan_commands={ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES}, ) scanner.start_scans([server_scan_req]) # Then retrieve the results for server_scan_result in scanner.get_results(): print(f"\nResults for {server_scan_result.server_info.server_location.hostname}:") # SSL 2.0 results ssl2_result = server_scan_result.scan_commands_results[ScanCommand.SSL_2_0_CIPHER_SUITES] print("\nAccepted cipher suites for SSL 2.0:") for accepted_cipher_suite in ssl2_result.accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") # Certificate info results certinfo_result = server_scan_result.scan_commands_results[ScanCommand.CERTIFICATE_INFO] print("\nCertificate info:") for cert_deployment in certinfo_result.certificate_deployments: print(f"Leaf certificate: \n{cert_deployment.received_certificate_chain_as_pem[0]}")
def test_invalid_certificate_bad_name(self, certificate_name_field): # Given a server to scan server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( "www.cloudflare.com", 443) server_info = ServerConnectivityTester().perform(server_location) # And the server has a certificate with an invalid Subject field with mock.patch.object( hazmat.backends.openssl.x509._Certificate, certificate_name_field, new_callable=PropertyMock) as mock_certificate_name: # https://github.com/nabla-c0d3/sslyze/issues/495 mock_certificate_name.side_effect = ValueError( "Country name must be a 2 character country code") # When running the scan, it succeeds scan_result = CertificateInfoImplementation.scan_server( server_info) # And the result can be converted to console output result_as_txt = CertificateInfoImplementation.cli_connector_cls.result_to_console_output( scan_result) assert result_as_txt # And the result can be converted to JSON result_as_json = json.dumps(asdict(scan_result), cls=JsonEncoder) assert result_as_json
def test_ed25519_certificate(self): # Given a server that is configured with an ED25519 certificate with ModernOpenSslServer( server_certificate_path=Path(__file__).parent.absolute() / "server-ed25519-cert.pem", server_key_path=Path(__file__).parent.absolute() / "server-ed25519-key.pem", ) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, port=server.port, ip_address=server.ip_address) server_info = ServerConnectivityTester().perform(server_location) # When running the scan, it succeeds scan_result = CertificateInfoImplementation.scan_server( server_info) assert scan_result.certificate_deployments[ 0].received_certificate_chain # And the result can be converted to JSON result_as_json = json.dumps(asdict(scan_result), cls=JsonEncoder) assert result_as_json # And the result can be converted to console output result_as_txt = CertificateInfoImplementation.cli_connector_cls.result_to_console_output( scan_result) assert result_as_txt
def main() -> None: # First validate that we can connect to the servers we want to scan servers_to_scan = [] for hostname in ["cloudflare.com", "google.com"]: server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( hostname, 443) try: server_info = ServerConnectivityTester().perform(server_location) servers_to_scan.append(server_info) except ConnectionToServerFailed as e: print( f"Error connecting to {server_location.hostname}:{server_location.port}: {e.error_message}" ) return scanner = Scanner() # Then queue some scan commands for each server for server_info in servers_to_scan: server_scan_req = ServerScanRequest( server_info=server_info, scan_commands={ ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES }, ) scanner.queue_scan(server_scan_req) # Then retrieve the result of the scan commands for each server for server_scan_result in scanner.get_results(): print( f"\nResults for {server_scan_result.server_info.server_location.hostname}:" ) # Scan commands that were run with no errors try: ssl2_result = server_scan_result.scan_commands_results[ ScanCommand.SSL_2_0_CIPHER_SUITES] print(f"\nAccepted cipher suites for SSL 2.0:") for accepted_cipher_suite in ssl2_result.accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") except KeyError: pass try: certinfo_result = server_scan_result.scan_commands_results[ ScanCommand.CERTIFICATE_INFO] print("\nCertificate info:") for cert_deployment in certinfo_result.certificate_deployments: print( f"Leaf certificate: \n{cert_deployment.received_certificate_chain_as_pem[0]}" ) except KeyError: pass # Scan commands that were run with errors for scan_command, error in server_scan_result.scan_commands_errors.items( ): print( f"\nError when running {scan_command}:\n{error.exception_trace}" )
def basic_example_connectivity_testing() -> None: # Define the server that you want to scan server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("www.google.com", 443) # Do connectivity testing to ensure SSLyze is able to connect try: server_info = ServerConnectivityTester().perform(server_location) except ConnectionToServerFailed as e: # Could not connect to the server; abort print(f"Error connecting to {server_location}: {e.error_message}") return print(f"Connectivity testing completed: {server_info}")
def scan_runner(seq,host): hostname=host.decode("utf-8") servers_to_scan = [] server_location = None try: if r.hget(seq,"ipaddr"): server_location = ServerNetworkLocationViaDirectConnection(hostname, 443, r.hget(seq,"ipaddr").decode("utf-8")) else: server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup(hostname, 443) r.hset(seq,"ipaddr",server_location.ip_address) #Initialize with hostname, port int and ip address str #print(server_location) except Exception as e: return try: server_info = ServerConnectivityTester().perform(server_location) servers_to_scan.append(server_info) except ConnectionToServerFailed as e: return scanner = Scanner() # Then queue some scan commands for each server for server_info in servers_to_scan: server_scan_req = ServerScanRequest( server_info=server_info, scan_commands={ScanCommand.TLS_1_3_EARLY_DATA}, ) scanner.queue_scan(server_scan_req) # Then retrieve the result of the scan commands for each server for server_scan_result in scanner.get_results(): try: if server_scan_result.scan_commands_results[ScanCommand.TLS_1_3_EARLY_DATA].supports_early_data: r.hset(seq,"early","TRUE") except KeyError: return
def createServerConnections(ip_address, hostname, servers_to_scan, port_to_scan): """ Create connections to each server in ip_list, store connection results in servers_to_scan list :param ip_address: IP address to connect to :param hostname: name of host to connect to :param servers_to_scan: list of open server connections :param port_to_scan: desired port to attempt connection with :return: None """ server_location = ServerNetworkLocationViaDirectConnection(str(hostname), port_to_scan, ip_address) try: server_info = ServerConnectivityTester().perform(server_location) servers_to_scan.append(server_info) return "success" except ConnectionToServerFailed as e: return f"Error connecting to {server_location.hostname}:{server_location.port}: {e.error_message}"
def test_ecdsa_certificate(self): # Given a server to scan that has an ECDSA certificate server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( "www.cloudflare.com", 443) server_info = ServerConnectivityTester().perform(server_location) # When running the scan, it succeeds scan_result = CertificateInfoImplementation.scan_server(server_info) # And the result can be converted to JSON result_as_json = json.dumps(asdict(scan_result), cls=JsonEncoder) assert result_as_json # And the result can be converted to console output result_as_txt = CertificateInfoImplementation.cli_connector_cls.result_to_console_output( scan_result) assert result_as_txt
def test_supported_curves(self): # Given a server to scan that supports ECDH cipher suites server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( "www.cloudflare.com", 443) server_info = ServerConnectivityTester().perform(server_location) # When scanning for supported elliptic curves, it succeeds result: SupportedEllipticCurvesScanResult = SupportedEllipticCurvesImplementation.scan_server( server_info) # And the result confirms that some curves are supported and some are not assert result.supports_ecdh_key_exchange assert result.supported_curves assert result.rejected_curves # And a CLI output can be generated assert SupportedEllipticCurvesImplementation.cli_connector_cls.result_to_console_output( result)
def test_supported_curves(self): # Given a server to scan that supports ECDH cipher suites with specific curves server_curves = [ "X25519", "X448", "prime256v1", "secp384r1", "secp521r1" ] with ModernOpenSslServer(groups=":".join(server_curves)) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = ServerConnectivityTester().perform(server_location) # When scanning the server for supported curves, it succeeds result: SupportedEllipticCurvesScanResult = SupportedEllipticCurvesImplementation.scan_server( server_info) # And the supported curves were detected assert set(server_curves) == { curve.name for curve in result.supported_curves }
def search_subject_alt_name(self, target): print("Searching for Subject Alt Names") try: server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( target, 443) # Do connectivity testing to ensure SSLyze is able to connect try: server_info = ServerConnectivityTester().perform(server_location) except ConnectionToServerFailed as e: # Could not connect to the server; abort print(f"Error connecting to {server_location}: {e.error_message}") return # Then queue some scan commands for the server scanner = Scanner() server_scan_req = ServerScanRequest(server_info=server_info, scan_commands={ ScanCommand.CERTIFICATE_INFO}, ) scanner.queue_scan(server_scan_req) # Then retrieve the results for server_scan_result in scanner.get_results(): # Certificate info results certinfo_result = server_scan_result.scan_commands_results[ ScanCommand.CERTIFICATE_INFO] # Direct object reference is pretty bad, but then again so is the crypto.x509 object implementation, so... cert_deployment = certinfo_result.certificate_deployments[0] chain = cert_deployment.received_certificate_chain[0] ext = chain.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_ALTERNATIVE_NAME) for entry in ext.value.get_values_for_type(x509.DNSName): if entry.strip() not in self.domains: self.domains.append(entry.strip()) except Exception as e: self.handle_exception(e)
def parseArgsAndCheckConnectivity(self): if len(sys.argv) == 3 or len(sys.argv) == 4: if len(sys.argv) == 4: if sys.argv[1] == '--verbose': self.verbose = True self.host = sys.argv[2] self.port = sys.argv[3] else: self.printHelp() else: self.verbose = False self.host = sys.argv[1] self.port = sys.argv[2] try: print('Testing connectivity ...', end='', flush=True) # Define the server that you want to scan serverLocation = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( self.host, self.port) # Do connectivity testing to ensure SSLyze is able to connect self.serverInfo = ServerConnectivityTester().perform( serverLocation) except ConnectionToServerFailed as e: # Could not connect to the server; abort print( f"Error connecting to {serverLocation}: {e.error_message}") sys.exit() except ServerHostnameCouldNotBeResolved: print( f"Cannot resolve {self.host}, check that it is correct (IP is correct and domain does not include protocol)" ) sys.exit() print(" COMPLETED.") self.highestProtocol = self.serverInfo.tls_probing_result.highest_tls_version_supported.name else: self.printHelp()
def main(server_software_running_on_localhost: WebServerSoftwareEnum) -> None: # Ensure the server is accessible on localhost server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( "localhost", 443) server_info = ServerConnectivityTester().perform(server_location) if server_software_running_on_localhost == WebServerSoftwareEnum.APACHE2: # Apache2 is configured to require a client cert, and returns an error at the TLS layer if it is missing if server_info.tls_probing_result.client_auth_requirement != ClientAuthRequirementEnum.REQUIRED: raise RuntimeError( f"SSLyze did not detect that client authentication was required by Apache2:" f" {server_info.tls_probing_result.client_auth_requirement}.") elif server_software_running_on_localhost == WebServerSoftwareEnum.NGINX: # Nginx is configured to require a client cert but implements this by returning an error at the HTTP layer, # if the client cert is missing. This gets translated in SSLyze as "optionally" requiring a client cert if server_info.tls_probing_result.client_auth_requirement != ClientAuthRequirementEnum.OPTIONAL: raise RuntimeError( f"SSLyze did not detect that client authentication was required by Nginx:" f" {server_info.tls_probing_result.client_auth_requirement}.") elif server_software_running_on_localhost == WebServerSoftwareEnum.IIS: # IIS is not configured to require a client cert for now because I don't know how to enable this if server_info.tls_probing_result.client_auth_requirement != ClientAuthRequirementEnum.DISABLED: raise RuntimeError( f"SSLyze detected that client authentication was enabled by IIS:" f" {server_info.tls_probing_result.client_auth_requirement}.") else: raise ValueError( f"Unexpected value: {server_software_running_on_localhost}") # Queue all scan commands print("Starting scan.") scanner = Scanner() server_scan_req = ServerScanRequest( server_info=server_info, scan_commands=ScanCommandsRepository.get_all_scan_commands(), ) scanner.queue_scan(server_scan_req) # Retrieve the result for server_scan_result in scanner.get_results(): successful_cmds_count = len(server_scan_result.scan_commands_results) errored_cmds_count = len(server_scan_result.scan_commands_errors) print( f"Finished scan with {successful_cmds_count} results and {errored_cmds_count} errors." ) # Crash if any scan commands triggered an error that's not due to client authentication being required triggered_unexpected_error = False for scan_command, error in server_scan_result.scan_commands_errors.items( ): if error.reason != ScanCommandErrorReasonEnum.CLIENT_CERTIFICATE_NEEDED: triggered_unexpected_error = True print( f"\nError when running {scan_command}: {error.reason.name}." ) if error.exception_trace: exc_trace = "" for line in error.exception_trace.format(chain=False): exc_trace += f" {line}" print(exc_trace) print("\n") if triggered_unexpected_error: raise RuntimeError("The scan triggered unexpected errors") else: # The CLIENT_CERTIFICATE_NEEDED errors are expected, because of how Apache2 is configured print("OK: Triggered CLIENT_CERTIFICATE_NEEDED errors only.") # Crash if SSLyze didn't complete the scan commands that are supposed to work even when we don't provide a # client certificate if server_software_running_on_localhost == WebServerSoftwareEnum.APACHE2: expected_scan_command_results = { ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES, ScanCommand.SSL_3_0_CIPHER_SUITES, ScanCommand.SSL_2_0_CIPHER_SUITES, ScanCommand.OPENSSL_CCS_INJECTION, ScanCommand.HEARTBLEED, ScanCommand.ELLIPTIC_CURVES, ScanCommand.TLS_FALLBACK_SCSV, ScanCommand.CERTIFICATE_INFO, ScanCommand.TLS_COMPRESSION, } elif server_software_running_on_localhost == WebServerSoftwareEnum.NGINX: # With nginx, when configured to require client authentication, more scan commands work because unlike # Apache2, it does complete a full TLS handshake even when a client cert was not provided. It then returns # an error page at the HTTP layer. expected_scan_command_results = { ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES, ScanCommand.SSL_3_0_CIPHER_SUITES, ScanCommand.SSL_2_0_CIPHER_SUITES, ScanCommand.OPENSSL_CCS_INJECTION, ScanCommand.HEARTBLEED, ScanCommand.ELLIPTIC_CURVES, ScanCommand.TLS_FALLBACK_SCSV, ScanCommand.CERTIFICATE_INFO, ScanCommand.TLS_COMPRESSION, ScanCommand.SESSION_RESUMPTION, ScanCommand.TLS_1_3_EARLY_DATA, ScanCommand.HTTP_HEADERS, ScanCommand.SESSION_RESUMPTION_RATE, ScanCommand.SESSION_RENEGOTIATION, } elif server_software_running_on_localhost == WebServerSoftwareEnum.IIS: # With IIS, client authentication is not enabled so all scan commands should succeed expected_scan_command_results = ScanCommandsRepository.get_all_scan_commands( ) # type: ignore else: raise ValueError( f"Unexpected value: {server_software_running_on_localhost}") completed_scan_command_results = server_scan_result.scan_commands_results.keys( ) if completed_scan_command_results != expected_scan_command_results: raise RuntimeError( f"SSLyze did not complete all the expected scan commands: {completed_scan_command_results}" ) else: print("OK: Completed all the expected scan commands.") # Ensure the right TLS versions were detected by SSLyze as enabled # https://github.com/nabla-c0d3/sslyze/issues/472 if server_software_running_on_localhost in [ WebServerSoftwareEnum.APACHE2, WebServerSoftwareEnum.NGINX ]: # Apache and nginx are configured to only enable TLS 1.2 and TLS 1.3 expected_enabled_tls_scan_commands = { ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, } elif server_software_running_on_localhost == WebServerSoftwareEnum.IIS: # TLS 1.3 is not supported by IIS expected_enabled_tls_scan_commands = { ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES, } else: raise ValueError( f"Unexpected value: {server_software_running_on_localhost}") for ciphers_scan_cmd in expected_enabled_tls_scan_commands: scan_cmd_result = server_scan_result.scan_commands_results[ ciphers_scan_cmd] # type: ignore if not scan_cmd_result.accepted_cipher_suites: raise RuntimeError( f"SSLyze did not detect {scan_cmd_result.tls_version_used.name} to be enabled on the server." ) else: print( f"OK: Scan command {ciphers_scan_cmd} detected cipher suites." ) # Ensure a JSON output can be generated from the results json_output = _SslyzeOutputAsJson( server_scan_results=[server_scan_result], server_connectivity_errors=[], total_scan_time=3, ) json_output_as_dict = asdict(json_output) json.dumps(json_output_as_dict, cls=JsonEncoder, sort_keys=True, indent=4, ensure_ascii=True) print("OK: Was able to generate JSON output.")
def main() -> None: # Ensure the server is accessible on localhost server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup("localhost", 443) server_info = ServerConnectivityTester().perform(server_location) if server_info.tls_probing_result.client_auth_requirement != ClientAuthRequirementEnum.REQUIRED: raise RuntimeError( f"SSLyze did not detect that client authentication was required by the server:" f" {server_info.tls_probing_result.client_auth_requirement}." ) # Queue all scan commands print("Starting scan.") scanner = Scanner() server_scan_req = ServerScanRequest( server_info=server_info, scan_commands=ScanCommandsRepository.get_all_scan_commands(), ) scanner.queue_scan(server_scan_req) # Retrieve the result for server_scan_result in scanner.get_results(): successful_cmds_count = len(server_scan_result.scan_commands_results) errored_cmds_count = len(server_scan_result.scan_commands_errors) print(f"Finished scan with {successful_cmds_count} results and {errored_cmds_count} errors.") # Crash if any scan commands triggered an error that's not due to client authentication being required triggered_unexpected_error = False for scan_command, error in server_scan_result.scan_commands_errors.items(): if error.reason != ScanCommandErrorReasonEnum.CLIENT_CERTIFICATE_NEEDED: triggered_unexpected_error = True print(f"\nError when running {scan_command}: {error.reason.name}.") if error.exception_trace: exc_trace = "" for line in error.exception_trace.format(chain=False): exc_trace += f" {line}" print(exc_trace) print("\n") if triggered_unexpected_error: raise RuntimeError("The scan triggered unexpected errors") else: # The CLIENT_CERTIFICATE_NEEDED errors are expected, because of how Apache2 is configured print("OK: Triggered CLIENT_CERTIFICATE_NEEDED errors only.") # Crash if SSLyze didn't complete the scan commands that are supposed to work even when we don't provide a # client certificate expected_scan_command_results = { ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES, ScanCommand.SSL_3_0_CIPHER_SUITES, ScanCommand.SSL_2_0_CIPHER_SUITES, ScanCommand.OPENSSL_CCS_INJECTION, ScanCommand.HEARTBLEED, ScanCommand.ELLIPTIC_CURVES, ScanCommand.TLS_FALLBACK_SCSV, ScanCommand.CERTIFICATE_INFO, ScanCommand.TLS_COMPRESSION, } if server_scan_result.scan_commands_results.keys() != expected_scan_command_results: raise RuntimeError("SSLyze did not complete all the expected scan commands.") else: print("OK: Completed all the expected scan commands.") # Ensure TLS 1.2 and 1.3 were detected by SSLyze to be enabled # https://github.com/nabla-c0d3/sslyze/issues/472 for ciphers_scan_cmd in [ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES]: scan_cmd_result = server_scan_result.scan_commands_results[ciphers_scan_cmd] # type: ignore if not scan_cmd_result.accepted_cipher_suites: raise RuntimeError( f"SSLyze did not detect {scan_cmd_result.tls_version_used.name} to be enabled on the server." ) else: print(f"OK: Scan command {ciphers_scan_cmd} detected cipher suites.")
def scan_runner(seq, host): hostname = host.decode("utf-8") servers_to_scan = [] server_location = None try: if r.hget(seq, "ipaddr"): server_location = ServerNetworkLocationViaDirectConnection( hostname, 443, r.hget(seq, "ipaddr").decode("utf-8")) else: server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( hostname, 443) r.hset(seq, "ipaddr", server_location.ip_address) #Initialize with hostname, port int and ip address str #print(server_location) except Exception as e: print(e) r.hset(seq, "STATUS", 2) try: server_info = ServerConnectivityTester().perform(server_location) servers_to_scan.append(server_info) except ConnectionToServerFailed as e: if 'Probing failed' in str(e): r.hset(seq, "STATUS", 31) else: r.hset(seq, "STATUS", 32) return scanner = Scanner() # Then queue some scan commands for each server for server_info in servers_to_scan: server_scan_req = ServerScanRequest( server_info=server_info, scan_commands={ ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES }, ) scanner.queue_scan(server_scan_req) # Then retrieve the result of the scan commands for each server for server_scan_result in scanner.get_results(): try: tls1_3_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_3_CIPHER_SUITES] cipherstr = "" if tls1_3_result.accepted_cipher_suites: for accepted_cipher_suite in tls1_3_result.accepted_cipher_suites: cipherstr = cipherstr + str( accepted_cipher_suite.cipher_suite.name) + " " r.hset(seq, "TLS1_3", cipherstr) tls1_2_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_2_CIPHER_SUITES] cipherstr = "" if tls1_2_result.accepted_cipher_suites: for accepted_cipher_suite in tls1_2_result.accepted_cipher_suites: cipherstr = cipherstr + str( accepted_cipher_suite.cipher_suite.name) + " " r.hset(seq, "TLS1_2", cipherstr) tls1_1_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_1_CIPHER_SUITES] cipherstr = "" if tls1_1_result.accepted_cipher_suites: for accepted_cipher_suite in tls1_1_result.accepted_cipher_suites: cipherstr = cipherstr + str( accepted_cipher_suite.cipher_suite.name) + " " r.hset(seq, "TLS1_1", cipherstr) tls1_0_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_0_CIPHER_SUITES] cipherstr = "" if tls1_0_result.accepted_cipher_suites: for accepted_cipher_suite in tls1_0_result.accepted_cipher_suites: cipherstr = cipherstr + str( accepted_cipher_suite.cipher_suite.name) + " " r.hset(seq, "TLS1_0", cipherstr) r.hset(seq, "STATUS", 1) except KeyError: r.hset(seq, "STATUS", 4) # Scan commands that were run with errors for scan_command, error in server_scan_result.scan_commands_errors.items( ): r.hset(seq, "STATUS", 5)
def scan(target, ip, port, view, suite): """ Five inputs: web site name, ip, port split-dns view, and cipher suite """ server_location = ServerNetworkLocationViaDirectConnection( target, port, ip) # This line checks to see if the host is online try: server_info = ServerConnectivityTester().perform(server_location) except errors.ConnectionToServerTimedOut: raise ConnectionError("Connection Timeout", ERROR_MSG_CONNECTION_TIMEOUT(target, port)) except errors.ConnectionToServerFailed: raise ConnectionError("Unknown Connection Error", ERROR_MSG_UNKNOWN_CONNECTION(target, port)) # Create a new results dictionary scan_output = results.new_result_set() # I hash the combination of hostname and ip for tracking key = md5((target + ip).encode("utf-8")).hexdigest() results.set_result(scan_output, "MD5", key) results.set_result(scan_output, "Target", f"{target}:{port}") results.set_result(scan_output, "IP", f"{ip}:{port}") results.set_result(scan_output, "Scan", suite) results.set_result(scan_output, "View", view) scanner = Scanner() server_scan_req = ServerScanRequest(server_info=server_info, scan_commands=CIPHER_SUITES.get(suite)) scanner.queue_scan(server_scan_req) for result in scanner.get_results(): for cipher_suite in CIPHER_SUITES.get(suite): scan_result = result.scan_commands_results[cipher_suite] for accepted_cipher_suite in scan_result.accepted_cipher_suites: if suite == "policy" and scan_result.tls_version_used.name == "TLS_1_2": if (accepted_cipher_suite.cipher_suite.name not in ALLOWED_TLS12_CIPHERS): results.set_ciphers( scan_output, { "Version": f"{scan_result.tls_version_used.name}", "Cipher": f"{accepted_cipher_suite.cipher_suite.name}", }, ) else: results.set_ciphers( scan_output, { "Version": f"{scan_result.tls_version_used.name}", "Cipher": f"{accepted_cipher_suite.cipher_suite.name}", }, ) if len(scan_output["Results"]) == 0: results.set_result(scan_output, "Results", "No Policy Violations") return scan_output
def ssl_scan(self, target): print("Running SSL Scan") # Define the server that you want to scan server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( target, 443) try: # Do connectivity testing to ensure SSLyze is able to connect try: server_info = ServerConnectivityTester().perform(server_location) except ConnectionToServerFailed as e: # Could not connect to the server; abort print(f"Error connecting to {server_location}: {e.error_message}") return # Then queue some scan commands for the server scanner = Scanner() server_scan_req = ServerScanRequest(server_info=server_info, scan_commands={ ScanCommand.CERTIFICATE_INFO, ScanCommand.SSL_2_0_CIPHER_SUITES, ScanCommand.TLS_1_0_CIPHER_SUITES, ScanCommand.TLS_1_1_CIPHER_SUITES, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.HEARTBLEED, ScanCommand.HTTP_HEADERS}, ) scanner.queue_scan(server_scan_req) # Then retrieve the results for server_scan_result in scanner.get_results(): print( f"\nResults for {server_scan_result.server_info.server_location.hostname}:") heartbleed_vuln = server_scan_result.scan_commands_results[ ScanCommand.HEARTBLEED].is_vulnerable_to_heartbleed print(f"\nIs vulnerable to heartbleed? {heartbleed_vuln}") print("\nAccepted cipher suites for TLS 1.0:") for accepted_cipher_suite in server_scan_result.scan_commands_results[ ScanCommand.TLS_1_0_CIPHER_SUITES].accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") print("\nAccepted cipher suites for TLS 1.1:") for accepted_cipher_suite in server_scan_result.scan_commands_results[ ScanCommand.TLS_1_1_CIPHER_SUITES].accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") print("\nAccepted cipher suites for TLS 1.2:") for accepted_cipher_suite in server_scan_result.scan_commands_results[ ScanCommand.TLS_1_2_CIPHER_SUITES].accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") print("\nAccepted cipher suites for TLS 1.3:") for accepted_cipher_suite in server_scan_result.scan_commands_results[ ScanCommand.TLS_1_3_CIPHER_SUITES].accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") # SSL 2.0 results ssl2_result = server_scan_result.scan_commands_results[ ScanCommand.SSL_2_0_CIPHER_SUITES] print("\nAccepted cipher suites for SSL 2.0:") for accepted_cipher_suite in ssl2_result.accepted_cipher_suites: print(f"* {accepted_cipher_suite.cipher_suite.name}") # Certificate info results certinfo_result = server_scan_result.scan_commands_results[ ScanCommand.CERTIFICATE_INFO] print("\nCertificate info:") for cert_deployment in certinfo_result.certificate_deployments: print( f"Leaf certificate: \n{cert_deployment.received_certificate_chain_as_pem[0]}") except Exception as e: self.handle_exception(e, "Error running SSL scan") pass
def run(url): scan_result = {"name": __plugin__, "sequence": SEQUENCE, "result": []} error_result = {"name": __plugin__, "sequence": SEQUENCE, "result": []} error_result["result"] = [{ "name": "Error", "result": [{ "name": f"{__plugin__} can't scan this website" }] }] result_map = { "https": { "name": "Enabled HTTPS", "sequence": 0, "result": [] }, "effective": { "name": "Effective", "sequence": 1, "result": [] }, "subject": { "name": "Subject", "sequence": 2, "result": [] }, "issuer": { "name": "Issuer", "sequence": 3, "result": [] }, "public": { "name": "Public key algorithm", "sequence": 4, "result": [], }, "signature": { "name": "Signature hash algorithm", "sequence": 5, "result": [], }, "before": { "name": "Not valid before (UTC)", "sequence": 6, "result": [], }, "after": { "name": "Not valid after (UTC)", "sequence": 7, "result": [], }, "tls1_2": { "name": "Accepted TLS1.2 cipher suites", "sequence": 8, "result": [] }, "tls1_3": { "name": "Accepted TLS1.3 cipher suites", "sequence": 9, "result": [] }, "pfs": { "name": "Perfect Forward Secrecy (PFS)", "sequence": 10, "result": [], }, "ats": { "name": "App Transport Security (ATS)", "sequence": 11, "result": [], }, "tls1_3_early_data": { "name": "Support TLS1.3 early data", "sequence": 12, "result": [] }, "match": { "name": "Leaf certificate subject matches hostname", "sequence": 13, "result": [], }, "ocsp": { "name": "OCSP Must-Staple", "sequence": 14, "result": [], }, "fallback": { "name": "The TLS_FALLBACK_SCSV mechanism", "sequence": 15, "result": [] }, "ccs": { "name": "The OpenSSL CCS Injection vulnerability", "sequence": 16, "result": [] }, "heartbleed": { "name": "The Heartbleed vulnerability", "sequence": 17, "result": [] }, "crime": { "name": "The CRIME vulnerability", "sequence": 18, "result": [] }, "robot": { "name": "The ROBOT vulnerability", "sequence": 19, "result": [] }, } server_location = ServerNetworkLocationViaDirectConnection.with_ip_address_lookup( url.netloc, 443) try: server_info = ServerConnectivityTester().perform(server_location) except ConnectionToServerFailed as e: return error_result scanner = Scanner() server_scan_req = ServerScanRequest( server_info, { ScanCommand.CERTIFICATE_INFO, ScanCommand.TLS_1_2_CIPHER_SUITES, ScanCommand.TLS_1_3_CIPHER_SUITES, ScanCommand.TLS_1_3_EARLY_DATA, ScanCommand.TLS_FALLBACK_SCSV, ScanCommand.OPENSSL_CCS_INJECTION, ScanCommand.HEARTBLEED, ScanCommand.TLS_COMPRESSION, ScanCommand.ROBOT, }, ) scanner.queue_scan(server_scan_req) for server_scan_result in scanner.get_results(): certificate_result = server_scan_result.scan_commands_results[ ScanCommand.CERTIFICATE_INFO] tls_1_2_cipher_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_2_CIPHER_SUITES] tls_1_3_cipher_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_3_CIPHER_SUITES] tls_1_3_early_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_1_3_EARLY_DATA] tls_fallback_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_FALLBACK_SCSV] ccs_result = server_scan_result.scan_commands_results[ ScanCommand.OPENSSL_CCS_INJECTION] heartbleed_result = server_scan_result.scan_commands_results[ ScanCommand.HEARTBLEED] crime_result = server_scan_result.scan_commands_results[ ScanCommand.TLS_COMPRESSION] robot_result = server_scan_result.scan_commands_results[ ScanCommand.ROBOT] for certificate_deployment in certificate_result.certificate_deployments: for certificate_info in certificate_deployment.received_certificate_chain: result_map["subject"]["result"] = [{ "name": certificate_info.subject.rfc4514_string() }] result_map["issuer"]["result"] = [{ "name": certificate_info.issuer.rfc4514_string() }] public_key = certificate_info.public_key() public_key_name = type(public_key).__name__[1:][:-9] if "key_size" in dir(public_key): result_map["public"]["result"] = [{ "name": f"{public_key_name}{certificate_info.public_key().key_size}" }] else: result_map["public"]["result"] = [{"name": public_key_name}] result_map["signature"]["result"] = [{ "name": certificate_info.signature_hash_algorithm.name.upper() }] if datetime.now( ) > certificate_info.not_valid_before and datetime.now( ) < certificate_info.not_valid_after: result_map["effective"]["result"] = True result_map["before"]["result"] = [{ "name": datetime.strftime(certificate_info.not_valid_before, "%Y-%m-%d %H:%M:%S") }] result_map["after"]["result"] = [{ "name": datetime.strftime(certificate_info.not_valid_after, "%Y-%m-%d %H:%M:%S") }] break result_map["https"]["result"] = True result_map["match"][ "result"] = certificate_deployment.leaf_certificate_subject_matches_hostname result_map["ocsp"][ "result"] = certificate_deployment.leaf_certificate_has_must_staple_extension break tls_1_2_cipher_list = [ accepted_cipher_suite.cipher_suite.name for accepted_cipher_suite in tls_1_2_cipher_result.accepted_cipher_suites ] tls_1_3_cipher_list = [ accepted_cipher_suite.cipher_suite.name for accepted_cipher_suite in tls_1_3_cipher_result.accepted_cipher_suites ] result_map["tls1_2"]["result"] = [{ "name": tls_1_2_cipher } for tls_1_2_cipher in tls_1_2_cipher_list ] if tls_1_2_cipher_list else False result_map["tls1_3"]["result"] = [{ "name": tls_1_3_cipher } for tls_1_3_cipher in tls_1_3_cipher_list ] if tls_1_3_cipher_list else False cipher_list = tls_1_2_cipher_list + tls_1_3_cipher_list for cipher in cipher_list: if "DHE" in cipher: result_map["pfs"]["result"] = True if set(cipher_list).intersection(ATS_CIPHER_SET): result_map["ats"]["result"] = True result_map["tls1_3_early_data"][ "result"] = tls_1_3_early_result.supports_early_data result_map["fallback"][ "result"] = tls_fallback_result.supports_fallback_scsv result_map["ccs"]["result"] = not ccs_result.is_vulnerable_to_ccs_injection result_map["heartbleed"][ "result"] = not heartbleed_result.is_vulnerable_to_heartbleed result_map["crime"]["result"] = not crime_result.supports_compression result_map["robot"]["result"] = [{"name": robot_result.robot_result.name}] scan_result["result"] = sorted([item for item in result_map.values()], key=lambda x: x.get("sequence", 0)) return scan_result