def start(session: Session): print(f"Scanning: {session.url}") # make sure it resolves try: socket.gethostbyname(session.domain) except socket.gaierror as error: print( f"Fatal Error: Unable to resolve {session.domain} ({str(error)})") return try: cutils.check_redirect(session) except ValueError as error: print(f"Unable to continue: {str(error)}") return # check to see if we are looking at an HTTPS server if session.url_parsed.scheme == "https": if (session.args.internalssl or utils.is_ip(session.domain) or utils.get_port(session.url) != 443): # use internal scanner ssl_internal.scan(session) else: try: ssl_labs.scan(session) except Exception as error: output.debug_exception() output.error(f"Error running scan with SSL Labs: {str(error)}") if session.args.tdessessioncount: ssl_sweet32.scan(session)
def start(session: Session): print(f"Scanning: {session.url}") # make sure it resolves try: socket.gethostbyname(session.domain) except socket.gaierror as error: output.debug_exception() output.error( f"Fatal Error: Unable to resolve {session.domain} ({str(error)})") return try: cutils.check_redirect(session) except Exception as error: output.debug_exception() output.error(f"Unable to continue: {str(error)}") return if not session.args.nodns: dns.scan(session) if session.args.ports: network.scan(session) # check to see if we are looking at an HTTPS server if session.url_parsed.scheme == "https" and not session.args.nossl: if (session.args.internalssl or utils.is_ip(session.domain) or utils.get_port(session.url) != 443): # use internal scanner ssl_internal.scan(session) else: try: ssl_labs.scan(session) except Exception as error: output.debug_exception() output.error(f"Error running scan with SSL Labs: {str(error)}") output.norm("Switching to internal SSL scanner...") ssl_internal.scan(session) if session.args.tdessessioncount: ssl_sweet32.scan(session) http.scan(session) # reset any stored data http.reset() return
def start(args, url): print(f"Scanning: {url}") # parse the URL, we'll need this parsed = urlparse(url) # get rid of any port number & credentials that may exist domain = utils.get_domain(parsed.netloc) # make sure it resolves try: socket.gethostbyname(domain) except socket.gaierror as error: print(f"Fatal Error: Unable to resolve {domain} ({str(error)})") return if parsed.scheme == "http": try: # check for TLS redirect tls_redirect = network.check_ssl_redirect(url) if tls_redirect != url: print(f"Server redirects to TLS: Scanning: {tls_redirect}") url = tls_redirect parsed = urlparse(url) except Exception as error: output.debug_exception() output.error(f"Failed to connect to {url} ({str(error)})") return www_redirect = network.check_www_redirect(url) if www_redirect is not None and www_redirect != url: print(f"Server performs WWW redirect: Scanning: {www_redirect}") url = www_redirect parsed = urlparse(url) # check to see if we are looking at an HTTPS server if parsed.scheme == "https": if args.internalssl or utils.is_ip(domain) or utils.get_port(url) != 443: # use internal scanner ssl_internal.scan(args, url, domain) else: try: ssl_labs.scan(args, url, domain) except Exception as error: output.debug_exception() output.error(f"Error running scan with SSL Labs: {str(error)}") if args.tdessessioncount: ssl_sweet32.scan(args, url, domain)
def check_local_ip_disclosure(session: Session) -> List[Result]: def _send_http_10_get( con: Union[SslConnection, socket.socket] ) -> Tuple[str, HTTPResponse]: req = ( "HEAD / HTTP/1.0\r\n" "User-Agent: {user_agent}\r\n" "Accept: */*\r\n\r\n".format(user_agent=network.YAWAST_UA) ) if type(con) is SslConnection: con.ssl_client.write(req.encode("utf_8")) res = http_response_parser.HttpResponseParser.parse_from_ssl_connection( con.ssl_client ) else: con.sendall(req.encode("utf_8")) res = http_response_parser.HttpResponseParser.parse_from_socket(con) return req, res def _resp_to_str(res: HTTPResponse) -> str: ver = "1.1" if res.version == 11 else "1.0" body = f"HTTP/{ver} {res.code} {res.reason}\r\n" for k, val in res.headers.items(): body += f"{k}: {val}\r\n" return body def _get_ip(res: HTTPResponse) -> Union[str, None]: loc = res.getheader("Location") if loc is not None: # it's a redirect, check to see if there's an IP in it parsed = urlparse(loc) domain = utils.get_domain(parsed.netloc) if utils.is_ip(domain): # it's an IP, now, is it private? if utils.is_private_ip(domain): return domain else: return None return None def _get_result(client, prt): req, resp = _send_http_10_get(client) ip = _get_ip(resp) if ip is not None: results.append( Result( f"Private IP Found: {ip} via HTTP 1.0 Redirect", Vln.SERVER_INT_IP_EXP_HTTP10, session.url, { "request": req, "response": _resp_to_str(resp), "ip": {ip}, "port": prt, }, ) ) results: List[Result] = [] if session.url_parsed.scheme == "https": conn_tester = server_connectivity_tester.ServerConnectivityTester( hostname=session.domain, port=utils.get_port(session.url) ) server_info = conn_tester.perform() conn = ssl_connection_configurator.SslConnectionConfigurator.get_connection( ssl_version=OpenSslVersionEnum.SSLV23, server_info=server_info, should_ignore_client_auth=True, ssl_verify_locations=None, should_use_legacy_openssl=False, ) try: conn.connect() _get_result(conn, utils.get_port(session.url)) except Exception: output.debug_exception() if session.supports_http: url = session.get_http_url() port = utils.get_port(url) conn = socket.socket() conn.connect((utils.get_domain(url), port)) try: _get_result(conn, port) except Exception: output.debug_exception() return results
def scan(args: Namespace, url: str, domain: str): output.norm( f"Beginning SSL scan using sslyze {__version__} (this could take a minute or two)" ) output.empty() ips = basic.get_ips(domain) for ip in ips: try: conn_tester = server_connectivity_tester.ServerConnectivityTester( hostname=domain, port=utils.get_port(url), ip_address=ip) output.norm(f"IP: {conn_tester.ip_address}:{conn_tester.port}") server_info = conn_tester.perform() scanner = synchronous_scanner.SynchronousScanner() cinfo = scanner.run_scan_command( server_info, certificate_info_plugin.CertificateInfoScanCommand()) cinfo = typing.cast( certificate_info_plugin.CertificateInfoScanResult, cinfo) # print info on the server cert _get_leaf_cert_info(cinfo.verified_certificate_chain[0]) # get all but the first element _get_cert_chain(cinfo.verified_certificate_chain[1:], url) # list the root stores this is trusted by trust = "" for t in _get_trusted_root_stores(cinfo): trust += f"{t} (trusted) " output.norm(f"\tRoot Stores: {trust}") output.empty() # get info for the various versions of SSL/TLS output.norm("\tCipher Suite Support:") sslv2 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Sslv20ScanCommand()) sslv2 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, sslv2) _get_suite_info("SSLv2", sslv2, url) sslv3 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Sslv30ScanCommand()) sslv3 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, sslv3) _get_suite_info("SSLv3", sslv3, url) tls10 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Tlsv10ScanCommand()) tls10 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, tls10) _get_suite_info("TLSv1.0", tls10, url) tls11 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Tlsv11ScanCommand()) tls11 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, tls11) _get_suite_info("TLSv1.1", tls11, url) tls12 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Tlsv12ScanCommand()) tls12 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, tls12) _get_suite_info("TLSv1.2", tls12, url) tls13 = scanner.run_scan_command( server_info, openssl_cipher_suites_plugin.Tlsv13ScanCommand()) tls13 = typing.cast( openssl_cipher_suites_plugin.CipherSuiteScanResult, tls13) _get_suite_info("TLSv1.3", tls13, url) output.empty() # check compression compression = scanner.run_scan_command( server_info, compression_plugin.CompressionScanCommand()) compression = typing.cast(compression_plugin.CompressionScanResult, compression) if compression.compression_name is not None: reporter.display( f"\tCompression: {compression.compression_name}", issue.Issue(Vulnerabilities.TLS_COMPRESSION_ENABLED, url, {}), ) else: output.norm("\tCompression: None") # check TLS_FALLBACK_SCSV fallback = scanner.run_scan_command( server_info, fallback_scsv_plugin.FallbackScsvScanCommand()) fallback = typing.cast(fallback_scsv_plugin.FallbackScsvScanResult, fallback) if fallback.supports_fallback_scsv: output.norm("\tDowngrade Prevention: Yes") else: reporter.display( "\tDowngrade Prevention: No", issue.Issue(Vulnerabilities.TLS_FALLBACK_SCSV_MISSING, url, {}), ) # check Heartbleed heartbleed = scanner.run_scan_command( server_info, heartbleed_plugin.HeartbleedScanCommand()) heartbleed = typing.cast(heartbleed_plugin.HeartbleedScanResult, heartbleed) if heartbleed.is_vulnerable_to_heartbleed: reporter.display( "\tHeartbleed: Vulnerable", issue.Issue(Vulnerabilities.TLS_HEARTBLEED, url, {}), ) else: output.norm("\tHeartbleed: No") # check OpenSSL CCS injection vulnerability (CVE-2014-0224) openssl_ccs = scanner.run_scan_command( server_info, openssl_ccs_injection_plugin.OpenSslCcsInjectionScanCommand(), ) openssl_ccs = typing.cast( openssl_ccs_injection_plugin.OpenSslCcsInjectionScanResult, openssl_ccs) if openssl_ccs.is_vulnerable_to_ccs_injection: reporter.display( "\tOpenSSL CCS (CVE-2014-0224): Vulnerable", issue.Issue(Vulnerabilities.TLS_OPENSSL_CVE_2014_0224, url, {}), ) else: output.norm("\tOpenSSL CCS (CVE-2014-0224): No") # check SessionRenegotiation sr = scanner.run_scan_command( server_info, session_renegotiation_plugin.SessionRenegotiationScanCommand(), ) sr = typing.cast( session_renegotiation_plugin.SessionRenegotiationScanResult, sr) if sr.accepts_client_renegotiation: output.norm( "\tSecure Renegotiation: client-initiated renegotiation supported" ) if sr.supports_secure_renegotiation: output.norm( "\tSecure Renegotiation: secure renegotiation supported") # check SessionResumption resump = scanner.run_scan_command( server_info, session_resumption_plugin.SessionResumptionSupportScanCommand( ), ) resump = typing.cast( session_resumption_plugin.SessionResumptionSupportScanResult, resump) output.norm( f"\tSession Resumption Tickets Supported: {resump.is_ticket_resumption_supported}" ) output.norm( f"\tSession Resumption: {resump.successful_resumptions_nb} of " f"{resump.attempted_resumptions_nb} successful") # check ROBOT robot = scanner.run_scan_command(server_info, robot_plugin.RobotScanCommand()) robot = typing.cast(robot_plugin.RobotScanResult, robot) if (robot.robot_result_enum == robot_plugin.RobotScanResultEnum.VULNERABLE_WEAK_ORACLE): reporter.display( "\tROBOT: Vulnerable - Not Exploitable", issue.Issue(Vulnerabilities.TLS_ROBOT_ORACLE_WEAK, url, {}), ) elif (robot.robot_result_enum == robot_plugin.RobotScanResultEnum.VULNERABLE_STRONG_ORACLE): reporter.display( "\tROBOT: Vulnerable - Exploitable", issue.Issue(Vulnerabilities.TLS_ROBOT_ORACLE_STRONG, url, {}), ) elif (robot.robot_result_enum == robot_plugin.RobotScanResultEnum. UNKNOWN_INCONSISTENT_RESULTS): output.error("\tROBOT: Test Failed (Inconsistent Results)") else: output.norm("\tROBOT: No") # check TLS 1.3 Early Data ed = scanner.run_scan_command( server_info, early_data_plugin.EarlyDataScanCommand()) ed = typing.cast(early_data_plugin.EarlyDataScanResult, ed) if ed.is_early_data_supported: output.info("\tTLS 1.3 0-RTT Support: Yes") else: output.norm("\tTLS 1.3 0-RTT Support: No") if cinfo.ocsp_response_status is not None: output.norm("\tOCSP Stapling: Yes") else: reporter.display( "\tOCSP Stapling: No", issue.Issue(Vulnerabilities.TLS_OCSP_STAPLE_MISSING, url, {}), ) output.empty() except server_connectivity_tester.ServerConnectivityError as error: output.debug_exception() if checkers.is_ipv6(ip): output.error( "\tError connecting to IPv6 IP. Please ensure that your system is configured properly." ) output.error(f"\tConnection failed ({str(error)})") output.empty()
def scan(session: Session): ips = basic.get_ips(session.domain) for ip in ips: conn = None count = 0 try: count = 0 conn_tester = server_connectivity_tester.ServerConnectivityTester( hostname=session.domain, port=utils.get_port(session.url), ip_address=ip) output.norm( f"TLS Session Request Limit: Checking number of requests accepted using 3DES suites " f"(IP: {conn_tester.ip_address}:{conn_tester.port})") server_info = conn_tester.perform() conn = ssl_connection_configurator.SslConnectionConfigurator.get_connection( ssl_version=OpenSslVersionEnum.SSLV23, server_info=server_info, should_use_legacy_openssl=True, openssl_cipher_string="3DES", should_ignore_client_auth=True, ssl_verify_locations=None, ) conn.connect() req = ("HEAD / HTTP/1.1\r\n" "Host: {host}\r\n" "User-Agent: {user_agent}\r\n" "Accept: */*\r\n" "Connection: keep-alive\r\n\r\n".format( host=session.domain, user_agent=network.YAWAST_UA)) ossl_name = conn.ssl_client.get_current_cipher_name() name = OPENSSL_TO_RFC_NAMES_MAPPING[OpenSslVersionEnum.TLSV1].get( ossl_name, ossl_name) print(" ", end="", flush=True) print(f"(using {name})", end="", flush=True) for i in range(0, 10000): conn.ssl_client.write(req) http_response_parser.HttpResponseParser.parse_from_ssl_connection( conn.ssl_client) count += 1 if i % 20: print(".", end="", flush=True) output.empty() reporter.display( f"\tTLS Session Request Limit: Connection not terminated after {count} requests; " f"possibly vulnerable to SWEET32", issue.Issue(Vulnerabilities.TLS_SWEET32, session.url, {}), ) except ssl_connection.SslHandshakeRejected as error: output.debug_exception() output.empty() output.norm(f"\tServer rejected our connection ({str(error)})") output.empty() except IOError as error: output.debug_exception() output.empty() if count > 0: output.norm( f"\tTLS Session Request Limit: Connection terminated after {count} requests ({str(error)})" ) else: output.norm( "\tTLS Session Request Limit: Server does not support 3DES cipher suites" ) output.empty() except server_connectivity_tester.ServerConnectivityError as error: output.debug_exception() output.empty() if checkers.is_ipv6(ip): output.error( "\tError connecting to IPv6 IP. Please ensure that your system is configured properly." ) output.error(f"\tConnection failed ({str(error)})") output.empty() finally: if conn is not None: conn.close() output.empty()
def start(args, url): print(f"Scanning: {url}") # parse the URL, we'll need this parsed = urlparse(url) # get rid of any port number & credentials that may exist domain = utils.get_domain(parsed.netloc) # make sure it resolves try: socket.gethostbyname(domain) except socket.gaierror as error: print(f"Fatal Error: Unable to resolve {domain} ({str(error)})") return # perform some connection testing if parsed.scheme == "http": try: # check for TLS redirect tls_redirect = network.check_ssl_redirect(url) if tls_redirect != url: print(f"Server redirects to TLS: Scanning: {tls_redirect}") url = tls_redirect parsed = urlparse(url) except Exception: output.debug_exception() # we tried to connect to port 80, and it failed # this could mean a couple things, first, we need to # see if it answers to 443 parsed = parsed._replace(scheme="https") url = urlunparse(parsed) print("Server does not respond to HTTP, switching to HTTPS") print() print(f"Scanning: {url}") # grab the head, to see if we get anything try: network.http_head(url, timeout=5) print() except Exception as err: output.debug_exception() print(f"Fatal Error: Can not connect to {url} ({str(err)})") return else: # if we are scanning HTTPS, try HTTP to see what it does try: http_parsed = parsed._replace(scheme="http") http_url = urlunparse(http_parsed) network.http_head(http_url, timeout=5) print("Server responds to HTTP requests") print() except Exception: output.debug_exception() print("Server does not respond to HTTP requests") print() # check for www redirect www_redirect = network.check_www_redirect(url) if www_redirect is not None and www_redirect != url: print(f"Server performs WWW redirect: Scanning: {www_redirect}") url = www_redirect if not args.nodns: dns.scan(args, url, domain) # check to see if we are looking at an HTTPS server if parsed.scheme == "https" and not args.nossl: if args.internalssl or utils.is_ip( domain) or utils.get_port(url) != 443: # use internal scanner ssl_internal.scan(args, url, domain) else: try: ssl_labs.scan(args, url, domain) except Exception as error: output.debug_exception() output.error(f"Error running scan with SSL Labs: {str(error)}") if args.tdessessioncount: ssl_sweet32.scan(args, url, domain) http.scan(args, url, domain) # reset any stored data http.reset() return