diff --git a/testssl.sh b/testssl.sh index 2c831a8..a487812 100755 --- a/testssl.sh +++ b/testssl.sh @@ -648,10 +648,12 @@ fi runs_HTTP() { local -i ret=0 local -i was_killed + local addcmd="" if ! $CLIENT_AUTH; then # SNI is nonsense for !HTTPS but fortunately for other protocols s_client doesn't seem to care - printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE & + [[ ! "$1" =~ ssl ]] && addcmd="$SNI" + printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $addcmd >$TMPFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP was_killed=$? head $TMPFILE | grep -aq ^HTTP && SERVICE=HTTP @@ -701,7 +703,7 @@ runs_HTTP() { #problems not handled: chunked run_http_header() { - local header + local header addcmd="" local -i ret local referer useragent local url redirect @@ -711,12 +713,13 @@ run_http_header() { outln [[ -z "$1" ]] && url="/" || url="$1" - printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI >$HEADERFILE 2>$ERRFILE & + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd="$SNI" + printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $addcmd >$HEADERFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP if [[ $? -eq 0 ]]; then # we do the get command again as it terminated within $HEADER_MAXSLEEP. Thus it didn't hang, we do it # again in the foreground to get an accurate header time! - printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI >$HEADERFILE 2>$ERRFILE + printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $addcmd >$HEADERFILE 2>$ERRFILE NOW_TIME=$(date "+%s") HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $HEADERFILE) HAD_SLEPT=0 @@ -1010,7 +1013,7 @@ run_hpkp() { # print key=value pair with awk, then strip non-numbers, to be improved with proper parsing of key-value with awk hpkp_age_sec=$(awk -F= '/max-age/{max_age=$2; print max_age}' $TMPFILE | sed -E 's/[^[:digit:]]//g') hpkp_age_days=$((hpkp_age_sec / 86400)) - if [[ $hpkp_age_days -ge $HPKP_MIN ]]; then + if [[ $hpkp_age_sec -ge $HPKP_MIN ]]; then pr_done_good "$hpkp_age_days days" ; out "=$hpkp_age_sec s" fileout "hpkp_age" "OK" "HPKP age is set to $hpkp_age_days days ($hpkp_age_sec sec)" else @@ -1373,11 +1376,12 @@ prettyprint_local() { # list ciphers (and makes sure you have them locally configured) # arg[1]: cipher list (or anything else) +# arg[2]: protocol (e.g., -ssl2) listciphers() { local -i ret local debugname="$(sed -e s'/\!/not/g' -e 's/\:/_/g' <<< "$1")" - $OPENSSL ciphers "$1" &>$TMPFILE + $OPENSSL ciphers $2 "$1" &>$TMPFILE ret=$? debugme cat $TMPFILE @@ -1391,12 +1395,14 @@ listciphers() { # argv[3]: ok to offer? 0: yes, 1: no std_cipherlists() { local -i sclient_success - local singlespaces + local singlespaces proto="" addcmd="" local debugname="$(sed -e s'/\!/not/g' -e 's/\:/_/g' <<< "$1")" + [[ "$OPTIMAL_PROTO" == "-ssl2" ]] && addcmd="$OPTIMAL_PROTO" && proto="$OPTIMAL_PROTO" + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd="$SNI" pr_bold "$2 " # indent in order to be in the same row as server preferences - if listciphers "$1"; then # is that locally available?? - $OPENSSL s_client -cipher "$1" $BUGS $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI 2>$ERRFILE >$TMPFILE $ERRFILE >$TMPFILE $ERRFILE >$TMPFILE + [[ "$OPTIMAL_PROTO" == "-ssl2" ]] && addcmd="$OPTIMAL_PROTO" + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd="$SNI" && sni="$SNI" + $OPENSSL s_client $STARTTLS -cipher $list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd $ERRFILE >$TMPFILE if ! sclient_connect_successful $? $TMPFILE && [[ -z "$STARTTLS_PROTOCOL" ]]; then pr_warning "no matching cipher in this list found (pls report this): " outln "$list_fwd . " @@ -3349,7 +3361,8 @@ run_server_preference() { # workaround is to connect with a protocol debugme out "(workaround #188) " determine_optimal_proto $STARTTLS_PROTOCOL - $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $SNI $ERRFILE >$TMPFILE + [[ ! "$STARTTLS_OPTIMAL_PROTO" =~ ssl ]] && addcmd2="$SNI" + $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_fwd $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2 $ERRFILE >$TMPFILE if ! sclient_connect_successful $? $TMPFILE; then pr_warning "no matching cipher in this list found (pls report this): " outln "$list_fwd . " @@ -3361,7 +3374,15 @@ run_server_preference() { if $has_cipher_order; then cipher1=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g') - $OPENSSL s_client $STARTTLS $STARTTLS_OPTIMAL_PROTO -cipher $list_reverse $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$ERRFILE >$TMPFILE + addcmd2="" + if [[ -n "$STARTTLS_OPTIMAL_PROTO" ]]; then + addcmd2="$STARTTLS_OPTIMAL_PROTO" + [[ ! "$STARTTLS_OPTIMAL_PROTO" =~ ssl ]] && addcmd2="$addcmd2 $SNI" + else + [[ "$OPTIMAL_PROTO" == "-ssl2" ]] && addcmd2="$OPTIMAL_PROTO" + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd2="$addcmd2 $SNI" + fi + $OPENSSL s_client $STARTTLS -cipher $list_reverse $BUGS -connect $NODEIP:$PORT $PROXY $addcmd2 >$ERRFILE >$TMPFILE # that worked above so no error handling here cipher2=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g') @@ -3378,10 +3399,10 @@ run_server_preference() { outln pr_bold " Negotiated protocol " - $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$ERRFILE >$TMPFILE + $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $addcmd >$ERRFILE >$TMPFILE if ! sclient_connect_successful $? $TMPFILE; then # 2 second try with $OPTIMAL_PROTO especially for intolerant IIS6 servers: - $OPENSSL s_client $STARTTLS $OPTIMAL_PROTO $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$ERRFILE >$TMPFILE + $OPENSSL s_client $STARTTLS $OPTIMAL_PROTO $BUGS -connect $NODEIP:$PORT $PROXY $sni >$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || pr_warning "Handshake error!" fi default_proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') @@ -3880,7 +3901,7 @@ sclient_connect_successful() { # arg1 is "-cipher " or empty # arg2 is a list of protocols to try (tls1_2, tls1_1, tls1, ssl3) or empty (if all should be tried) determine_tls_extensions() { - local proto + local proto addcmd local success local alpn="" local savedir @@ -3896,14 +3917,48 @@ determine_tls_extensions() { # throwing 1st every cipher/protocol at the server to know what works success=7 + + if [[ "$OPTIMAL_PROTO" == "-ssl2" ]]; then + $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY -ssl2 $ERRFILE >$TMPFILE + sclient_connect_successful $? $TMPFILE && success=0 + if [[ $success -eq 0 ]]; then + # Place the server's certificate in $HOSTCERT and any intermediate + # certificates that were provided in $TEMPDIR/intermediatecerts.pem + savedir=$(pwd); cd $TEMPDIR + # http://backreference.org/2010/05/09/ocsp-verification-with-openssl/ + awk -v n=-1 '/Server certificate/ {start=1} + /-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} } + inc { print > ("level" n ".crt") } + /---END CERTIFICATE-----/{ inc=0 }' $TMPFILE + nrsaved=$(count_words "$(echo level?.crt 2>/dev/null)") + if [[ $nrsaved -eq 0 ]]; then + success=1 + else + success=0 + mv level0.crt $HOSTCERT + if [[ $nrsaved -eq 1 ]]; then + echo "" > $TEMPDIR/intermediatecerts.pem + else + cat level?.crt > $TEMPDIR/intermediatecerts.pem + rm level?.crt + fi + fi + cd "$savedir" + fi + tmpfile_handle $FUNCNAME.txt + return $success + fi + for proto in $protocols_to_try; do # alpn: echo | openssl s_client -connect google.com:443 -tlsextdebug -alpn h2-14 -servername google.com <-- suport needs to be checked b4 -- see also: ssl/t1_trce.c - $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug -nextprotoneg $alpn -status $ERRFILE >$TMPFILE + addcmd="" + [[ ! "$proto" =~ ssl ]] && addcmd="$SNI" + $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $addcmd -$proto -tlsextdebug -nextprotoneg $alpn -status $ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE && success=0 && break done # this loop is needed for IIS6 and others which have a handshake size limitations if [[ $success -eq 7 ]]; then # "-status" above doesn't work for GOST only servers, so we do another test without it and see whether that works then: - $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug >$ERRFILE >$TMPFILE + $OPENSSL s_client $STARTTLS $BUGS $1 -showcerts -connect $NODEIP:$PORT $PROXY $addcmd -$proto -tlsextdebug >$ERRFILE >$TMPFILE if ! sclient_connect_successful $? $TMPFILE; then if [ -z "$1" ]; then pr_warningln "Strange, no SSL/TLS protocol seems to be supported (error around line $((LINENO - 6)))" @@ -5728,20 +5783,22 @@ socksend_tls_clienthello() { fi done - #formatted example for SNI - #00 00 # extension server_name - #00 1a # length = the following +2 = server_name length + 5 - #00 18 # server_name list_length = server_name length +3 - #00 # server_name type (hostname) - #00 15 # server_name length - #66 66 66 66 66 66 2e 66 66 66 66 66 66 66 66 66 66 2e 66 66 66 target.mydomain1.tld # server_name target - len_servername=${#NODE} - hexdump_format_str="$len_servername/1 \"%02x,\"" - servername_hexstr=$(printf $NODE | hexdump -v -e "${hexdump_format_str}" | sed 's/,$//') - # convert lengths we need to fill in from dec to hex: - len_servername_hex=$(printf "%02x\n" $len_servername) - len_sni_listlen=$(printf "%02x\n" $((len_servername+3))) - len_sni_ext=$(printf "%02x\n" $((len_servername+5))) + if [[ -n "$SNI" ]]; then + #formatted example for SNI + #00 00 # extension server_name + #00 1a # length = the following +2 = server_name length + 5 + #00 18 # server_name list_length = server_name length +3 + #00 # server_name type (hostname) + #00 15 # server_name length + #66 66 66 66 66 66 2e 66 66 66 66 66 66 66 66 66 66 2e 66 66 66 target.mydomain1.tld # server_name target + len_servername=${#NODE} + hexdump_format_str="$len_servername/1 \"%02x,\"" + servername_hexstr=$(printf $NODE | hexdump -v -e "${hexdump_format_str}" | sed 's/,$//') + # convert lengths we need to fill in from dec to hex: + len_servername_hex=$(printf "%02x\n" $len_servername) + len_sni_listlen=$(printf "%02x\n" $((len_servername+3))) + len_sni_ext=$(printf "%02x\n" $((len_servername+5))) + fi extension_signature_algorithms=" 00, 0d, # Type: signature_algorithms , see RFC 5246 @@ -5771,16 +5828,20 @@ socksend_tls_clienthello() { 01, 00" all_extensions=" - 00, 00 # extension server_name - ,00, $len_sni_ext # length SNI EXT - ,00, $len_sni_listlen # server_name list_length - ,00 # server_name type (hostname) - ,00, $len_servername_hex # server_name length. We assume len(hostname) < FF - 9 - ,$servername_hexstr # server_name target - ,$extension_heartbeat + $extension_heartbeat ,$extension_session_ticket ,$extension_next_protocol" + if [[ -n "$SNI" ]]; then + all_extensions="$all_extensions + ,00, 00 # extension server_name + ,00, $len_sni_ext # length SNI EXT + ,00, $len_sni_listlen # server_name list_length + ,00 # server_name type (hostname) + ,00, $len_servername_hex # server_name length. We assume len(hostname) < FF - 9 + ,$servername_hexstr" # server_name target + fi + # RFC 5246 says that clients MUST NOT offer the signature algorithms # extension if they are offering TLS versions prior to 1.2. if [[ "0x$tls_low_byte" -ge "0x03" ]]; then @@ -6218,13 +6279,14 @@ run_renego() { # no SNI here. Not needed as there won't be two different SSL stacks for one IP local legacycmd="" local insecure_renogo_str="Secure Renegotiation IS NOT" - local sec_renego sec_client_renego + local sec_renego sec_client_renego addcmd="" [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for Renegotiation vulnerabilities " && outln pr_bold " Secure Renegotiation "; out "(CVE-2009-3555) " # and RFC5746, OSVDB 59968-59974 # community.qualys.com/blogs/securitylabs/2009/11/05/ssl-and-tls-authentication-gap-vulnerability-discovered - $OPENSSL s_client $OPTIMAL_PROTO $STARTTLS $BUGS -connect $NODEIP:$PORT $SNI $PROXY 2>&1 $TMPFILE 2>$ERRFILE + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd="$SNI" + $OPENSSL s_client $OPTIMAL_PROTO $STARTTLS $BUGS -connect $NODEIP:$PORT $addcmd $PROXY 2>&1 $TMPFILE 2>$ERRFILE if sclient_connect_successful $? $TMPFILE; then grep -iaq "$insecure_renogo_str" $TMPFILE sec_renego=$? # 0= Secure Renegotiation IS NOT supported @@ -6277,7 +6339,7 @@ run_renego() { else # We need up to two tries here, as some LiteSpeed servers don't answer on "R" and block. Thus first try in the background # msg enables us to look deeper into it while debugging - echo R | $OPENSSL s_client $OPTIMAL_PROTO $BUGS $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $SNI $PROXY >$TMPFILE 2>>$ERRFILE & + echo R | $OPENSSL s_client $OPTIMAL_PROTO $BUGS $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $addcmd $PROXY >$TMPFILE 2>>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP if [[ $? -eq 3 ]]; then pr_done_good "likely not vulnerable (OK)"; outln " (timed out)" # it hung @@ -6285,7 +6347,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 $legacycmd $STARTTLS $BUGS -msg -connect $NODEIP:$PORT $SNI $PROXY >$TMPFILE 2>>$ERRFILE + echo R | $OPENSSL s_client $legacycmd $STARTTLS $BUGS -msg -connect $NODEIP:$PORT $addcmd $PROXY >$TMPFILE 2>>$ERRFILE sec_client_renego=$? # 0=client is renegotiating & doesn't return an error --> vuln! case "$sec_client_renego" in 0) @@ -6397,7 +6459,7 @@ run_crime() { # to the version of TLS/SSL, more: http://www.breachattack.com/ . Foreign referrers are the important thing here! # Mitigation: see https://community.qualys.com/message/20360 run_breach() { - local header + local header addcmd="" local -i ret=0 local -i was_killed=0 local referer useragent @@ -6421,7 +6483,8 @@ run_breach() { useragent="$UA_STD" $SNEAKY && useragent="$UA_SNEAKY" - printf "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: gzip,deflate,compress\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI 1>$TMPFILE 2>$ERRFILE & + [[ ! "$OPTIMAL_PROTO" =~ ssl ]] && addcmd="$SNI" + printf "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: gzip,deflate,compress\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $addcmd 1>$TMPFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP was_killed=$? # !=0 was killed result=$(awk '/^Content-Encoding/ { print $2 }' $TMPFILE) @@ -6509,7 +6572,12 @@ run_tls_fallback_scsv() { # c) best to make sure that we hit a specific protocol, see https://alpacapowered.wordpress.com/2014/10/20/ssl-poodle-attack-what-is-this-scsv-thingy/ # d) minor: we should do "-state" here - # first: make sure we have tls1_2: + # first: make sure SSLv3 or some TLS protocol is supported + if [[ "$OPTIMAL_PROTO" == "-ssl2" ]]; then + pr_svrty_criticalln "No fallback possible, SSLv2 is the only protocol" + return 7 + fi + # second: make sure we have tls1_2: $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI -no_tls1_2 >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE $TMPFILE 2>>$ERRFILE $TMPFILE 2>>$ERRFILE $TMPFILE 2>>$ERRFILE $TMPFILE 2>>$ERRFILE >$ERRFILE) # -V doesn't work with openssl < 1.0 # ^^^^^ process substitution as shopt will either segfault or doesn't work with old bash versions - $OPENSSL s_client -cipher "$cbc_cipher" -"$proto" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>>$ERRFILE $TMPFILE 2>>$ERRFILE /dev/null | egrep -v ':|^#' | egrep "[[:space:]]$1" | awk '{ print $1 }') + ip4=$(grep -wh "$1[^\.]" $etchosts 2>/dev/null | egrep -v ':|^#' | egrep "[[:space:]]$1" | awk '{ print $1 }') if is_ipv4addr "$ip4"; then echo "$ip4" else @@ -8679,4 +8751,4 @@ fi exit $? -# $Id: testssl.sh,v 1.535 2016/09/01 10:42:53 dirkw Exp $ +# $Id: testssl.sh,v 1.537 2016/09/01 17:09:11 dirkw Exp $