parallel mass testing mode, Ticketbleed+client auth, parallel mode also for nmap

Parallel mass testing mode is now not anymore experimental. To
use it a separate flag ``--mode=parallel`` was introduced. Serial
is still the default for now to avoid unexpected conditions.
Both the mode arguement and the default is subject to change.

The parallel mass testing mode can now also make use of a
nmap file. Also the functional test for nmap file was put
into a separate function and made more user safe. Open point is
that we better should use the hostname if the forward DNS record matches.

Fixed logical inconsistency: Ticketbleed was not being tested against a server with client authentication

Some variables in the beginning reordered
This commit is contained in:
Dirk 2017-06-12 18:23:55 +02:00
parent 1b0ac5ffd6
commit 241b6e4d2e
1 changed files with 63 additions and 33 deletions

View File

@ -161,6 +161,7 @@ if [[ -z $TERM_WIDTH ]]; then # no batch file and
fi
TERM_CURRPOS=0 # custom line wrapping needs alter the current horizontal cursor pos
## CONFIGURATION PART ##
# following variables make use of $ENV, e.g. OPENSSL=<myprivate_path_to_openssl> ./testssl.sh <host>
# 0 means (normally) true here. Some of the variables are also accessible with a command line switch, see --help
@ -183,6 +184,7 @@ DEBUG=${DEBUG:-0} # 1: normal putput the files in /tmp/ ar
# 6: whole 9 yards
FAST=${FAST:-false} # preference: show only first cipher, run_allciphers with openssl instead of sockets
WIDE=${WIDE:-false} # whether to display for some options just ciphers or a table w hexcode/KX,Enc,strength etc.
MASS_TESTING_MODE=${MASS_TESTING_MODE:-serial} # can be serial or parallel. Subject to change
LOGFILE="${LOGFILE:-""}" # logfile if used
JSONFILE="${JSONFILE:-""}" # jsonfile if used
CSVFILE="${CSVFILE:-""}" # csvfile if used
@ -228,7 +230,13 @@ if [[ -n "$MEASURE_TIME_FILE" ]] && [[ -z "$MEASURE_TIME" ]]; then
else
MEASURE_TIME=${MEASURE_TIME:-false}
fi
DISPLAY_CIPHERNAMES="openssl" # display OpenSSL ciphername (but both OpenSSL and RFC ciphernames in wide mode)
SHOW_CENSYS_LINK=${SHOW_CENSYS_LINK:-true}
readonly UA_STD="TLS tester from $SWURL"
readonly UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
## INITIALIZATION PART ##
# further global vars just declared here
readonly NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1"
# alpn_protos needs to be space-separated, not comma-seperated, including odd ones observerd @ facebook and others, old ones like h2-17 omitted as they could not be found
@ -267,7 +275,6 @@ HAS_FALLBACK_SCSV=false
HAS_PROXY=false
HAS_XMPP=false
HAS_POSTGRES=false
DISPLAY_CIPHERNAMES="openssl" # display OpenSSL ciphername (but both OpenSSL and RFC ciphernames in wide mode)
PORT=443 # unless otherwise auto-determined, see below
NODE=""
NODEIP=""
@ -285,7 +292,6 @@ SERVICE="" # is the server running an HTTP server,
URI=""
CERT_FINGERPRINT_SHA2=""
RSA_CERT_FINGERPRINT_SHA2=""
SHOW_CENSYS_LINK=${SHOW_CENSYS_LINK:-true}
STARTTLS_PROTOCOL=""
OPTIMAL_PROTO="" # we need this for IIS6 (sigh) and OpenSSL 1.0.2, otherwise some handshakes
# will fail, see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
@ -295,8 +301,6 @@ TLS_NOW=""
NOW_TIME=""
HTTP_TIME=""
GET_REQ11=""
readonly UA_STD="TLS tester from $SWURL"
readonly UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
START_TIME=0 # time in epoch when the action started
END_TIME=0 # .. ended
SCAN_TIME=0 # diff of both: total scan time
@ -1279,7 +1283,7 @@ service_detection() {
local -i was_killed
local addcmd=""
if ! $CLIENT_AUTH; then
if ! "$CLIENT_AUTH"; then
# SNI is not standardardized for !HTTPS but fortunately for other protocols s_client doesn't seem to care
[[ ! "$1" =~ ssl ]] && addcmd="$SNI"
printf "$GET_REQ11" | $OPENSSL s_client $1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $addcmd >$TMPFILE 2>$ERRFILE &
@ -1305,7 +1309,7 @@ service_detection() {
fileout "service" "INFO" "Service detected: $SERVICE, thus skipping HTTP specific checks"
ret=0
;;
*) if $CLIENT_AUTH; then
*) if "$CLIENT_AUTH"; then
out "certificate based authentication => skipping all HTTP checks"
echo "certificate based authentication => skipping all HTTP checks" >$TMPFILE
fileout "client_auth" "INFO" "certificate based authentication => skipping all HTTP checks"
@ -9178,7 +9182,7 @@ run_ticketbleed() {
[[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for Ticketbleed vulnerability " && outln
pr_bold " Ticketbleed"; out " ($cve), experiment. "
if [[ "$SERVICE" != HTTP ]]; then
if [[ "$SERVICE" != HTTP ]] && ! "$CLIENT_AUTH"; then
outln "-- (applicable only for HTTPS)"
fileout "ticketbleed" "INFO" "Ticketbleed: not applicable, not HTTP" "$cve" "$cwe"
return 0
@ -9562,7 +9566,7 @@ run_crime() {
ret=7
elif grep -a Compression $TMPFILE | grep -aq NONE >/dev/null; then
pr_done_good "not vulnerable (OK)"
if [[ $SERVICE != "HTTP" ]] && ! $CLIENT_AUTH; then
if [[ $SERVICE != "HTTP" ]] && ! "$CLIENT_AUTH"; then
out " (not using HTTP anyway)"
fileout "crime" "OK" "CRIME, TLS: Not vulnerable (not using HTTP anyway)" "$cve" "$cwe"
else
@ -9635,10 +9639,14 @@ run_breach() {
local cwe="CWE-310"
local hint=""
[[ $SERVICE != "HTTP" ]] && return 7
[[ $SERVICE != "HTTP" ]] && ! "$CLIENT_AUTH" && return 7
[[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for BREACH (HTTP compression) vulnerability " && outln
pr_bold " BREACH"; out " ($cve) "
if "$CLIENT_AUTH"; then
outln "cannot be tested (server side requires authentication"
fileout "breach" "INFO" "BREACH: Test failed (HTTP request stalled)" "$cve" "$cwe"
fi
url="$1"
[[ -z "$url" ]] && url="/"
@ -11124,13 +11132,14 @@ help() {
"$PROG_NAME <options> URI", where <options> is:
-t, --starttls <protocol> does a default run against a STARTTLS enabled <protocol,
-t, --starttls <protocol> Does a default run against a STARTTLS enabled <protocol,
protocol is <ftp|smtp|pop3|imap|xmpp|telnet|ldap|postgres> (latter three require supplied openssl)
--xmpphost <to_domain> for STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed
--mx <domain/host> tests MX records from high to low priority (STARTTLS, port 25)
--file <fname|fname.gmap> mass testing option: Reads command lines from <fname>, one line per instance.
--xmpphost <to_domain> For STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed
--mx <domain/host> Tests MX records from high to low priority (STARTTLS, port 25)
--file <fname|fname.gmap> Mass testing option: Reads command lines from <fname>, one line per instance.
Comments via # allowed, EOF signals end of <fname>. Implicitly turns on "--warnings batch".
Alternatively: nmap output in greppable format (-oG) is also allowed (1x same port per line)
--mode <serial|parallel> Mass testing to be done serial (default) or parallel
single check as <options> ("$PROG_NAME URI" does everything except -E):
-e, --each-cipher checks each local cipher remotely
@ -12324,6 +12333,18 @@ nmap_to_plain_file() {
local target_fname=""
local oneline=""
# Ok, since we are here we have an nmap file. To avoid questions we make sure it's the right format too
if [[ "$(head -1 "$FNAME")" =~ ( -oG )(.*) ]]; then
# yes, grappable
if [[ $(grep -c Status "$FNAME") -ge 1 ]]; then
[[ $(grep -c '\/open\/' $FNAME) -eq 0 ]] && \
fatal "Nmap file $FNAME should contain at least one open port" -1
else
fatal "strange, nmap grepable misses \"Status\"" -1
fi
else
fatal "Nmap file $FNAME is not in grep(p)able format (-oG filename.gmap)" -1
fi
# test whether there's more than one "open" per line which is not supported currently
while read -r oneline; do
if [[ $(tr ',' '\n' <<< "$oneline" | grep -c '\/open\/') -gt 1 ]]; then
@ -12331,6 +12352,10 @@ nmap_to_plain_file() {
fi
done < "$FNAME"
target_fname=${FNAME%.*}.txt # strip extension
#FIXME: # nmap in mass testing mode supplies the hostname too in $3. That's normally unreliable
# as a DNS reverse lookup used and reality shows those records are not as good maintained.
# However a check whether the forward lookup matches the IP should be done and if so we should
# should rather use the fqdn And: better use read here
awk '/\/open\// { print $2":"$5 }' "$FNAME" | sed 's/\/open.*$//g' >"$target_fname"
[[ $? -ne 0 ]] && \
fatal "conversion from nmap grepable to text somehow failed around $LINENO" -3
@ -12343,29 +12368,18 @@ run_mass_testing() {
local cmdline=""
local first=true
local gmapadd=""
local saved_fname="$FNAME"
if [[ ! -r "$FNAME" ]] && "$IKNOW_FNAME"; then
fatal "Can't read file \"$FNAME\"" "2"
fi
# at least now we checked the command line. But it's not sure yet whether we have the right file
if [[ "$(head -1 "$FNAME")" =~ (Nmap [4-8])(.*)( scan initiated )(.*) ]]; then
# Ok, we have an nmap file. To avoid questions we make sure it's the right format too
if [[ "$(head -1 "$FNAME")" =~ ( -oG )(.*) ]]; then
if [[ $(grep -c Status "$FNAME") -ge 1 ]]; then
[[ $(grep -c '\/open\/' $FNAME) -eq 0 ]] && \
fatal "Nmap file $FNAME should contain at least one open port" -1
IS_GMAP_FILE=true
gmapadd="grep(p)able nmap "
nmap_to_plain_file
else
fatal "wierdly nmap grepable misses \"Status\"" -1
fi
else
fatal "Nmap file $FNAME is not in grep(p)able format (-oG filename.gmap)" -1
fi
fi
pr_reverse "====== Running in file batch mode with ${gmapadd}file=\"$FNAME\" ======"; outln "\n"
if [[ "$(head -1 "$FNAME")" =~ (Nmap [4-8])(.*)( scan initiated )(.*) ]]; then
gmapadd="grep(p)able nmap "
nmap_to_plain_file
fi
pr_reverse "====== Running in file batch mode with ${gmapadd}file=\"$saved_fname\" ======"; outln "\n"
while read cmdline; do
cmdline="$(filter_input "$cmdline")"
[[ -z "$cmdline" ]] && continue
@ -12424,12 +12438,19 @@ run_mass_testing_parallel() {
local -i i nr_active_tests=0
local -a -i start_time=()
local -i curr_time wait_time
local gmapadd=""
local saved_fname="$FNAME"
if [[ ! -r "$FNAME" ]] && $IKNOW_FNAME; then
fatal "Can't read file \"$FNAME\"" "2"
fi
pr_reverse "====== Running in parallel file batch mode with file=\"$FNAME\" ======"; outln "\n"
if [[ "$(head -1 "$FNAME")" =~ (Nmap [4-8])(.*)( scan initiated )(.*) ]]; then
gmapadd="grep(p)able nmap "
nmap_to_plain_file
fi
pr_reverse "====== Running in file batch mode with ${gmapadd}file=\"$saved_fname\" ======"; outln "\n"
while read cmdline; do
cmdline="$(filter_input "$cmdline")"
[[ -z "$cmdline" ]] && continue
@ -12915,6 +12936,15 @@ parse_cmd_line() {
WARNINGS=batch # set this implicitly!
do_mass_testing=true
;;
--mode|--mode=*)
MASS_TESTING_MODE="$(parse_opt_equal_sign "$1" "$2")"
[[ $? -eq 0 ]] && shift
case "$MASS_TESTING_MODE" in
serial|parallel) ;;
*) tmln_magenta "\nmass testing mode can be either \"serial\" or \"parallel\""
help 1
esac
;;
--warnings|--warnings=*)
WARNINGS=$(parse_opt_equal_sign "$1" "$2")
[[ $? -eq 0 ]] && shift
@ -13251,7 +13281,7 @@ lets_roll() {
if "$do_mass_testing"; then
prepare_logging
if "$EXPERIMENTAL"; then
if [[ "$MASS_TESTING_MODE" == "parallel" ]]; then
run_mass_testing_parallel
else
run_mass_testing