From ad5590a444b0dfcf9fbca6f19165d6e3bed8ab75 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 4 Jan 2017 10:19:11 -0500 Subject: [PATCH 1/4] run_server_defaults() bugfix If `determine_tls_extensions()` does not create a temporary file (`$TEMPDIR/$NODEIP.determine_tls_extensions.txt`) then `run_server_defaults()` will display error messages when an attempt is made to copy this file or to search (grep) it. This may happen if `$OPTIMAL_PROTO` is `-ssl2` or if `determine_tls_extensions()` uses sockets and `parse_tls_serverhello()` encountered an error and did not create a temporary file (`$TEMPDIR/$NODEIP.parse_tls_serverhello.txt`). This PR fixes this by only trying to copy and search `$TEMPDIR/$NODEIP.determine_tls_extensions.txt` is `$OPTIMAL_PROTO` is not `-ssl2` and `determine_tls_extensions()` was successful (return value 0). --- testssl.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 80a8ac8..fc6287e 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6097,11 +6097,12 @@ run_server_defaults() { done determine_tls_extensions + if [[ $? -eq 0 ]] && [[ "$OPTIMAL_PROTO" != "-ssl2" ]]; then + cp "$TEMPDIR/$NODEIP.determine_tls_extensions.txt" $TMPFILE + >$ERRFILE - cp "$TEMPDIR/$NODEIP.determine_tls_extensions.txt" $TMPFILE - >$ERRFILE - - [[ -z "$sessticket_str" ]] && sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime) + [[ -z "$sessticket_str" ]] && sessticket_str=$(grep -aw "session ticket" $TMPFILE | grep -a lifetime) + fi outln pr_headlineln " Testing server defaults (Server Hello) " From d66e5ec0d7b0b307cbb94405866336288697f3ef Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 4 Jan 2017 10:47:36 -0500 Subject: [PATCH 2/4] sslv2_sockets() bug fixes This PR fixes a few bugs in `sslv2_sockets()`. The main issue is that a server may not send the entire ServerHello in a single packet. If it doesn't and the full response is being parsed (i.e., certificate and list of ciphers), then `parse_sslv2_serverhello()` will encounter errors, since it assumes that it has the entire ServerHello. This PR compares the length of the response to the length of the ServerHello as specified in the first two bytes of the response and requests more data from the server if the response appears incomplete. This PR also modifies `parse_sslv2_serverhello()` to check for more errors. It compares the length of the response it has been provided to the specified length (`$v2_hello_length`) and returns an error if the response is shorter than `$v2_hello_length` and the full response is supposed to be parsed. It will also check whether there was an error in converting the certificate from DER to PEM format and will return an error if there was (and it will suppress the error message). --- testssl.sh | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index 80a8ac8..5c133d1 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7063,6 +7063,7 @@ parse_sslv2_serverhello() { "$parse_complete" && echo "======================================" > $TMPFILE v2_hello_ascii=$(hexdump -v -e '16/1 "%02X"' $1) + v2_hello_ascii="${v2_hello_ascii%%[!0-9A-F]*}" [[ "$DEBUG" -ge 5 ]] && echo "$v2_hello_ascii" if [[ -z "$v2_hello_ascii" ]]; then ret=0 # 1 line without any blanks: no server hello received @@ -7094,6 +7095,10 @@ parse_sslv2_serverhello() { echo "SSLv2 certificate length: 0x$v2_hello_cert_length" echo "SSLv2 cipher spec length: 0x$v2_hello_cipherspec_length" fi + + if "$parse_complete" && [[ 2*$(hex2dec "$v2_hello_length") -ne ${#v2_hello_ascii}-4 ]]; then + ret=7 + fi fi "$parse_complete" || return $ret @@ -7101,11 +7106,15 @@ parse_sslv2_serverhello() { rm -f $HOSTCERT $TEMPDIR/intermediatecerts.pem if [[ $ret -eq 3 ]]; then certificate_len=2*$(hex2dec "$v2_hello_cert_length") - + if [[ "$v2_cert_type" == "01" ]] && [[ "$v2_hello_cert_length" != "00" ]]; then tmp_der_certfile=$(mktemp $TEMPDIR/der_cert.XXXXXX) || return $ret asciihex_to_binary_file "${v2_hello_ascii:26:certificate_len}" "$tmp_der_certfile" - $OPENSSL x509 -inform DER -in $tmp_der_certfile -outform PEM -out $HOSTCERT + $OPENSSL x509 -inform DER -in $tmp_der_certfile -outform PEM -out $HOSTCERT 2>$ERRFILE + if [[ $? -ne 0 ]]; then + debugme echo "Malformed certificate in ServerHello." + return 1 + fi rm $tmp_der_certfile get_pub_key_size echo "======================================" >> $TMPFILE @@ -8100,6 +8109,13 @@ sslv2_sockets() { local ret local client_hello cipher_suites len_client_hello local len_ciph_suites_byte len_ciph_suites + local server_hello sock_reply_file2 + local -i response_len server_hello_len + local parse_complete=false + + if [[ "$2" == "true" ]]; then + parse_complete=true + fi if [[ -n "$1" ]]; then cipher_suites="$1" @@ -8140,13 +8156,31 @@ sslv2_sockets() { socksend_sslv2_clienthello "$client_hello" sockread_serverhello 32768 + if "$parse_complete"; then + server_hello=$(hexdump -v -e '16/1 "%02X"' "$SOCK_REPLY_FILE") + server_hello_len=2+$(hex2dec "${server_hello:1:3}") + response_len=$(wc -c "$SOCK_REPLY_FILE" | awk '{ print $1 }') + for (( 1; response_len < server_hello_len; 1 )); do + sock_reply_file2=$(mktemp $TEMPDIR/ddreply.XXXXXX) || return 7 + mv "$SOCK_REPLY_FILE" "$sock_reply_file2" + + debugme echo "requesting more server hello data..." + socksend "" $USLEEP_SND + sockread_serverhello 32768 + + [[ ! -s "$SOCK_REPLY_FILE" ]] && break + cat "$SOCK_REPLY_FILE" >> "$sock_reply_file2" + mv "$sock_reply_file2" "$SOCK_REPLY_FILE" + response_len=$(wc -c "$SOCK_REPLY_FILE" | awk '{ print $1 }') + done + fi debugme outln "reading server hello... " if [[ "$DEBUG" -ge 4 ]]; then hexdump -C "$SOCK_REPLY_FILE" | head -6 outln fi - parse_sslv2_serverhello "$SOCK_REPLY_FILE" "$2" + parse_sslv2_serverhello "$SOCK_REPLY_FILE" "$parse_complete" ret=$? close_socket From 70e6e289e1572770d52572350dc4e7ff85d6e86e Mon Sep 17 00:00:00 2001 From: Todd Swatling Date: Thu, 5 Jan 2017 14:20:19 -0500 Subject: [PATCH 3/4] removed trailing spaces --- testssl.sh | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/testssl.sh b/testssl.sh index 80a8ac8..fc010c3 100755 --- a/testssl.sh +++ b/testssl.sh @@ -148,7 +148,7 @@ BUGS=${BUGS:-""} # -bugs option from openssl, needed for DEBUG=${DEBUG:-0} # 1: normal putput the files in /tmp/ are kept for further debugging purposes # 2: list more what's going on , also lists some errors of connections # 3: slight hexdumps + other info, - # 4: display bytes sent via sockets + # 4: display bytes sent via sockets # 5: display bytes received via sockets # 6: whole 9 yards FAST=${FAST:-false} # preference: show only first cipher, run_allciphers with openssl instead of sockets @@ -159,7 +159,7 @@ CSVFILE=${CSVFILE:-""} # csvfile if used APPEND=${APPEND:-false} # append to csv/json file instead of overwriting it GIVE_HINTS=false # give an addtional info to findings HAS_IPv6=${HAS_IPv6:-false} # if you have OpenSSL with IPv6 support AND IPv6 networking set it to yes -UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses +UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses SERVER_SIZE_LIMIT_BUG=false # Some servers have either a ClientHello total size limit or cipher limit of ~128 ciphers (e.g. old ASAs) # tuning vars, can not be set by a cmd line switch @@ -556,7 +556,7 @@ declare TLS_CIPHER_OSSL_SUPPORTED=() ###### output functions ###### # a little bit of sanitzing with bash internal search&replace -- otherwise printf will hiccup at '%' and '--' does the rest. -out(){ +out(){ # if [[ "$BASH_VERSINFO" -eq 4 ]]; then printf -- "%b" "${1//%/%%}" # else @@ -590,10 +590,10 @@ pr_greyln() { pr_grey "$1"; outln; } pr_done_good() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[0;34m$1" || out "\033[0;32m$1" ) || out "$1"; pr_off; } # litegreen (liteblue), This is good pr_done_goodln() { pr_done_good "$1"; outln; } -pr_done_best() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;34m$1" || out "\033[1;32m$1" ) || out "$1"; pr_off; } # green (blue), This is the best +pr_done_best() { [[ "$COLOR" -eq 2 ]] && ( "$COLORBLIND" && out "\033[1;34m$1" || out "\033[1;32m$1" ) || out "$1"; pr_off; } # green (blue), This is the best pr_done_bestln() { pr_done_best "$1"; outln; } -pr_svrty_minor() { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; } # yellow brown | academic or minor problem +pr_svrty_minor() { [[ "$COLOR" -eq 2 ]] && out "\033[1;33m$1" || out "$1"; pr_off; } # yellow brown | academic or minor problem pr_svrty_minorln() { pr_svrty_minor "$1"; outln; } pr_svrty_medium() { [[ "$COLOR" -eq 2 ]] && out "\033[0;33m$1" || out "$1"; pr_off; } # brown | it is not a bad problem but you shouldn't do this pr_svrty_mediumln() { pr_svrty_medium "$1"; outln; } @@ -1082,7 +1082,7 @@ string_to_asciihex() { [[ -n "$string" ]] && output+="$(printf "%02x" "'${string:eos:1}")" out "$output" return 0 - + } ###### check code starts here ###### @@ -6221,7 +6221,7 @@ run_pfs() { done < <($OPENSSL ciphers -V "$pfs_cipher_list" 2>$ERRFILE) fi export="" - + if "$using_sockets"; then tls_sockets "03" "${pfs_hex_cipher_list:2}" sclient_success=$? @@ -7101,7 +7101,7 @@ parse_sslv2_serverhello() { rm -f $HOSTCERT $TEMPDIR/intermediatecerts.pem if [[ $ret -eq 3 ]]; then certificate_len=2*$(hex2dec "$v2_hello_cert_length") - + if [[ "$v2_cert_type" == "01" ]] && [[ "$v2_hello_cert_length" != "00" ]]; then tmp_der_certfile=$(mktemp $TEMPDIR/der_cert.XXXXXX) || return $ret asciihex_to_binary_file "${v2_hello_ascii:26:certificate_len}" "$tmp_der_certfile" @@ -7222,7 +7222,7 @@ parse_tls_serverhello() { local process_full="$2" local tls_handshake_ascii="" tls_alert_ascii="" local -i tls_hello_ascii_len tls_handshake_ascii_len tls_alert_ascii_len msg_len - local tls_serverhello_ascii="" tls_certificate_ascii="" + local tls_serverhello_ascii="" tls_certificate_ascii="" local tls_serverkeyexchange_ascii="" tls_certificate_status_ascii="" local -i tls_serverhello_ascii_len=0 tls_certificate_ascii_len=0 local -i tls_serverkeyexchange_ascii_len=0 tls_certificate_status_ascii_len=0 @@ -8029,7 +8029,7 @@ parse_tls_serverhello() { len1="82$(printf "%04x" $((dh_param_len/2)))" fi dh_param="30${len1}${dh_p}${dh_g}" - + # Make a SEQUENCE of the paramters SEQUENCE and the OID dh_param_len=22+${#dh_param} if [[ $dh_param_len -lt 256 ]]; then @@ -8069,7 +8069,7 @@ parse_tls_serverhello() { [[ -n "$key_bitstring" ]] && echo "$key_bitstring" >> $TMPFILE # Check to see whether the ephemeral public key uses one of the groups from - # RFC 7919 for parameters + # RFC 7919 for parameters case $dh_bits in 2048) named_curve=256; named_curve_str=" ffdhe2048," ;; 3072) named_curve=257; named_curve_str=" ffdhe3072," ;; @@ -8263,7 +8263,7 @@ socksend_tls_clienthello() { 00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11" # Supported Point Formats Extension extension_supported_point_formats=" - 00, 0b, # Type: Supported Point Formats , see RFC 4492 + 00, 0b, # Type: Supported Point Formats , see RFC 4492 00, 02, # len 01, 00" fi @@ -9641,7 +9641,7 @@ run_rc4() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]="${TLS_CIPHER_OSSL_SUPPORTED[i]}" - if "$using_sockets" && "$WIDE" && ! "$HAS_DH_BITS" && + if "$using_sockets" && "$WIDE" && ! "$HAS_DH_BITS" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi @@ -9897,7 +9897,7 @@ get_install_dir() { fi if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] ; then - unset ADD_RFC_STR + unset ADD_RFC_STR debugme echo "$CIPHERS_BY_STRENGTH_FILE" pr_warningln "\nATTENTION: No cipher mapping file found!" outln "Please note from 2.9dev on $PROG_NAME needs files in \"\$TESTSSL_INSTALL_DIR/etc/\" to function correctly." @@ -10045,9 +10045,9 @@ check4openssl_oldfarts() { pr_warningln " Your \"$OPENSSL\" is way too old (", where is: @@ -10092,7 +10092,7 @@ help() { "$PROG_NAME URI", where is: - -t, --starttls does a default run against a STARTTLS enabled does a default run against a STARTTLS enabled (latter three require supplied openssl) --xmpphost for STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed --mx tests MX records from high to low priority (STARTTLS, port 25) @@ -10281,7 +10281,7 @@ EOF ossl_ciph="$(grep -w "$hexc" <<< "$ossl_supported_tls" | awk '{ print $3 }')" if [[ -n "$ossl_ciph" ]]; then TLS_CIPHER_OSSL_SUPPORTED[TLS_NR_CIPHERS]=true - [[ "$ossl_ciph" != "${TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]}" ]] && TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]="$ossl_ciph" + [[ "$ossl_ciph" != "${TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]}" ]] && TLS_CIPHER_OSSL_NAME[TLS_NR_CIPHERS]="$ossl_ciph" fi fi elif [[ $OSSL_VER_MAJOR -lt 1 ]]; then @@ -11708,4 +11708,3 @@ else fi exit $? - From 557c15607a7b82dac59b6330dff9f158660f8665 Mon Sep 17 00:00:00 2001 From: Todd Swatling Date: Thu, 5 Jan 2017 14:45:39 -0500 Subject: [PATCH 4/4] detects install dir when symlinked and realpath not present $ ls -l /usr/local/bin/testssl lrwxrwxrwx /usr/local/bin/testssl -> /home/user/testssl.sh/testssl.sh --- testssl.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/testssl.sh b/testssl.sh index fc010c3..dee99e4 100755 --- a/testssl.sh +++ b/testssl.sh @@ -9896,6 +9896,16 @@ get_install_dir() { [[ -r "$TESTSSL_INSTALL_DIR/cipher-mapping.txt" ]] && CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/cipher-mapping.txt" fi + # still no cipher mapping file (and realpath is not present): + if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] && which readlink &>/dev/null ; then + readlink -f ls &>/dev/null && \ + TESTSSL_INSTALL_DIR=$(dirname $(readlink -f ${BASH_SOURCE[0]})) || \ + TESTSSL_INSTALL_DIR=$(dirname $(readlink ${BASH_SOURCE[0]})) + # not sure whether Darwin has -f + CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/etc/cipher-mapping.txt" + [[ -r "$TESTSSL_INSTALL_DIR/cipher-mapping.txt" ]] && CIPHERS_BY_STRENGTH_FILE="$TESTSSL_INSTALL_DIR/cipher-mapping.txt" + fi + if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]] ; then unset ADD_RFC_STR debugme echo "$CIPHERS_BY_STRENGTH_FILE"