diff --git a/testssl.sh b/testssl.sh index 541597c..f7ede86 100755 --- a/testssl.sh +++ b/testssl.sh @@ -148,6 +148,9 @@ NODE="" NODEIP="" IPADDRs="" IP46ADDRs="" +PROXY="" +PROXYIP="" +PROXYPORT="" VULN_COUNT=0 readonly VULN_THRESHLD=1 # if bigger than this no we show a separate header in blue IPS="" @@ -435,7 +438,7 @@ wait_kill(){ # arg1 could be the protocol determined as "working". IIS6 needs that runs_HTTP() { # SNI is nonsense for !HTTPS but fortunately other protocols don't seem to care - printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet -connect $NODEIP:$PORT $SNI &>$TMPFILE & + printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE & wait_kill $! $HEADER_MAXSLEEP head $TMPFILE | grep -aq ^HTTP && SERVICE=HTTP head $TMPFILE | grep -aq SMTP && SERVICE=SMTP @@ -492,7 +495,7 @@ http_header() { useragent="$UA_STD" fi ( - $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $SNI << EOF + $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $PROXY $SNI << EOF GET $url HTTP/1.1 Host: $NODE Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 @@ -518,7 +521,7 @@ EOF ret=0 else #TODO: attention: if this runs into a timeout, we're dead. Try again differently: - printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -quiet -ign_eof -connect $NODEIP:$PORT $SNI &>$HEADERFILE + printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI &>$HEADERFILE if [ $? -ne 0 ]; then pr_litemagentaln " failed (HTTP header request stalled)" return 3 @@ -563,7 +566,7 @@ http_date() { if [[ $SERVICE != "HTTP" ]] ; then out "not tested as we're not targeting HTTP" else - printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -ign_eof -connect $NODEIP:$PORT $SNI &>$TMPFILE + printf "$GET_REQ11" | $OPENSSL s_client $OPTIMAL_PROTO -ign_eof -connect $NODEIP:$PORT $PROXY $SNI &>$TMPFILE now=$(date "+%s") # we need an ACCURATE date here and cannot rely on the headerfile! HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $TMPFILE) if [ -n "$HTTP_TIME" ] ; then @@ -982,7 +985,7 @@ std_cipherlists() { pr_bold "$2 " # indent in order to be in the same row as server preferences if listciphers $1; then # is that locally available?? [ $SHOW_LOC_CIPH -eq 0 ] && out "local ciphers are: " && sed 's/:/, /g' $TMPFILE - $OPENSSL s_client -cipher "$1" $STARTTLS -connect $NODEIP:$PORT $SNI 2>$TMPFILE >/dev/null $TMPFILE >/dev/null $TMPFILE $TMPFILE we can't do anything about it! - $OPENSSL s_client -cipher $ciph $STARTTLS -connect $NODEIP:$PORT $SNI &>$TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE need to read the error + [ "$VERBERR" -eq 0 ] && egrep "error|failure" $TMPFILE | egrep -av "unable to get local|verify error" + if ! locally_supported "$1" "$2" ; then return 7 else # we remove SNI for SSLv2 and v3: @@ -1424,7 +1432,7 @@ server_preference() { pr_blue "--> Testing server preferences"; outln "\n" pr_bold " Has server cipher order? " - $OPENSSL s_client $STARTTLS -cipher $list1 -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -cipher $list1 -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE if [ $? -ne 0 ]; then pr_magenta "no matching cipher in this list found (pls report this): " outln "$list1 . " @@ -1432,7 +1440,7 @@ server_preference() { else cipher1=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g') list2=$(echo $list1 | tr ':' '\n' | sort -r | tr '\n' ':') # pr_reverse the list - $OPENSSL s_client $STARTTLS -cipher $list2 -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -cipher $list2 -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE cipher2=$(grep -wa Cipher $TMPFILE | egrep -avw "New|is" | sed -e 's/^ \+Cipher \+://' -e 's/ //g') if [[ "$cipher1" != "$cipher2" ]]; then @@ -1445,7 +1453,7 @@ server_preference() { [[ $DEBUG -ge 2 ]] && out " $cipher1 | $cipher2" outln - $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE pr_bold " Negotiated protocol " default_proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') case "$default_proto" in @@ -1478,7 +1486,7 @@ server_preference() { i=1 for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do locally_supported -"$p" || continue - $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE if [ $? -eq 0 ]; then proto[i]=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') cipher[i]=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') @@ -1492,7 +1500,7 @@ server_preference() { done if spdy_pre ; then # is NPN/SPDY supported and is this no STARTTLS? - $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg "$NPN_PROTOs" /dev/null >$TMPFILE + $OPENSSL s_client -host $NODE -port $PORT $PROXY -nextprotoneg "$NPN_PROTOs" /dev/null >$TMPFILE if [ $? -eq 0 ]; then proto[i]=$(grep -aw "Next protocol" $TMPFILE | sed -e 's/^Next protocol://' -e 's/(.)//' -e 's/ //g') if [ -z "${proto[i]}" ]; then @@ -1539,7 +1547,7 @@ cipher_pref_check() { pr_bold " Cipher order" for p in ssl2 ssl3 tls1 tls1_1 tls1_2; do - $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -"$p" -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE if [ $? -eq 0 ]; then tested_cipher="" proto=$(grep -aw "Protocol" $TMPFILE | sed -e 's/^.*Protocol.*://' -e 's/ //g') @@ -1549,7 +1557,7 @@ cipher_pref_check() { printf " %-10s %s " "$proto:" "$cipher" tested_cipher="-"$cipher while true; do - $OPENSSL s_client $STARTTLS -"$p" -cipher "ALL:$tested_cipher" -connect $NODEIP:$PORT $SNI /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -"$p" -cipher "ALL:$tested_cipher" -connect $NODEIP:$PORT $PROXY $SNI /dev/null >$TMPFILE [ $? -ne 0 ] && break cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') out "$cipher " @@ -1562,14 +1570,14 @@ cipher_pref_check() { if ! spdy_pre ; then # is NPN/SPDY supported and is this no STARTTLS? outln else - protos=$($OPENSSL s_client -host $NODE -port $PORT -nextprotoneg \"\" /dev/null | grep -a "^Protocols " | sed -e 's/^Protocols.*server: //' -e 's/,//g') + protos=$($OPENSSL s_client -host $NODE -port $PORT -nextprotoneg \"\" $PROXY /dev/null | grep -a "^Protocols " | sed -e 's/^Protocols.*server: //' -e 's/,//g') for p in $protos; do - $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg "$p" /dev/null >$TMPFILE + $OPENSSL s_client -host $NODE -port $PORT -nextprotoneg "$p" $PROXY /dev/null >$TMPFILE cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') printf " %-10s %s " "$p:" "$cipher" tested_cipher="-"$cipher while true; do - $OPENSSL s_client -cipher "ALL:$tested_cipher" -host $NODE -port $PORT -nextprotoneg "$p" /dev/null >$TMPFILE + $OPENSSL s_client -cipher "ALL:$tested_cipher" -host $NODE -port $PORT -nextprotoneg "$p" $PROXY /dev/null >$TMPFILE [ $? -ne 0 ] && break cipher=$(grep -aw "Cipher" $TMPFILE | egrep -avw "New|is" | sed -e 's/^.*Cipher.*://' -e 's/ //g') out "$cipher " @@ -1586,7 +1594,7 @@ cipher_pref_check() { get_host_cert() { # arg1 is proto or empty - $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $SNI $1 2>/dev/null /dev/null $HOSTCERT return $? } @@ -1633,7 +1641,7 @@ server_defaults() { #TLS extensions follow now # throwing 1st every cipher/protocol at the server to know what works for proto in tls1_2 tls1_1 tls1 ssl3; do - $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $SNI -$proto -tlsextdebug -status /dev/null >$TMPFILE + $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug -status /dev/null >$TMPFILE ret=$? get_host_cert "-$proto" [ $? -eq 0 ] && [ $ret -eq 0 ] && break @@ -1641,7 +1649,7 @@ server_defaults() { done # this loop is needed for IIS/6 if [ $ret -eq 7 ]; then # "-status" kills GOST only servers, so we do another test without it and see whether that works then: - if ! $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $SNI -$proto -tlsextdebug /dev/null >$TMPFILE; then + if ! $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI -$proto -tlsextdebug /dev/null >$TMPFILE; then pr_magentaln "$OPENSSL returned an error around line $LINENO". tmpfile_handle tlsextdebug+status.txt return 7 # this is ugly, I know @@ -1649,7 +1657,7 @@ server_defaults() { gost_status_problem=true fi fi - $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT -$proto 2>/dev/null $HOSTCERT.nosni + $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT $PROXY -$proto 2>/dev/null $HOSTCERT.nosni pr_bold " TLS server extensions " extensions=$(grep -aw "^TLS server extension" $TMPFILE | sed -e 's/^TLS server extension \"//' -e 's/\".*$/,/g') if [ -z "$extensions" ]; then @@ -1784,7 +1792,7 @@ server_defaults() { outln " ($startdate --> $enddate)" savedir=$(pwd); cd $TEMPDIR - $OPENSSL s_client -showcerts $STARTTLS -connect $NODEIP:$PORT $SNI 2>/dev/null /dev/null ("level" c ".crt")} /---END CERTIFICATE-----/{inc=0}' nrsaved=$(ls $TEMPDIR/level?.crt 2>/dev/null | wc -w | sed 's/^ *//') pr_bold " # of certificates provided"; outln " $nrsaved" @@ -1857,7 +1865,7 @@ pfs() { return 1 fi - $OPENSSL s_client -cipher 'ECDH:DH' $STARTTLS -connect $NODEIP:$PORT $SNI &>$TMPFILE $TMPFILE $tmpfile $tmpfile /dev/null >$TMPFILE + $OPENSSL s_client -host $NODE -port $PORT $PROXY -nextprotoneg $NPN_PROTOs /dev/null >$TMPFILE tmpstr=$(grep -a '^Protocols' $TMPFILE | sed 's/Protocols.*: //') if [ -z "$tmpstr" -o "$tmpstr" == " " ] ; then out "not offered" @@ -1963,13 +1971,35 @@ spdy() { # arg for a fd doesn't work here fd_socket() { + if [ "$PROXY" ] ; then + if ! exec 5<> /dev/tcp/${PROXYIP}/${PROXYPORT}; then + outln + pr_magenta "$(basename "$0"): unable to open a socket to proxy $PROXYIP:$PROXYPORT" + return 6 + fi + echo "CONNECT $NODEIP:$PORT" >&5 + while true ; do + read x <&5 + if [ "${x%/*}" = "HTTP" ] ; then + x=${x#* } + if [ "${x%% *}" != "200" ] ; then + pr_magenta "Unable to CONNECT via proxy" + return 6 + fi + fi + if [ "$x" = $'\r' ] ; then + break + fi + done + else if ! exec 5<>/dev/tcp/$NODEIP/$PORT; then # 2>/dev/null removes an error message, but disables debugging outln pr_magenta "Unable to open a socket to $NODEIP:$PORT. " # It can last ~2 minutes but for for those rare occasions we don't do a timeout handler here, KISS return 6 fi - return 0 + fi + return 0 } @@ -2425,7 +2455,7 @@ heartbleed(){ fi # determine TLS versions available: - $OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT -tlsextdebug &>$TMPFILE $TMPFILE $TMPFILE $TMPFILE &1 &1 $TMPFILE & # msg enables us to look deeper into it while debugging + echo R | $OPENSSL s_client $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $PROXY &>$TMPFILE & # msg enables us to look deeper into it while debugging wait_kill $! $HEADER_MAXSLEEP if [ $? -eq 3 ]; then pr_litegreen "likely not vulnerable (OK)"; outln " (timed out)" # it hung 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 -msg -connect $NODEIP:$PORT &>$TMPFILE + echo R | $OPENSSL s_client $legacycmd $STARTTLS -msg -connect $NODEIP:$PORT $PROXY &>$TMPFILE sec_client_renego=$? # 0=client is renegotiating & doesn't return an error --> vuln! case $sec_client_renego in 0) pr_litered "VULNERABLE (NOT ok)"; outln ", DoS threat" ;; @@ -2722,7 +2752,7 @@ crime() { fi #STR=$($OPENSSL s_client $ADDCMD $STARTTLS -connect $NODEIP:$PORT $SNI 2>&1 $TMPFILE + $OPENSSL s_client $ADDCMD $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI $TMPFILE if grep -a Compression $TMPFILE | grep -aq NONE >/dev/null; then pr_litegreen "not vulnerable (OK)" [[ $SERVICE == "HTTP" ]] || out " (not using HTTP anyway)" @@ -2805,7 +2835,7 @@ breach() { useragent="$UA_STD" fi ( - $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $SNI << EOF + $OPENSSL s_client $OPTIMAL_PROTO -quiet -connect $NODEIP:$PORT $PROXY $SNI << EOF GET $url HTTP/1.1 Host: $NODE User-Agent: $useragent @@ -2856,7 +2886,7 @@ ssl_poodle() { cbc_ciphers=$($OPENSSL ciphers -v 'ALL:eNULL' | awk '/CBC/ { print $1 }' | tr '\n' ':') #FIXME: even with worst openssl client (FreeBSD9) we have 17 reasonable ciphers but is that enough to check?? debugme echo $cbc_ciphers - $OPENSSL s_client -ssl3 $STARTTLS -cipher $cbc_ciphers -connect $NODEIP:$PORT $SNI &>$TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE $TMPFILE 2>/dev/null $TMPFILE 2>/dev/null $TMPFILE 2>/dev/null $TMPFILE 2>/dev/null /dev/null >$TMPFILE /dev/null >$TMPFILE $TMPFILE # -V doesn't work with openssl < 1.0, feeding this into the while loop below - $OPENSSL s_client -cipher $rc4_ciphers_list $STARTTLS -connect $NODEIP:$PORT $SNI &>/dev/null /dev/null /dev/null + $OPENSSL s_client -cipher $rc4_cipher $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI /dev/null ret=$? # here we have a fp with openssl < 1.0 if [[ $ret -ne 0 ]] && [[ "$SHOW_EACH_C" -eq 0 ]] ; then continue # no successful connect AND not verbose displaying each cipher @@ -3287,6 +3317,17 @@ openssl_age() { fi } +# We need to get the IP address of the proxy so we can use it in fs_socket +check_proxy() +{ + if [ "$PROXY" ] ; then + PROXYNODE=${PROXY%:*} + PROXYPORT=${PROXY#*:} + PROXYIP=$(host -t a $PROXYNODE 2>/dev/null | grep -v alias | sed 's/^.*address //') + PROXY="-proxy $PROXYIP:$PROXYPORT" + fi +} + help() { cat << EOF @@ -3342,6 +3383,7 @@ tuning options: --assuming-http if protocol check fails it assumes HTTP protocol and enforces HTTP checks --ssl-native fallback to checks with OpenSSL where sockets are normally used --openssl use this openssl binary (default: look in \$PATH, \$RUN_DIR of $PROG_NAME + --proxy : connect via the specified HTTP proxy --sneaky be less verbose wrt referer headers --wide wide output for tests like RC4, BEAST. PFS also with hexcode, kx, strength, RFC name --show-each for wide outputs: display all ciphers tested -- not only succeeded ones @@ -3518,7 +3560,8 @@ EOF ignore_no_or_lame() { - [[ "$WARNINGS" = "off" -o "$WARNINGS" == "false" ]] && return 0 + [[ "$WARNINGS" == "off" ]] && return 0 + [[ "$WARNINGS" == "false" ]] && return 0 [[ "$WARNINGS" == "batch" ]] && return 1 pr_magenta "$1 " read a @@ -3648,7 +3691,7 @@ determine_service() { # determine protocol which works (needed for IIS6). If we don't have IIS6, 1st try will succeed --> better because we use the variable # all over the place. Stupid thing that we need to do that stuff for IIS<=6 for OPTIMAL_PROTO in "" "-tls1_2" "-tls1" "-ssl3" "-tls1_1" "-ssl2" ""; do - $OPENSSL s_client $OPTIMAL_PROTO -connect "$NODEIP:$PORT" $SNI /dev/null && all_failed=1 && break + $OPENSSL s_client $OPTIMAL_PROTO -connect "$NODEIP:$PORT" $PROXY $SNI /dev/null && all_failed=1 && break all_failed=0 done debugme echo "OPTIMAL_PROTO: $OPTIMAL_PROTO" @@ -3672,7 +3715,7 @@ determine_service() { ftp|smtp|pop3|imap|xmpp|telnet|ldap) STARTTLS="-starttls $protocol"; export STARTTLS SNI="" - $OPENSSL s_client -connect $NODEIP:$PORT $STARTTLS 2>/dev/null >$TMPFILE /dev/null >$TMPFILE