mirror of
https://github.com/drwetter/testssl.sh.git
synced 2025-01-03 23:39:45 +01:00
- testing all IP addresses of a node works now (refactoring of parse_hn_port into three functions) FIX #96
- SNI is unset if STARTTLS is set - some BSD fixes (sed)
This commit is contained in:
parent
ac92ffb3c2
commit
e16ccd06b6
227
testssl.sh
227
testssl.sh
@ -26,15 +26,15 @@ SWCONTACT="dirk aet testssl dot sh"
|
||||
# your OWN RISK!
|
||||
|
||||
# HISTORY: I know this shell script is still on its way to be nice and readable. ;-) It
|
||||
# all started with a few openssl commands around 2006. That's because openssl is a such a good
|
||||
# swiss army knife (see e.g. wiki.openssl.org/index.php/Command_Line_Utilities) that it was
|
||||
# difficult to resist wrapping some shell commands around it, which I used for my pen tests.
|
||||
# This is how everything started.
|
||||
# all started with a few openssl commands around 2006 for me as a pentester.
|
||||
# That's because openssl is a such a good swiss army knife (see e.g.
|
||||
# wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist
|
||||
# wrapping some shell commands around it, which I used for my pen tests. This is how everything started.
|
||||
# Now it has grown up, it has bash socket support for some features which basically replacing
|
||||
# more and more functions of OpenSSL and will serve as some kind of library in the future.
|
||||
# The socket checks in bash may sound cool and unique -- they are -- but probably you
|
||||
# can achieve e.g. the same result with my favorite interactive shell: zsh (zmodload zsh/net/socket
|
||||
# -- checkout zsh/net/tcp too!) /bin/bash is way more often used within Linux and it's perfect
|
||||
# -- checkout zsh/net/tcp) too! /bin/bash though is way more often used within Linux and it's perfect
|
||||
# for cross platform support, see MacOS X and also under Windows the MSYS2 extension.
|
||||
# Cross-platform is one of the three ideas of this script. Second: Ease of installation.
|
||||
# No compiling, install gems, go to CPAN, use pip etc. Third: Easy to use and to interpret
|
||||
@ -56,6 +56,8 @@ SWCONTACT="dirk aet testssl dot sh"
|
||||
# however it's still recommended to use the supplied binaries or cook your own, see
|
||||
# https://github.com/drwetter/testssl.sh/blob/master/openssl-bins/openssl-1.0.2-chacha.pm/Readme.md
|
||||
# Don't worry if feature X is not available you'll get a warning about this missing feature!
|
||||
# The idea is if this script can't tell something for sure it speaks up so that you have
|
||||
# a better picture.
|
||||
|
||||
# debugging help:
|
||||
readonly PS4='${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
|
||||
@ -369,7 +371,7 @@ tmpfile_handle() {
|
||||
if [[ "$DEBUG" -eq 0 ]] ; then
|
||||
rm $TMPFILE
|
||||
else
|
||||
mv $TMPFILE "$TEMPDIR/$1"
|
||||
mv $TMPFILE "$TEMPDIR/$NODEIP.$1"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -790,7 +792,7 @@ normalize_ciphercode() {
|
||||
fi
|
||||
HEXC="$part1$part2$part3"
|
||||
fi
|
||||
# FIXME: we shuld just echo this and avoid the global var HEXC
|
||||
#TODO: we should just echo this and avoid the global var HEXC
|
||||
HEXC=$(echo $HEXC | tr 'A-Z' 'a-z' | sed 's/0x/x/') #tolower + strip leading 0
|
||||
return 0
|
||||
}
|
||||
@ -3251,8 +3253,6 @@ cleanup () {
|
||||
[ -d "$TEMPDIR" ] && rm -rf ${TEMPDIR};
|
||||
fi
|
||||
outln
|
||||
[ -n "$NODE" ] && datebanner "Done" # only if running against server
|
||||
outln
|
||||
}
|
||||
|
||||
# for now only GOST engine
|
||||
@ -3339,14 +3339,82 @@ parse_hn_port() {
|
||||
URL_PATH=$(echo $URL_PATH | sed 's/\/\//\//g') # we rather want // -> /
|
||||
[ -z "$URL_PATH" ] && URL_PATH="/"
|
||||
|
||||
# now get NODEIP
|
||||
if ! get_dns_entries ; then
|
||||
pr_magenta "Can't proceed: No IP address for \"$NODE\" available"; outln "\n"
|
||||
return 0 # NODE, URL_PATH, PORT is set now
|
||||
}
|
||||
|
||||
newline_to_spaces() {
|
||||
echo "$1" | tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
|
||||
# now get all IP addresses
|
||||
determine_ip_addresses() {
|
||||
local ip4=""
|
||||
local ip6=""
|
||||
|
||||
if [ -z "$(printf $NODE | sed -e 's/[0-9]//g' -e 's/\.//g')" ]; then
|
||||
ip4=$NODE # only an IPv4 address was supplied as an argument, no hostname
|
||||
SNI="" # override Server Name Indication as we test the IP only
|
||||
else
|
||||
# for security testing sometimes we have local entries. Getent is BS under Linux for localhost: No network, no resolution
|
||||
ip4=$(grep -w "$NODE" /etc/hosts | egrep -v ':|^#' | egrep "[[:space:]]$NODE" | awk '{ print $1 }')
|
||||
if [[ -z "$ip4" ]]; then
|
||||
if which host &> /dev/null && [ -z "$ip4" ] ; then
|
||||
ip4=$(host -t a $NODE 2>/dev/null | grep -v alias | sed 's/^.*address //')
|
||||
if echo "$ip4" | grep -q NXDOMAIN || echo "$ip4" | grep -q "no A record"; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$ip4" ]] ; then
|
||||
# MSYS2 has no host or getent, so we need nslookup
|
||||
ip4=$(nslookup $NODE 2>/dev/null | grep -A10 Name | grep -v Name | sed 's/^Address.*: .//')
|
||||
[[ -z "$ip4" ]] && return 2
|
||||
fi
|
||||
fi
|
||||
|
||||
ip6=$(grep -w "$NODE" /etc/hosts | grep ':' | grep -v '^#' | egrep "[[:space:]]$NODE" | awk '{ print $1 }')
|
||||
if [[ -z "$ip6" ]]; then
|
||||
# for IPv6 we often get this :ffff:IPV4 address which isn't of any use
|
||||
#which getent 2>&1 >/dev/null && ip6=$(getent ahostsv6 $NODE | grep $NODE | awk '{ print $1}' | grep -v '::ffff' | uniq)
|
||||
if host -t aaaa $NODE &>/dev/null ; then
|
||||
ip6=$(host -t aaaa $NODE | grep -v alias | grep -v "no AAAA record" | sed 's/^.*address //')
|
||||
else
|
||||
ip6=""
|
||||
fi
|
||||
# MSYS2 has no host or getent, so we need nslookup
|
||||
if [[ -z "$ip6" ]]; then
|
||||
ip6=$(nslookup -type=aaaa $NODE 2>/dev/null | grep -A10 Name | grep -v Name | sed 's/^Address.*: .//')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
IPADDRs=$(newline_to_spaces "$ip4")
|
||||
if [[ -z "$IPADDRs" ]] && [[ -z "$CMDLINE_IP" ]] ; then
|
||||
pr_magenta "Can't proceed: No IP address for \"$NODE\" available"
|
||||
outln "\n"
|
||||
exit -1
|
||||
fi
|
||||
[[ ! -z "$ip6" ]] && IP46ADDRs="$ip4 $ip6" || IP46ADDRs="$IPADDRs"
|
||||
IP46ADDRs=$(newline_to_spaces "$IP46ADDR")
|
||||
|
||||
# check if we can connect to port
|
||||
if ! fd_socket; then
|
||||
return 0 # IPADDR and IP46ADDR is set now
|
||||
}
|
||||
|
||||
determine_rdns() {
|
||||
# we can't do this as some checks and even openssl is not IPv6 safe. BTW: bash sockets do IPv6 transparently!
|
||||
#NODEIP=$(echo "$ip6" | head -1)
|
||||
if which host &> /dev/null; then
|
||||
rDNS=$(host -t PTR $NODEIP 2>/dev/null | grep 'pointer' | sed -e 's/^.*pointer //' -e 's/\.$//')
|
||||
elif which nslookup &> /dev/null; then
|
||||
rDNS=$(nslookup -type=PTR $NODEIP 2> /dev/null | grep -v 'canonical name =' | grep 'name = ' | awk '{ print $NF }' | sed 's/\.$//')
|
||||
fi
|
||||
[ -z "$rDNS" ] && rDNS="--"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# arg1: ftp smtp, pop3, imap, xmpp, telnet, ldap (maybe with trailing s)
|
||||
determine_service() {
|
||||
if ! fd_socket; then # check if we can connect to $NODEIP:$PORT
|
||||
ignore_no_or_lame "Ignore? "
|
||||
[ $? -ne 0 ] && exit 3
|
||||
fi
|
||||
@ -3354,7 +3422,7 @@ parse_hn_port() {
|
||||
|
||||
datebanner "Testing"
|
||||
|
||||
if [[ -z "$2" ]] ; then # for starttls we want another check
|
||||
if [[ -z "$1" ]] ; then # for starttls we want another check
|
||||
# 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
|
||||
@ -3377,10 +3445,11 @@ parse_hn_port() {
|
||||
fi
|
||||
runs_HTTP $OPTIMAL_PROTO
|
||||
else
|
||||
protocol=$(echo "$2" | sed 's/s$//') # strip trailing s in ftp(s), smtp(s), pop3(s), imap(s), ldap(s), telnet(s)
|
||||
protocol=$(echo "$1" | sed 's/s$//') # strip trailing s in ftp(s), smtp(s), pop3(s), imap(s), ldap(s), telnet(s)
|
||||
case "$protocol" in
|
||||
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
|
||||
if [ $? -ne 0 ]; then
|
||||
pr_magentaln " $OPENSSL couldn't establish STARTTLS via $protocol to $NODEIP:$PORT"
|
||||
@ -3399,75 +3468,14 @@ parse_hn_port() {
|
||||
${do_mx_all_ips} || initialize_engine
|
||||
outln
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
get_dns_entries() {
|
||||
local ip4=""
|
||||
local ip6=""
|
||||
|
||||
if [ -z "$(printf $NODE | sed -e 's/[0-9]//g' -e 's/\.//g')" ]; then
|
||||
ip4=$NODE # only an IPv4 address was supplied as an argument, no hostname
|
||||
SNI="" # override Server Name Indication as we test the IP only
|
||||
else
|
||||
# for security testing sometimes we have local entries. Getent is BS under Linux for localhost: No network, no resolution
|
||||
ip4=$(grep -w "$NODE" /etc/hosts | egrep -v ':|^#' | egrep "[[:space:]]$NODE" | awk '{ print $1 }')
|
||||
if [ -z "$ip4" ]; then
|
||||
if which host &> /dev/null && [ -z "$ip4" ] ; then
|
||||
ip4=$(host -t a $NODE 2>/dev/null | grep -v alias | sed 's/^.*address //')
|
||||
if echo "$ip4" | grep -q NXDOMAIN || echo "$ip4" | grep -q "no A record"; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [ -z "$ip4" ] ; then
|
||||
# MSYS2 has no host or getent, so we need nslookup
|
||||
ip4=$(nslookup $NODE 2>/dev/null | grep -A10 Name | grep -v Name | sed 's/^Address.*: .//')
|
||||
[ -z "$ip4" ] && return 2
|
||||
fi
|
||||
fi
|
||||
|
||||
ip6=$(grep -w "$NODE" /etc/hosts | grep ':' | grep -v '^#' | egrep "[[:space:]]$NODE" | awk '{ print $1 }')
|
||||
if [ -z "$ip6" ] ; then
|
||||
# for IPv6 we often get this :ffff:IPV4 address which isn't of any use
|
||||
#which getent 2>&1 >/dev/null && ip6=$(getent ahostsv6 $NODE | grep $NODE | awk '{ print $1}' | grep -v '::ffff' | uniq)
|
||||
if host -t aaaa $NODE &>/dev/null ; then
|
||||
ip6=$(host -t aaaa $NODE | grep -v alias | grep -v "no AAAA record" | sed 's/^.*address //')
|
||||
else
|
||||
ip6=""
|
||||
fi
|
||||
# MSYS2 has no host or getent, so we need nslookup
|
||||
if [ -z "$ip6" ] ; then
|
||||
ip6=$(nslookup -type=aaaa $NODE 2>/dev/null | grep -A10 Name | grep -v Name | sed 's/^Address.*: .//')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
IPADDRs="$ip4"
|
||||
[ ! -z "$ip6" ] && IP46ADDRs="$ip4 $ip6" || IP46ADDRs="$IPADDRs"
|
||||
|
||||
# FIXME: we could/should test more than one IPv4 addresses if available, same IPv6. For now we test the first IPv4 or CMDLINE_IP:
|
||||
NODEIP=$(echo "$ip4" | head -1)
|
||||
[ -n "$CMDLINE_IP" ] && NODEIP="$CMDLINE_IP"
|
||||
[ -z "$NODEIP" ] && return 3
|
||||
|
||||
# we can't do this as some checks and even openssl are not yet IPv6 safe. BTW: bash sockets do IPv6 transparently!
|
||||
#NODEIP=$(echo "$ip6" | head -1)
|
||||
if which host &> /dev/null; then
|
||||
#rDNS=$(host -t PTR $NODEIP 2>/dev/null | grep -v "is an alias for" | sed -e 's/^.*pointer //' -e 's/\.$//')
|
||||
rDNS=$(host -t PTR $NODEIP 2>/dev/null | grep 'pointer' | sed -e 's/^.*pointer //' -e 's/\.$//')
|
||||
elif which nslookup &> /dev/null; then
|
||||
rDNS=$(nslookup -type=PTR $NODEIP 2> /dev/null | grep -v 'canonical name =' | grep 'name = ' | awk '{ print $NF }' | sed 's/\.$//')
|
||||
fi
|
||||
[ -z "$rDNS" ] && rDNS="--"
|
||||
return 0
|
||||
return 0 # OPTIMAL_PROTO, GET_REQ*/HEAD_REQ* is set now
|
||||
}
|
||||
|
||||
|
||||
display_rdns_etc() {
|
||||
local i
|
||||
|
||||
if [ $(printf "$IP46ADDRs" | wc -w) -gt 1 ]; then
|
||||
if [ $(printf "$IP46ADDRs" | wc -w | sed 's/ //g') -gt 1 ]; then
|
||||
out " further IP addresses: "
|
||||
for i in $IP46ADDRs; do
|
||||
[ "$i" == "$NODEIP" ] && continue
|
||||
@ -3496,7 +3504,8 @@ draw_dotted_line() {
|
||||
|
||||
mx_all_ips() {
|
||||
local mxs mx
|
||||
local mxport
|
||||
local mxport
|
||||
local starttls_proto="smtp"
|
||||
local ret=0
|
||||
|
||||
if which host &> /dev/null; then
|
||||
@ -3515,16 +3524,20 @@ mx_all_ips() {
|
||||
|
||||
mxport=${2:-25}
|
||||
if [ -n "$mxs" ] && [ "$mxs" != ' ' ] ; then
|
||||
starttls_proto="smtp"
|
||||
[[ $mxport == "465" ]] && starttls_proto="" # no starttls for Port 465
|
||||
[[ $mxport == "465" ]] && \
|
||||
starttls_proto="" # no starttls for Port 465, on all other ports we speak starttls
|
||||
pr_bold "Testing now all MX records (on port $mxport): "; outln "$mxs"
|
||||
for mx in $mxs; do
|
||||
draw_dotted_line "-" $TERM_DWITH
|
||||
outln
|
||||
parse_hn_port "$mx:$mxport" $starttls_proto && lets_roll
|
||||
parse_hn_port "$mx:$mxport"
|
||||
determine_ip_addresses
|
||||
NODEIP="$IPADDRs"
|
||||
lets_roll "${starttls_proto}"
|
||||
done
|
||||
draw_dotted_line
|
||||
draw_dotted_line "-" $TERM_DWITH
|
||||
outln
|
||||
pr_bold "Done testing now all MX records (on port $mxport): "; outln "$mxs"
|
||||
else
|
||||
pr_boldln " $1 has no MX records(s)"
|
||||
fi
|
||||
@ -3841,6 +3854,9 @@ startup() {
|
||||
lets_roll() {
|
||||
local ret
|
||||
|
||||
determine_rdns
|
||||
determine_service "$1" # any starttls service goes here
|
||||
|
||||
${do_tls_sockets} && { tls_sockets "$TLS_LOW_BYTE" "$HEX_CIPHER"; exit $?; }
|
||||
|
||||
${do_test_just_one} && test_just_one ${single_cipher}
|
||||
@ -3883,6 +3899,8 @@ lets_roll() {
|
||||
${do_allciphers} && { allciphers; ret=$(($? + ret)); }
|
||||
${do_cipher_per_proto} && { cipher_per_proto; ret=$(($? + ret)); }
|
||||
|
||||
datebanner "Done"
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
@ -3899,21 +3917,48 @@ mybanner
|
||||
openssl_age
|
||||
maketempf
|
||||
|
||||
# TODO: it's ugly to have those two vars here --> main()
|
||||
ret=0
|
||||
ip=""
|
||||
|
||||
#TODO: there shouldn't be the need for a special case for --mx, only the ip adresses we would need upfront and the do-parser
|
||||
if ${do_mx_all_ips} ; then
|
||||
query_globals
|
||||
# if we have just one "do_*" set here --> query_globals: we do a standard run -- otherwise just the one specified
|
||||
query_globals # if we have just 1x "do_*" --> we do a standard run -- otherwise just the one specified
|
||||
[ $? -eq 1 ] && set_scanning_defaults
|
||||
initialize_engine
|
||||
mx_all_ips "${URI}" $PORT
|
||||
ret=$?
|
||||
else
|
||||
parse_hn_port "${URI}" "${STARTTLS_PROTOCOL}"
|
||||
lets_roll
|
||||
ret=$?
|
||||
else
|
||||
parse_hn_port "${URI}" # NODE, URL_PATH, PORT, IPADDR and IP46ADDR is set now
|
||||
determine_ip_addresses
|
||||
if [[ -n "$CMDLINE_IP" ]]; then
|
||||
NODEIP="$CMDLINE_IP" # specific ip address for NODE was supplied
|
||||
lets_roll "${STARTTLS_PROTOCOL}"
|
||||
ret=$?
|
||||
else # no --ip was supplied
|
||||
if [[ $(printf "$IPADDRs" | wc -w | sed 's/ //g') -gt 1 ]]; then # we have more that one ipv4 address to check
|
||||
pr_bold "Testing now all IP addresses (on port $PORT): "; outln "$IPADDRs"
|
||||
for ip in $IPADDRs; do
|
||||
draw_dotted_line "-" $TERM_DWITH
|
||||
outln
|
||||
NODEIP="$ip"
|
||||
lets_roll "${STARTTLS_PROTOCOL}"
|
||||
ret=$(($? + ret))
|
||||
done
|
||||
draw_dotted_line "-" $TERM_DWITH
|
||||
outln
|
||||
pr_bold "Done testing now all IP addresses (on port $PORT): "; outln "$IPADDRs"
|
||||
else # we need just one ip4v to check
|
||||
NODEIP="$IPADDRs"
|
||||
lets_roll "${STARTTLS_PROTOCOL}"
|
||||
ret=$?
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $ret
|
||||
|
||||
# $Id: testssl.sh,v 1.275 2015/06/15 10:13:15 dirkw Exp $
|
||||
|
||||
# $Id: testssl.sh,v 1.276 2015/06/16 12:04:43 dirkw Exp $
|
||||
# vim:ts=5:sw=5
|
||||
# ^^^ FYI: use vim and you will see everything beautifully indented with a 5 char tab
|
||||
|
Loading…
Reference in New Issue
Block a user