diff --git a/testssl.sh b/testssl.sh index 407e0dc..f2ae525 100755 --- a/testssl.sh +++ b/testssl.sh @@ -12480,7 +12480,7 @@ parse_tls_serverhello() { # If the ClientHello included a supported_versions extension, then check that the # $DETECTED_TLS_VERSION appeared in the list offered in the ClientHello. - if [[ "${TLS_CLIENT_HELLO:0:2}" == "01" ]]; then + if [[ "${TLS_CLIENT_HELLO:0:2}" == 01 ]]; then # get position of cipher lists (just after session id) offset=78+2*$(hex2dec "${TLS_CLIENT_HELLO:76:2}") # get position of compression methods @@ -12496,7 +12496,7 @@ parse_tls_serverhello() { offset+=6 tls_protocol2="$(tolower "$tls_protocol2")" for (( j=0; j < extension_len-2; j=j+4 )); do - [[ "${TLS_CLIENT_HELLO:offset:4}" == "$tls_protocol2" ]] && break + [[ "${TLS_CLIENT_HELLO:offset:4}" == $tls_protocol2 ]] && break offset+=4 done if [[ $j -eq $extension_len-2 ]]; then @@ -14333,27 +14333,36 @@ run_renego() { pr_bold " Secure Renegotiation (RFC 5746) " jsonID="secure_renego" - # first fingerprint for the Line "Secure Renegotiation IS NOT" or "Secure Renegotiation IS " - $OPENSSL s_client $(s_client_options "$proto $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") 2>&1 $TMPFILE 2>$ERRFILE - if sclient_connect_successful $? $TMPFILE; then - grep -iaq "Secure Renegotiation IS NOT" $TMPFILE - sec_renego=$? # 0= Secure Renegotiation IS NOT supported - # grep -iaq "Secure Renegotiation IS supported" - #FIXME: didn't occur to me yet but why not also to check on "Secure Renegotiation IS supported" - case $sec_renego in - 0) prln_svrty_critical "Not supported / VULNERABLE (NOT ok)" - fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" - ;; - 1) prln_svrty_best "supported (OK)" - fileout "$jsonID" "OK" "supported" "$cve" "$cwe" - ;; - *) prln_warning "FIXME (bug): $sec_renego" - fileout "$jsonID" "WARN" "FIXME (bug) $sec_renego" "$cve" "$cwe" - ;; - esac + + if "$TLS13_ONLY"; then + # https://www.openssl.org/blog/blog/2018/02/08/tlsv1.3/ + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no renegotiation support in TLS 1.3 only servers" + outln + fileout "$jsonID" "OK" "TLS 1.3 only server" "$cve" "$cwe" else - prln_warning "OpenSSL handshake didn't succeed" - fileout "$jsonID" "WARN" "OpenSSL handshake didn't succeed" "$cve" "$cwe" + # first fingerprint for the Line "Secure Renegotiation IS NOT" or "Secure Renegotiation IS " + $OPENSSL s_client $(s_client_options "$proto $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") 2>&1 $TMPFILE 2>$ERRFILE + if sclient_connect_successful $? $TMPFILE; then + grep -iaq "Secure Renegotiation IS NOT" $TMPFILE + sec_renego=$? # 0= Secure Renegotiation IS NOT supported + # grep -iaq "Secure Renegotiation IS supported" + #FIXME: didn't occur to me yet but why not also to check on "Secure Renegotiation IS supported" + case $sec_renego in + 0) prln_svrty_critical "Not supported / VULNERABLE (NOT ok)" + fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" + ;; + 1) prln_svrty_best "supported (OK)" + fileout "$jsonID" "OK" "supported" "$cve" "$cwe" + ;; + *) prln_warning "FIXME (bug): $sec_renego" + fileout "$jsonID" "WARN" "FIXME (bug) $sec_renego" "$cve" "$cwe" + ;; + esac + else + prln_warning "OpenSSL handshake didn't succeed" + fileout "$jsonID" "WARN" "OpenSSL handshake didn't succeed" "$cve" "$cwe" + fi fi # FIXME: Basically this can be done with sockets and we might have that information already @@ -14368,7 +14377,8 @@ run_renego() { cve="CVE-2011-1473" # see: https://blog.qualys.com/ssllabs/2011/10/31/tls-renegotiation-and-denial-of-service-attacks # http://blog.ivanristic.com/2009/12/testing-for-ssl-renegotiation.html -- head/get doesn't seem to be needed though - # https://archive.fo/20130415224936/http://www.thc.org/thc-ssl-dos/, https://vincent.bernat.ch/en/blog/2011-ssl-dos-mitigation + # https://archive.fo/20130415224936/http://www.thc.org/thc-ssl-dos/ + # https://vincent.bernat.ch/en/blog/2011-ssl-dos-mitigation case "$OSSL_VER" in 0.9.8*) # we need this for Mac OSX unfortunately case "$OSSL_VER_APPENDIX" in @@ -14389,7 +14399,12 @@ run_renego() { esac - if "$CLIENT_AUTH"; then + if "$TLS13_ONLY"; then + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no renegotiation support in TLS 1.3 only servers" + outln + fileout "$jsonID" "OK" "not vulnerable, TLS 1.3 only" "$cve" "$cwe" + elif "$CLIENT_AUTH"; then prln_warning "client x509-based authentication prevents this from being tested" fileout "$jsonID" "WARN" "client x509-based authentication prevents this from being tested" sec_client_renego=1 @@ -14404,7 +14419,7 @@ run_renego() { sec_client_renego=1 else # second try in the foreground as we are sure now it won't hang - echo R | $OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -msg -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>>$ERRFILE + echo R | $OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>>$ERRFILE sec_client_renego=$? # 0=client is renegotiating & doesn't return an error --> vuln! case "$sec_client_renego" in 0) # We try again if server is HTTP. This could be either a node.js server or something else. @@ -14415,7 +14430,7 @@ run_renego() { fileout "$jsonID" "MEDIUM" "VULNERABLE, potential DoS threat" "$cve" "$cwe" "$hint" else (for i in {1..4}; do echo R; sleep 1; done) | \ - $OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -msg -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>>$ERRFILE + $OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>>$ERRFILE case $? in 0) pr_svrty_high "VULNERABLE (NOT ok)"; outln ", DoS threat" fileout "$jsonID" "HIGH" "VULNERABLE, DoS threat" "$cve" "$cwe" "$hint" @@ -14461,15 +14476,23 @@ run_crime() { local cwe="CWE-310" local hint="" - # in a nutshell: don't offer TLS/SPDY compression on the server side - # This tests for CRIME Vulnerability (www.ekoparty.org/2012/juliano-rizzo.php) on HTTPS, not SPDY (yet) - # Please note that it is an attack where you need client side control, so in regular situations this - # means anyway "game over", w/wo CRIME - # www.h-online.com/security/news/item/Vulnerability-in-SSL-encryption-is-barely-exploitable-1708604.html + # In a nutshell: don't offer TLS/SPDY compression. This tests for CRIME Vulnerability on HTTPS only, + # not SPDY or ALPN (yet). Please note that it is an attack where you need client side control, so in + # regular situations this # means anyway "game over", with or without CRIME. + # + # https://blog.qualys.com/ssllabs/2012/09/14/crime-information-leakage-attack-against-ssltls [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for CRIME vulnerability " && outln pr_bold " CRIME, TLS " ; out "($cve) " + if "$TLS13_ONLY"; then + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no compression in TLS 1.3 only servers" + outln + fileout "$jsonID" "OK" "TLS 1.3 only server" "$cve" "$cwe" + return 0 + fi + if ! "$HAS_ZLIB"; then if "$SSL_NATIVE"; then prln_local_problem "$OPENSSL lacks zlib support" @@ -14488,20 +14511,21 @@ run_crime() { sclient_connect_successful $? $TMPFILE sclient_success=$? fi + if [[ $sclient_success -ne 0 ]]; then pr_warning "test failed (couldn't connect)" fileout "CRIME_TLS" "WARN" "Check failed, couldn't connect" "$cve" "$cwe" ret=1 elif grep -a Compression $TMPFILE | grep -aq NONE >/dev/null; then pr_svrty_good "not vulnerable (OK)" - if [[ $SERVICE != "HTTP" ]] && ! "$CLIENT_AUTH"; then + if [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH"; then out " (not using HTTP anyway)" fileout "CRIME_TLS" "OK" "not vulnerable (not using HTTP anyway)" "$cve" "$cwe" else fileout "CRIME_TLS" "OK" "not vulnerable" "$cve" "$cwe" fi else - if [[ $SERVICE == "HTTP" ]] || "$CLIENT_AUTH"; then + if [[ $SERVICE == HTTP ]] || "$CLIENT_AUTH"; then pr_svrty_high "VULNERABLE (NOT ok)" fileout "CRIME_TLS" "HIGH" "VULNERABLE" "$cve" "$cwe" "$hint" else @@ -14646,13 +14670,21 @@ run_sweet32() { [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for SWEET32 (Birthday Attacks on 64-bit Block Ciphers) " && outln pr_bold " SWEET32"; out " (${cve// /, }) " + if "$TLS13_ONLY"; then + # Unfortunately there's no restriction using TLS 1.2 with $sweet32_ciphers + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", TLS 1.3 doesn't offer such ciphers" + outln + fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe" + return 0 + fi + "$SSL_NATIVE" && using_sockets=false # The openssl binary distributed has almost everything we need (PSK, KRB5 ciphers and feff, ffe0 are typically missing). - # Measurements show that there's little impact whether we use sockets or TLS here, so the default is sockets here + # Measurements show that there's little impact whether we use sockets or TLS here, so the default is sockets here. if "$using_sockets"; then for proto in 03 02 01 00; do - "$FAST" && [[ "$proto" != 03 ]] && break - ! "$FAST" && [[ $(has_server_protocol "$proto") -eq 1 ]] && continue + [[ $(has_server_protocol "$proto") -eq 1 ]] && continue tls_sockets "$proto" "${sweet32_ciphers_hex}, 00,ff" sclient_success=$? [[ $sclient_success -eq 2 ]] && sclient_success=0 @@ -14766,6 +14798,15 @@ run_ssl_poodle() { [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for SSLv3 POODLE (Padding Oracle On Downgraded Legacy Encryption) " && outln pr_bold " POODLE, SSL"; out " ($cve) " + if "$TLS13_ONLY" || [[ $(has_server_protocol ssl3) -ne 0 ]]; then + # one condition should normally suffice but we don't know when run_poddle() was called + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no SSLv3 support" + outln + fileout "$jsonID" "OK" "not vulnerable, no SSLv3 support" "$cve" "$cwe" + return 0 + fi + "$SSL_NATIVE" && using_sockets=false # The openssl binary distributed has almost everything we need (PSK and KRB5 ciphers are typically missing). # Measurements show that there's little impact whether we use sockets or TLS here, so the default is sockets here @@ -14878,10 +14919,9 @@ run_tls_fallback_scsv() { return 0 ;; *) if [[ $(has_server_protocol tls1_3) -eq 0 ]]; then - # If the server supports TLS 1.3, and does not support TLS 1.2, TLS 1.1, - # or TLS 1, then assume it does not support SSLv3, even if SSLv3 cannot - # be tested. - prln_svrty_good "No fallback possible, TLS 1.3 is the only protocol (OK)" + # If the server supports TLS 1.3, and does not support TLS 1.2, TLS 1.1, or TLS 1, + # then assume it does not support SSLv3, even if SSLv3 cannot be tested. + pr_svrty_good "No fallback possible (OK)"; outln ", TLS 1.3 is the only protocol" fileout "$jsonID" "OK" "only TLS 1.3 supported" elif [[ $(has_server_protocol tls1_3) -eq 1 ]] && \ ( [[ $(has_server_protocol ssl3) -eq 1 ]] || "$HAS_SSL3" ); then @@ -14894,8 +14934,8 @@ run_tls_fallback_scsv() { # If the server does not support TLS 1.3, TLS 1.2, TLS 1.1, or TLS 1, and # support for SSLv3 cannot be tested, then treat it as HIGH severity, since # it is very likely that SSLv3 is the only supported protocol. - prln_svrty_high "No fallback possible, TLS 1.3, TLS 1.2, TLS 1.1, and TLS 1 not supported" - fileout "$jsonID" "HIGH" "TLS 1.3, TLS 1.2, TLS 1.1, and TLS 1 not supported" + pr_svrty_high "NOT ok, no fallback possible"; outln ", TLS 1.3, 1.2, 1.1 and 1.0 not supported" + fileout "$jsonID" "HIGH" "TLS 1.3, 1.2, 1.1, 1.0 not supported" else # TLS 1.2, TLS 1.1, and TLS 1 are not supported, but can't tell whether TLS 1.3 is supported. # This could be a TLS 1.3 only server, an SSLv3 only server (if SSLv3 support cannot be tested), @@ -14924,7 +14964,7 @@ run_tls_fallback_scsv() { if ! "$HAS_SSL3" && \ ( [[ "$low_proto" == ssl3 ]] || \ - ( [[ "$high_proto" == tls1 ]] && [[ $(has_server_protocol "ssl3") -eq 2 ]] ) ); then + ( [[ "$high_proto" == tls1 ]] && [[ $(has_server_protocol ssl3) -eq 2 ]] ) ); then # If the protocol that the server would fall back to is SSLv3, but $OPENSSL does # not support SSLv3, then the test cannot be performed. So, if $OPENSSL does not # support SSLv3 and it is known that SSLv3 is the fallback protocol ($low_proto), then @@ -14941,7 +14981,7 @@ run_tls_fallback_scsv() { if [[ -z "$low_proto" ]]; then case "$high_proto" in "tls1_2") - prln_svrty_good "No fallback possible, no protocol below $high_proto_str offered (OK)" + pr_svrty_good "No fallback possible (OK)"; outln ", no protocol below $high_proto_str offered" ;; *) outln "No fallback possible, no protocol below $high_proto_str offered (OK)" ;; @@ -15026,6 +15066,14 @@ run_freak() { [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for FREAK attack " && outln pr_bold " FREAK"; out " ($cve) " + if "$TLS13_ONLY"; then + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", TLS 1.3 only server" + outln + fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe" + return 0 + fi + "$SSL_NATIVE" && using_sockets=false if "$using_sockets"; then nr_supported_ciphers=$(count_words "$exportrsa_tls_cipher_list_hex")+$(count_words "$exportrsa_ssl2_cipher_list_hex") @@ -15473,7 +15521,6 @@ run_beast(){ local hint="" local jsonID="BEAST" - "$SSL_NATIVE" && using_sockets=false if [[ $VULN_COUNT -le $VULN_THRESHLD ]]; then outln pr_headlineln " Testing for BEAST vulnerability " @@ -15481,8 +15528,17 @@ run_beast(){ fi pr_bold " BEAST"; out " ($cve) " + if "$TLS13_ONLY" || ( [[ $(has_server_protocol ssl3) -eq 1 ]] && [[ $(has_server_protocol tls1) -eq 1 ]] ); then + pr_svrty_good "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no SSL3 or TLS1" + outln + fileout "$jsonID" "OK" "not vulnerable, no SSL3 or TLS1" "$cve" "$cwe" + return 0 + fi + + "$SSL_NATIVE" && using_sockets=false # $cbc_ciphers_hex has 126 ciphers, we omitted SRP-AES-256-CBC-SHA bc the trailing 00,ff below will pose - # a problem for ACE loadbalancers otherwise. So in case we know this is not true, we'll re-add it + # a problem for ACE loadbalancers otherwise. So in case we know this is not true, we'll re-add it ! "$SERVER_SIZE_LIMIT_BUG" & "$using_sockets" && cbc_ciphers_hex="$cbc_ciphers_hex, C0,20" [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false @@ -15564,12 +15620,14 @@ run_beast(){ if [[ $sclient_success -ne 0 ]]; then # protocol supported? if "$continued"; then # second round: we hit TLS1 if "$HAS_SSL3" || "$using_sockets"; then - prln_svrty_good "no SSL3 or TLS1 (OK)" + pr_svrty_good "not vulnerable (OK)" ; out ", no SSL3 or TLS1" fileout "$jsonID" "OK" "not vulnerable, no SSL3 or TLS1" "$cve" "$cwe" else prln_svrty_good "no TLS1 (OK)" fileout "$jsonID" "OK" "not vulnerable, no TLS1" "$cve" "$cwe" fi +set +x +exit 0 return 0 else # protocol not succeeded but it's the first time continued=true @@ -15597,7 +15655,7 @@ run_beast(){ sigalg[nr_ciphers]="" done while true; do - [[ "$proto" == "ssl3" ]] && ! "$HAS_SSL3" && break + [[ "$proto" == ssl3 ]] && ! "$HAS_SSL3" && break ciphers_to_test="" for (( i=0; i < nr_ciphers; i++ )); do ! "${ciphers_found[i]}" && "${ossl_supported[i]}" && ciphers_to_test+=":${ciph[i]}" @@ -15695,7 +15753,7 @@ run_beast(){ detected_cbc_ciphers="" # empty for next round first=false else - [[ $proto == "tls1" ]] && ! $first && echo -n "$spaces " + [[ $proto == tls1 ]] && ! $first && echo -n "$spaces " prln_svrty_good "no CBC ciphers for $(toupper $proto) (OK)" first=false fi @@ -15762,6 +15820,14 @@ run_lucky13() { fi pr_bold " LUCKY13"; out " ($cve), experimental " + if "$TLS13_ONLY"; then + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no CBC ciphers in TLS 1.3 only servers" + outln + fileout "$jsonID" "OK" "not vulnerable, TLS 1.3 only" "$cve" "$cwe" + return 0 + fi + "$SSL_NATIVE" && using_sockets=false # The openssl binary distributed has almost everything we need (PSK, KRB5 ciphers and feff, ffe0 are typically missing). # Measurements show that there's little impact whether we use sockets or TLS here, so the default is sockets here @@ -15841,6 +15907,14 @@ run_rc4() { fi pr_bold " RC4"; out " (${cve// /, }) " + if "$TLS13_ONLY"; then + pr_svrty_best "not vulnerable (OK)" + [[ $DEBUG -ge 1 ]] && out ", no RC4 support in TLS 1.3 only servers" + outln + fileout "$jsonID" "OK" "not vulnerable, TLS 1.3 only" "$cve" "$cwe" + return 0 + fi + # get a list of all the cipher suites to test if "$using_sockets" || [[ $OSSL_VER_MAJOR -lt 1 ]]; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do