Refactored major parts of code

Note that due to the refactoring of some status messages, the output
will be slightly different when using default scanning (more verbose) than previous versions

Moved specific status messages to http_header()
Moved specific status messages to breach()
Moved specific status messages to ccs_injection()
Moved specific status messages to heartbleed()
Moved specific status messages to renego()
Moved specific status messages to crime()
Moved specific status messages to tls_poodle()
Moved specific status messages to freak()
Moved specific status messages to beast()

Added some more documentation for functions

Fixed typos in help

Created new function main:
This is the main function of testssl.sh
Refactored major part of the original main function

Created new function startup:
Parses the startup options

Created new function intialize_globals:
Initializes all used global variables

Created new function scanning_defaults:
Sets default scanning options when only one parameter (URI) is given

TODO: Refactor more/duplicate parts of functions

Note: For the new functions, fixed spaces (4) are used instead of tabs
This commit is contained in:
Peter Mosmans 2015-03-22 12:33:43 +10:00
parent 9b03aca97b
commit 1ddb9e5358

View File

@ -388,6 +388,7 @@ runs_HTTP() {
#problems not handled: chunked
http_header() {
outln; pr_blue "--> Testing HTTP Header response"; outln "\n"
[ -z "$1" ] && url="/" || url="$1"
if [ $SNEAKY -eq 0 ] ; then
referer="Referer: http://google.com/"
@ -1385,6 +1386,7 @@ rc4() {
# is agnostic to the version of TLS/SSL, more: http://www.breachattack.com/
# foreign referers are the important thing here!
breach() {
outln; pr_blue "--> Testing for BREACH (HTTP compression) vulnerability"; outln "\n"
pr_bold " BREACH"; out " (CVE-2013-3587) =HTTP Compression "
url="$1"
[ -z "$url" ] && url="/"
@ -1879,6 +1881,7 @@ ok_ids(){
ccs_injection(){
# see https://www.openssl.org/news/secadv_20140605.txt
# mainly adapted from Ramon de C Valle's C code from https://gist.github.com/rcvalle/71f4b027d61a78c42607
outln; pr_blue "--> Testing for CCS injection vulnerability"; outln "\n"
pr_bold " CCS "; out " (CVE-2014-0224), experimental "
$OPENSSL s_client $STARTTLS -connect $NODEIP:$PORT &>$TMPFILE </dev/null
@ -1977,6 +1980,7 @@ ccs_injection(){
}
heartbleed(){
outln; pr_blue "--> Testing for heartbleed vulnerability"; outln "\n"
pr_bold " Heartbleed\c"; out " (CVE-2014-0160) "
# mainly adapted from https://gist.github.com/takeshixx/10107280
@ -2096,6 +2100,8 @@ renego() {
0.9.9*|1.0*)
# all ok ;;
esac
outln; pr_blue "--> Testing for Renegotiation vulnerability"; outln "\n"
pr_bold " Secure Client-Initiated Renegotiation " # RFC 5746, community.qualys.com/blogs/securitylabs/2011/10/31/tls-renegotiation-and-denial-of-service-attacks
echo R | $OPENSSL s_client $ADDCMD $STARTTLS -connect $NODEIP:$PORT $SNI &>$TMPFILE
reneg_ok=$? # 0=client is renegotiating and does not get an error: vuln to DoS via client initiated renegotiation
@ -2137,7 +2143,7 @@ crime() {
0.9.9*|1.0*)
;;
esac
outln; pr_blue "--> Testing for CRIME vulnerability"; outln "\n"
pr_bold " CRIME, TLS " ; out "(CVE-2012-4929) "
# first we need to test whether OpenSSL binary has zlib support
@ -2216,7 +2222,7 @@ tls_poodle() {
ssl_poodle() {
local ret
local cbc_ciphers
outln; pr_blue "--> Testing for POODLE (Padding Oracle On Downgraded Legacy Encryption) vulnerability, SSLv3"; outln "\n"
pr_bold " POODLE, SSL"; out " (CVE-2014-3566), experimental "
cbc_ciphers=$($OPENSSL ciphers -v 'ALL:eNULL' | awk '/CBC/ { print $1 }' | tr '\n' ':')
debugme echo $cbc_ciphers
@ -2240,7 +2246,7 @@ freak() {
local ret
local exportrsa_ciphers
local addtl_warning=""
outln; pr_blue "--> Testing for FREAK attack"; outln "\n"
pr_bold " FREAK "; out " (CVE-2015-0204), experimental "
no_exportrsa_ciphers=$($OPENSSL ciphers -v 'ALL:eNULL' | egrep "^EXP.*RSA" | wc -l)
exportrsa_ciphers=$($OPENSSL ciphers -v 'ALL:eNULL' | awk '/^EXP.*RSA/ {print $1}' | tr '\n' ':')
@ -2283,7 +2289,8 @@ beast(){
local -i ret=0
local spaces=" "
local cr=$'\n'
outln; pr_blue "--> Testing for BEAST vulnerability"; outln "\n"
pr_bold " BEAST"; out " (CVE-2011-3389) "
# 2) test handfull of common CBC ciphers
@ -2403,6 +2410,7 @@ find_openssl_binary() {
}
# Parameters: 1 protocol
starttls() {
protocol=$(echo "$1" | sed 's/s$//') # strip trailing s in ftp(s), smtp(s), pop3(s), imap(s), ldap(s), telnet(s)
case "$1" in
@ -2461,12 +2469,12 @@ $PRG <options>
<-b|--banner> displays banner + version
<-v|--version> same as above
<-V|--local> pretty print all local ciphers
<-V|--local> pattern what local cipher with <pattern> is a/v?
<-V|--local> <pattern> what local cipher with <pattern> is a/v?
$PRG <options> URI
<-e|--each-cipher> check each local ciphers remotely
<-E|-ee|--cipher-per-proto> check those per protocol
<-E|--cipher-per-proto> check those per protocol
<-f|--ciphers> check cipher suites
<-p|--protocols> check TLS/SSL protocols only
<-S|--server_defaults> displays the servers default picks and certificate info
@ -2654,7 +2662,8 @@ ignore_no_or_lame() {
return 1
}
# Parameters: 1 URI
# [2] protocol
parse_hn_port() {
PORT=443 # unless otherwise auto-determined, see below
NODE="$1"
@ -2821,221 +2830,239 @@ mx_allentries() {
}
# This function intializes all used global variables
# It's primarily meant to keep track of them (quite a lot, you see...) and
# strictly spoken unneccesary
initialize_globals() {
# variables
low_byte=""
hex_cipher=""
protocol=""
uri=""
# scan options (BOOLEANS)
do_allciphers=false
do_beast=false
do_breach=false
do_ccs_injection=false
do_cipher_per_proto=false
do_crime=false
do_freak=false
do_header=false
do_heartbleed=false
do_mx_allentries=false
do_pfs=false
do_prettyprint_local=false
do_protocols=false
do_rc4=false
do_renego=false
do_run_std_cipherlists=false
do_server_defaults=false
do_server_preference=false
do_spdy=false
do_ssl_poodle=false
do_starttls=false
do_test_just_one=false
do_tls_sockets=false
run_server_preference=false
}
# Set default scanning options
set_scanning_defaults() {
do_beast=true
do_breach=true
do_ccs_injection=true
do_crime=true
do_freak=true
do_header=true
do_heartbleed=true
do_pfs=true
do_protocols=true
do_rc4=true
do_renego=true
do_run_std_cipherlists=true
do_server_defaults=true
do_spdy=true
do_ssl_poodle=true
do_starttls=true
run_server_preference=true
}
# Parses options
startup() {
# Verify options
if ! options=$(getopt -o :4,A,B,b,C,E,e,F,f,H,h,I,O,P,p,q::,R,S,s,T,t:,V:,v,x:,z -l appelbaum,banner,beast,breach,cipher-per-proto,ccs,ccs_injection,ciphers,compression,crime,each-cipher,freak,fs,header,headers,heartbleed,help,local:,mx,nsa,poodle,protocols,pfs,rc4,renegotiation,server_defaults,server_preference,single-ciphers-test:,ssl_poodle,starttls:,version -- "$@"); then
help
exit 1
fi
# Parse all options
eval set --$options
# Set defaults if only a URI was specified
[[ "$#" -eq 2 ]] && set_scanning_defaults
while [[ $# -gt 0 ]]; do
case $1 in
-b|--banner|-v|--version)
exit 0;;
--mx)
do_mx_allentries=true;;
-V|--local)
do_prettyprint_local=true;;
-x|--single-ciphers-test)
do_test_just_one=true
single_cipher=$2
shift;;
-t|--starttls)
protocol=$2
do_starttls=true
shift;;
-e|--each-cipher)
do_allciphers=true;;
-E|--cipher-per-proto)
do_cipher_per_proto=true;;
-h|--help)
help
exit 0;;
-p|--protocols)
do_protocols=true
do_spdy=true;;
-f|--ciphers)
do_run_std_cipherlists=true;;
-S|--server_defaults)
do_server_defaults=true;;
-P|--server_preference)
do_server_preference=true;;
-y|--spdy|--google)
do_spdy=true;;
-B|--heartbleed)
do_heartbleed=true;;
-I|--ccs|--ccs_injection)
do_ccs_injection=true;;
-R|--renegotiation)
do_renego=true;;
-C|--compression|--crime)
do_crime=true;;
-T|--breach)
do_breach=true;;
-O|--ssl_poodle|poodle)
do_ssl_poodle=true;;
-F|--freak)
do_freak=true;;
-4|--rc4|--appelbaum)
do_rc4=true;;
-s|--pfs|--fs|--nsa)
do_pfs=true;;
-A|--beast)
do_beast=true;;
-H|--header|--headers)
do_header=true;;
-q) ### following is a development feature and will disappear:
# DEBUG=3 ./testssl.sh -q 03 "cc, 13, c0, 13" google.de
# DEBUG=3 ./testssl.sh -q 01 yandex.ru
low_byte=$2
hex_cipher=$3
do_tls_sockets=true
shift 2;;
(--) shift
break;;
(-*) echo "$0: unrecognized option $1" 1>&2; exit 1;;
(*) break;;
esac
shift
done
# Show usage if no options were specified
if [ -z $1 ]; then
help
exit 0
fi
uri=$1
}
################# main: #################
main() {
# auto determine where bins are
find_openssl_binary
mybanner
#PATH_TO_TESTSSL="$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"
PATH_TO_TESTSSL=$(readlink "$BASH_SOURCE") 2>/dev/null
[ -z "$PATH_TO_TESTSSL" ] && PATH_TO_TESTSSL="."
# next file provides a pair "keycode/ RFC style name", see the RFCs, cipher(1) and
# https://www.carbonwind.net/TLS_Cipher_Suites_Project/tls_ssl_cipher_suites_simple_table_all.htm
[ -r "$(dirname $PATH_TO_TESTSSL)/mapping-rfc.txt" ] && MAP_RFC_FNAME=$(dirname $PATH_TO_TESTSSL)"/mapping-rfc.txt"
initialize_globals
# TODO: ret variable could be changed to a global variable
# this would make the code probably a bit cleaner
ret=0
startup "$@"
maketempf
parse_hn_port "${uri}" "${protocol}"
# OK, let's roll..
${do_mx_allentries} && { mx_allentries "${uri}"; ret=$(($? + ret)); }
if ${do_prettyprint_local}; then
initialize_engine # GOST support-
prettyprint_local "${uri}"
fi
${do_test_just_one} && test_just_one ${single_cipher}
${do_starttls} && { starttls ${protocol}; ret=$(($? + ret)); }
${do_allciphers} && { allciphers; ret=$(($? + ret)); }
${do_cipher_per_proto} && { cipher_per_proto; ret=$(($? + ret)); }
${do_protocols} && { runprotocols; ret=$(($? + ret)); }
${do_spdy} && { spdy; ret=$(($? + ret)); }
${do_run_std_cipherlists} && { run_std_cipherlists; ret=$(($? + ret)); }
${do_server_preference} && { server_preference; ret=$(($? + ret)); }
${do_server_defaults} && { server_defaults; ret=$(($? + ret)); }
${do_heartbleed} && { heartbleed; ret=$(($? + ret)); }
${do_ccs_injection} && { ccs_injection; ret=$(($? + ret)); }
${do_renego} && { renego; ret=$(($? + ret)); }
# if you can't do the time, don't do the crime ;)
${do_crime} && { crime; ret=$(($? + ret)); }
if ${do_breach}; then
#TODO: refactor this into breach()
if [[ $SERVICE != "HTTP" ]] ; then
pr_litemagentaln " Wrong usage: You're not targetting a HTTP service"
ret=$((2 + ret))
else
breach "$URL_PATH"
ret=$(($? + ret))
fi
fi
${do_ssl_poodle} && { poodle; ret=$(($? + ret)); }
${do_freak} && { freak; ret=$(($? + ret)); }
${do_rc4} && { rc4; ret=$(($? + ret)); }
${do_tls_sockets} && { tls_sockets ${low_byte} ${hex_cipher}; \
ret=$(($? + ret)); }
if ${do_header}; then
#TODO: refactor this into other functions
if [[ $SERVICE == "HTTP" ]]; then
hsts "$URL_PATH"
hpkp "$URL_PATH"
serverbanner "$URL_PATH"
applicationbanner "$URL_PATH"
cookieflags "$URL_PATH"
else
pr_litemagentaln " Wrong usage: You're not targetting a HTTP service"
ret=$((2 + ret))
fi
fi
${do_pfs} && { pfs; ret=$(($? + ret)); }
exit $ret
}
case "$1" in
-h|--help|-help|"")
help
exit $? ;;
esac
# auto determine where bins are
find_openssl_binary
mybanner
#PATH_TO_TESTSSL="$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"
PATH_TO_TESTSSL=$(readlink "$BASH_SOURCE") 2>/dev/null
[ -z "$PATH_TO_TESTSSL" ] && PATH_TO_TESTSSL="."
#
# next file provides a pair "keycode/ RFC style name", see the RFCs, cipher(1) and
# https://www.carbonwind.net/TLS_Cipher_Suites_Project/tls_ssl_cipher_suites_simple_table_all.htm
[ -r "$(dirname $PATH_TO_TESTSSL)/mapping-rfc.txt" ] && MAP_RFC_FNAME=$(dirname $PATH_TO_TESTSSL)"/mapping-rfc.txt"
#FIXME: I know this sucks and getoptS is better
case "$1" in
-b|--banner|-banner|-v|--version|-version)
exit 0
;;
--mx)
mx_allentries "$2"
exit $?
;;
-V|--local)
initialize_engine # GOST support
prettyprint_local "$2"
exit $? ;;
-x|--single-ciphers-test)
maketempf
parse_hn_port "$3"
test_just_one $2
exit $? ;;
-t|--starttls)
maketempf
parse_hn_port "$3" "$2" # here comes protocol to signal starttls and hostname:port
starttls "$2" # protocol
exit $? ;;
-e|--each-cipher)
maketempf
parse_hn_port "$2"
allciphers
exit $? ;;
-E|-ee|--cipher-per-proto)
maketempf
parse_hn_port "$2"
cipher_per_proto
exit $? ;;
-p|--protocols)
maketempf
parse_hn_port "$2"
runprotocols ; ret=$?
spdy ; ret=$(($? + ret))
exit $ret ;;
-f|--ciphers)
maketempf
parse_hn_port "$2"
run_std_cipherlists
exit $? ;;
-S|--server_defaults)
maketempf
parse_hn_port "$2"
server_defaults
exit $? ;;
-P|--server_preference)
maketempf
parse_hn_port "$2"
server_preference
exit $? ;;
-y|--spdy|--google)
maketempf
parse_hn_port "$2"
spdy
exit $? ;;
-B|--heartbleet)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for heartbleed vulnerability"; outln "\n"
heartbleed
exit $? ;;
-I|--ccs|--ccs_injection)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for CCS injection vulnerability"; outln "\n"
ccs_injection
exit $? ;;
-R|--renegotiation)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for Renegotiation vulnerability"; outln "\n"
renego
exit $? ;;
-C|--compression|--crime)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for CRIME vulnerability"; outln "\n"
crime
exit $? ;;
-T|--breach)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for BREACH (HTTP compression) vulnerability"; outln "\n"
if [[ $SERVICE != "HTTP" ]] ; then
pr_litemagentaln " Wrong usage: You're not targetting a HTTP service"
ret=2
else
breach "$URL_PATH"
ret=$?
fi
ret=$(($? + ret))
exit $ret ;;
-O|--ssl_poodle|poodle)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for POODLE (Padding Oracle On Downgraded Legacy Encryption) vulnerability, SSLv3"; outln "\n"
ssl_poodle
exit $? ;;
-F|--freak)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for FREAK attack"; outln "\n"
freak
exit $? ;;
-4|--rc4|--appelbaum)
maketempf
parse_hn_port "$2"
rc4
exit $? ;;
-s|--pfs|--fs|--nsa)
maketempf
parse_hn_port "$2"
pfs
exit $? ;;
-A|--beast)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing for BEAST vulnerability"; outln "\n"
beast
exit $? ;;
-H|--header|--headers)
maketempf
parse_hn_port "$2"
outln; pr_blue "--> Testing HTTP Header response"; outln "\n"
if [[ $SERVICE == "HTTP" ]]; then
hsts "$URL_PATH"
hpkp "$URL_PATH"
ret=$?
serverbanner "$URL_PATH"
ret=$(($? + ret))
applicationbanner "$URL_PATH"
ret=$(($? + ret))
cookieflags "$URL_PATH"
ret=$(($? + ret))
else
pr_litemagentaln " Wrong usage: You're not targetting a HTTP service"
ret=2
fi
exit $ret ;;
### following is a development feature and will disappear:
-q)
maketempf
parse_hn_port "$2"
tls_sockets "$3" "$4"
exit $?
# DEBUG=3 ./testssl.sh -q google.de 03 "cc, 13, c0, 13"
# DEBUG=3 ./testssl.sh -q yandex.ru 01
;;
-*) help ;; # wrong argument
*)
maketempf
parse_hn_port "$1"
outln
runprotocols ; ret=$?
spdy ; ret=$(($? + ret))
run_std_cipherlists ; ret=$(($? + ret))
server_preference ; ret=$(($? + ret))
server_defaults ; ret=$(($? + ret))
if [[ $SERVICE == "HTTP" ]]; then
outln; pr_blue "--> Testing HTTP Header response"
outln "\n"
hsts "$URL_PATH" ; ret=$(($? + ret))
hpkp "$URL_PATH" ; ret=$(($? + ret))
serverbanner "$URL_PATH" ; ret=$(($? + ret))
applicationbanner "$URL_PATH" ; ret=$(($? + ret))
cookieflags "$URL_PATH" ; ret=$(($? + ret))
fi
outln; pr_blue "--> Testing specific vulnerabilities"
outln "\n"
heartbleed ; ret=$(($? + ret))
ccs_injection ; ret=$(($? + ret))
renego ; ret=$(($? + ret))
crime ; ret=$(($? + ret))
[[ $SERVICE == "HTTP" ]] && breach "$URL_PATH" ; ret=$(($? + ret))
ssl_poodle ; ret=$(($? + ret))
freak ; ret=$(($? + ret))
beast ; ret=$(($? + ret))
rc4 ; ret=$(($? + ret))
pfs ; ret=$(($? + ret))
exit $ret ;;
esac
main "$@"
# $Id: testssl.sh,v 1.214 2015/03/17 21:12:24 dirkw Exp $
# vim:ts=5:sw=5