diff --git a/testssl.sh b/testssl.sh index b5ea3a2..dc8598b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -952,6 +952,19 @@ hex2ascii() { done } +# convert decimal number < 256 to hex +dec02hex() { + printf "x%02x" "$1" +} + +# convert decimal number between 256 and < 256*256 to hex +dec04hex() { + local a=$(printf "%04x" "$1") + printf "x%02s, x%02s" "${a:0:2}" "${a:2:2}" +} + + + # trim spaces for BSD and old sed count_lines() { #echo "${$(wc -l <<< "$1")// /}" @@ -6912,14 +6925,17 @@ socksend_sslv2_clienthello() { # for SSLv2 to TLS 1.2: sockread_serverhello() { [[ -z "$2" ]] && maxsleep=$MAX_WAITSOCK || maxsleep=$2 - SOCK_REPLY_FILE=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7 dd bs=$1 of=$SOCK_REPLY_FILE count=1 <&5 2>/dev/null & wait_kill $! $maxsleep - return $? } +#trying a faster version +sockread_fast() { + dd bs=$1 count=1 <&5 2>/dev/null | hexdump -v -e '16/1 "%02X"' +} + get_pub_key_size() { local pubkey pubkeybits local -i i len1 len @@ -8777,7 +8793,7 @@ ok_ids(){ #FIXME: At a certain point heartbleed and ccs needs to be changed and make use of code2network using a file, then tls_sockets run_ccs_injection(){ - local tls_proto_offered tls_hexcode ccs_message client_hello byte6 sockreply + local tls_hexcode ccs_message client_hello byte6 sockreply local -i retval ret local tls_hello_ascii="" local cve="CVE-2014-0224" @@ -8789,16 +8805,25 @@ run_ccs_injection(){ [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for CCS injection vulnerability " && outln pr_bold " CCS"; out " ($cve) " - # determine TLS versions offered <-- needs to come from another place - $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE would save 1x connect + #ATTENTION: we don't do SNI here as we assume this is a vulnerabilty of the TLS stack. If we do SNI here, we'd also need to do it in the ClientHello + # of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work! + sessticket_tls="$($OPENSSL s_client $BUGS $OPTIMAL_PROTO $PROXY -connect $NODEIP:$PORT $ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" + sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/ .*$//g' <<< "$sessticket_tls" | tr '\n' ',')" + sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls" +} + + +# see https://blog.filippo.io/finding-ticketbleed/ | http://ticketbleed.com/ +run_ticketbleed() { + local session_tckt_tls="" + local -i len_ch=216 # fixed len of clienthello below + local sid="x00,x0B,xAD,xC0,xDE,x00," # some abitratry bytes + local len_sid="$(( ${#sid} / 4))" + local xlen_sid="$(dec02hex $len_sid)" + local -i len_tckt_tls=0 + local xlen_tckt_tls="" xlen_handshake_record_layer="" xlen_handshake_ssl_layer="" + local -i len_handshake_record_layer=0 + local cve="CVE-2016-9244" + local cwe="CWE-200" + local hint="" + local tls_version="" + + [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for Ticketbleed vulnerability " && outln + pr_bold " Ticketbleed"; out " ($cve), experiment. " + + [[ "$SERVICE" != HTTP ]] && prln "-- (applicable only for HTTPS)" && return 0 + + if $(has_server_protocol "tls1"); then + tls_hexcode="x03, x01" + elif $(has_server_protocol "tls1_1"); then + tls_hexcode="x03, x02" + elif $(has_server_protocol "tls1_2"); then + tls_hexcode="x03, x03" + elif $(has_server_protocol "ssl3"); then + tls_hexcode="x03, x00" + else # no protocol for some reason defined, determine TLS versions offered with another handshake + $OPENSSL s_client $BUGS $OPTIMAL_PROTO -connect $NODEIP:$PORT $PROXY $SNI >$TMPFILE 2>$ERRFILE $FUNCNAME.txt + tmpfile_handle $FUNCNAME.txt + fi + close_socket + return $ret +} + + run_renego() { # no SNI here. Not needed as there won't be two different SSL stacks for one IP local legacycmd="" @@ -10638,14 +10860,15 @@ single check as ("$PROG_NAME URI" does everything except -E): -x, --single-cipher tests matched of ciphers (if not a number: word match) -c, --client-simulation test client simulations, see which client negotiates with cipher and protocol - -H, --header, --headers tests HSTS, HPKP, server/app banner, security headers, cookie, reverse proxy, IPv4 address + -h, --header, --headers tests HSTS, HPKP, server/app banner, security headers, cookie, reverse proxy, IPv4 address -U, --vulnerable tests all (of the following) vulnerabilities (if applicable) - -B, --heartbleed tests for heartbleed vulnerability + -H, --heartbleed tests for heartbleed vulnerability -I, --ccs, --ccs-injection tests for CCS injection vulnerability + -T, --ticketbleed tests for Ticketbleed vulnerability in BigIP loadbalancers -R, --renegotiation tests for renegotiation vulnerabilities -C, --compression, --crime tests for CRIME vulnerability (TLS compression issue) - -T, --breach tests for BREACH vulnerability (HTTP compression issue) + -B, --breach tests for BREACH vulnerability (HTTP compression issue) -O, --poodle tests for POODLE (SSL) vulnerability -Z, --tls-fallback checks TLS_FALLBACK_SCSV mitigation -W, --sweet32 tests 64 bit block ciphers (3DES, RC2 and IDEA): SWEET32 vulnerability @@ -10856,7 +11079,7 @@ EOF modification under GPLv2 permitted. USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK! - Please file bugs @ + Please file bugs @ EOF ) bb3=$(cat <$ERRFILE if [[ -n "$1" ]]; then - # starttls workaround needed see https://github.com/drwetter/testssl.sh/issues/188 - # kind of odd + # starttls workaround needed see https://github.com/drwetter/testssl.sh/issues/188 -- kind of odd for STARTTLS_OPTIMAL_PROTO in -tls1_2 -tls1 -ssl3 -tls1_1 -ssl2; do $OPENSSL s_client $STARTTLS_OPTIMAL_PROTO $BUGS -connect "$NODEIP:$PORT" $PROXY -msg -starttls $1 $TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then - all_failed=1 + all_failed=false break fi - all_failed=0 + all_failed=true done - [[ $all_failed -eq 0 ]] && STARTTLS_OPTIMAL_PROTO="" + "$all_failed" && STARTTLS_OPTIMAL_PROTO="" debugme echo "STARTTLS_OPTIMAL_PROTO: $STARTTLS_OPTIMAL_PROTO" else for OPTIMAL_PROTO in '' -tls1_2 -tls1 -ssl3 -tls1_1 -ssl2; do [[ "$OPTIMAL_PROTO" =~ ssl ]] && sni="" || sni=$SNI $OPENSSL s_client $OPTIMAL_PROTO $BUGS -connect "$NODEIP:$PORT" -msg $PROXY $sni $TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then - all_failed=1 + # we use the successful handshake at least to get one valid protocol supported -- it saves us time later + if [[ -z "$OPTIMAL_PROTO" ]]; then + # convert to openssl terminology + tmp=$(get_protocol $TMPFILE) + tmp=${tmp/\./_} + tmp=${tmp/v/} + tmp="$(tolower $tmp)" + add_tls_offered "$tmp" + else + add_tls_offered "${OPTIMAL_PROTO/-/}" + fi + debugme echo "one proto determined: $tmp" + all_failed=false break fi - all_failed=0 + all_failed=true done - [[ $all_failed -eq 0 ]] && OPTIMAL_PROTO="" + "$all_failed" && OPTIMAL_PROTO="" debugme echo "OPTIMAL_PROTO: $OPTIMAL_PROTO" if [[ "$OPTIMAL_PROTO" == "-ssl2" ]]; then prln_magenta "$NODEIP:$PORT appears to only support SSLv2." @@ -11490,7 +11723,7 @@ determine_optimal_proto() { fi grep -q '^Server Temp Key' $TMPFILE && HAS_DH_BITS=true # FIX #190 - if [[ $all_failed -eq 0 ]]; then + if "$all_failed"; then outln if "$HAS_IPv6"; then pr_bold " Your $OPENSSL is not IPv6 aware, or $NODEIP:$PORT " @@ -11889,6 +12122,7 @@ initialize_globals() { do_lucky13=false do_breach=false do_ccs_injection=false + do_ticketbleed=false do_cipher_per_proto=false do_crime=false do_freak=false @@ -11931,6 +12165,7 @@ set_scanning_defaults() { do_breach=true do_heartbleed=true do_ccs_injection=true + do_ticketbleed=true do_crime=true do_freak=true do_logjam=true @@ -11956,7 +12191,7 @@ query_globals() { local gbl local true_nr=0 - for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_cipher_per_proto do_crime \ + for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_pfs do_protocols do_rc4 do_renego \ do_std_cipherlists do_server_defaults do_server_preference do_spdy do_http2 do_ssl_poodle do_tls_fallback_scsv \ do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only; do @@ -11969,7 +12204,7 @@ query_globals() { debug_globals() { local gbl - for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_cipher_per_proto do_crime \ + for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_pfs do_protocols do_rc4 do_renego \ do_std_cipherlists do_server_defaults do_server_preference do_spdy do_http2 do_ssl_poodle do_tls_fallback_scsv \ do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only; do @@ -12107,7 +12342,7 @@ parse_cmd_line() { -P|--server[_-]preference|--preference) do_server_preference=true ;; - -H|--header|--headers) + -h|--header|--headers) do_header=true ;; -c|--client-simulation) @@ -12117,6 +12352,7 @@ parse_cmd_line() { do_vulnerabilities=true do_heartbleed=true do_ccs_injection=true + do_ticketbleed=true do_renego=true do_crime=true do_breach=true @@ -12131,7 +12367,7 @@ parse_cmd_line() { do_rc4=true VULN_COUNT=10 ;; - -B|--heartbleed) + -H|--heartbleed) do_heartbleed=true let "VULN_COUNT++" ;; @@ -12139,6 +12375,10 @@ parse_cmd_line() { do_ccs_injection=true let "VULN_COUNT++" ;; + -T|--ticketbleed) + do_ticketbleed=true + let "VULN_COUNT++" + ;; -R|--renegotiation) do_renego=true let "VULN_COUNT++" @@ -12147,7 +12387,7 @@ parse_cmd_line() { do_crime=true let "VULN_COUNT++" ;; - -T|--breach) + -B|--breach) do_breach=true let "VULN_COUNT++" ;; @@ -12486,6 +12726,7 @@ lets_roll() { fileout_section_header $section_number true && ((section_number++)) $do_heartbleed && { run_heartbleed; ret=$(($? + ret)); time_right_align run_heartbleed; } $do_ccs_injection && { run_ccs_injection; ret=$(($? + ret)); time_right_align run_ccs_injection; } + $do_ticketbleed && { run_ticketbleed; ret=$(($? + ret)); time_right_align run_ticketbleed; } $do_renego && { run_renego; ret=$(($? + ret)); time_right_align run_renego; } $do_crime && { run_crime; ret=$(($? + ret)); time_right_align run_crime; } $do_breach && { run_breach "$URL_PATH" ; ret=$(($? + ret)); time_right_align run_breach; }