Merge branch 'drwetter:3.1dev' into 3.1dev

This commit is contained in:
Jauder Ho 2021-07-31 14:28:57 -07:00 committed by GitHub
commit 91970a2214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 65 deletions

View File

@ -7,7 +7,7 @@ assignees: ''
---
_Feel free to remove this line but please stick to the template. We would like to reproduce the bug and therefore need concise information. _
_Feel free to remove this line but please stick to the template. We would like to reproduce the bug and therefore need concise information. Depending on the completeness of your information provided we might close your issue otherwise. _
**Please check this repo whether this is a known issue**

View File

@ -1,9 +1,9 @@
### Contributions / participation
is always welcome!
is always welcome, here @ gihub or via e-mail.
Note please the following:
Note please the following
* Please read at least the [coding convention](https://github.com/drwetter/testssl.sh/Coding_Convention.md).
* One PR per feature or bug fix or improvement. Please do not mix issues.
@ -13,5 +13,9 @@ Note please the following:
* If it's a new feature please consider writing a unit test for it. You can use e.g. `t/20_baseline_ipv4_http.t` as a template. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start.
* If it's a new feature it would need to be documented in the appropriate section in `help()` and in `~/doc/testssl.1.md`
For questions just open an issue.
For questions just open an issue or feel free to send me an e-mail.
#### Patches via e-mail
Of course it is fine when you want to send in patches to use e-mail. For the address please grep for SWCONTACT in testssl.sh .
Let me know how you like them to be attributed.

View File

@ -1,4 +1,4 @@
FROM alpine:3.12
FROM alpine:3.13
RUN apk update && \
apk upgrade && \

View File

@ -351,6 +351,8 @@ HAS_ZLIB=false
HAS_UDS=false
HAS_UDS2=false
HAS_DIG=false
HAS_DIG_R=true
DIG_R="-r"
HAS_HOST=false
HAS_DRILL=false
HAS_NSLOOKUP=false
@ -634,7 +636,9 @@ pr_bold() { tm_bold "$1"; [[ "$COLOR" -ne 0 ]] && html_out "<span style=\"
prln_bold() { pr_bold "$1" ; outln; }
NO_ITALICS=false
if [[ $SYSTEM == OpenBSD ]]; then
if [[ $TERM == screen ]]; then
NO_ITALICS=true
elif [[ $SYSTEM == OpenBSD ]]; then
NO_ITALICS=true
elif [[ $SYSTEM == FreeBSD ]]; then
if [[ ${SYSTEMREV%\.*} -le 9 ]]; then
@ -1555,6 +1559,14 @@ prepare_logging() {
################### END all file output functions #########################
# prints a string of n spaces (n < 80)
print_n_spaces() {
local -i n="$1"
local spaces=" "
out "${spaces:0:n}"
}
# prints out multiple lines in $1, left aligned by spaces in $2
out_row_aligned() {
local first=true
@ -1638,14 +1650,12 @@ out_row_aligned_max_width_by_entry() {
print_fixed_width() {
local text="$1"
local -i i len width="$2"
local -i len width="$2"
local print_function="$3"
len=${#text}
$print_function "$text"
for (( i=len; i <= width; i++ )); do
out " "
done
print_n_spaces "$((width-len+1))"
}
# saves $TMPFILE or file supplied in $2 under name "$TEMPDIR/$NODEIP.$1".
@ -3570,9 +3580,7 @@ neat_list(){
fi
fi
len=${#kx}
for (( i=len; i<10; i++ )); do
out " "
done
print_n_spaces "$((10-len))"
out "$(printf -- " %-12s%-8s " "$enc" "$strength")"
if [[ "$COLOR" -le 2 ]]; then
if [[ "$DISPLAY_CIPHERNAMES" == rfc ]]; then
@ -4984,13 +4992,9 @@ run_client_simulation() {
pr_cipher_quality "$cipher"
fi
if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]]; then
for (( j=${#cipher}; j < 34; j++ )); do
out " "
done
print_n_spaces "$((34-${#cipher}))"
else
for (( j=${#cipher}; j < 50; j++ )); do
out " "
done
print_n_spaces "$((50-${#cipher}))"
fi
if [[ -n "$what_dh" ]]; then
[[ -n "$curve" ]] && curve="($curve)"
@ -19562,6 +19566,7 @@ HAS_IDN: $HAS_IDN
HAS_IDN2: $HAS_IDN2
HAS_AVAHIRESOLVE: $HAS_AVAHIRESOLVE
HAS_DIG_NOIDNOUT: $HAS_DIG_NOIDNOUT
HAS_DIG_R: $HAS_DIG_R
HAS_XXD: $HAS_XXD
PATH: $PATH
@ -20024,12 +20029,18 @@ check_resolver_bins() {
type -p idn &>/dev/null && HAS_IDN=true
type -p idn2 &>/dev/null && HAS_IDN2=true
# Old dig versions don't have an option to ignore $HOME/.digrc
if dig -r 2>&1 | grep -qiE 'invalid|usage'; then
HAS_DIG_R=false
DIG_R=""
fi
OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134
if ! "$HAS_DIG" && ! "$HAS_HOST" && ! "$HAS_DRILL" && ! "$HAS_NSLOOKUP"; then
fatal "Neither \"dig\", \"host\", \"drill\" or \"nslookup\" is present" $ERR_DNSBIN
fi
if "$HAS_DIG"; then
if dig +noidnout -t a 2>&1 | grep -Eq 'Invalid option: \+noidnout|IDN support not enabled'; then
if dig $DIG_R +noidnout -t a 2>&1 | grep -Eq 'Invalid option: \+noidnout|IDN support not enabled'; then
:
else
HAS_DIG_NOIDNOUT=true
@ -20063,13 +20074,13 @@ get_a_record() {
if "$HAS_AVAHIRESOLVE"; then
ip4=$(filter_ip4_address $(avahi-resolve -4 -n "$1" 2>/dev/null | awk '{ print $2 }'))
elif "$HAS_DIG"; then
ip4=$(filter_ip4_address $(dig @224.0.0.251 -p 5353 +short -t a +notcp "$1" 2>/dev/null | sed '/^;;/d'))
ip4=$(filter_ip4_address $(dig $DIG_R @224.0.0.251 -p 5353 +short -t a +notcp "$1" 2>/dev/null | sed '/^;;/d'))
else
fatal "Local hostname given but no 'avahi-resolve' or 'dig' available." $ERR_DNSBIN
fi
fi
if [[ -z "$ip4" ]] && "$HAS_DIG"; then
ip4=$(filter_ip4_address $(dig +short +timeout=2 +tries=2 $noidnout -t a "$1" 2>/dev/null | awk '/^[0-9]/ { print $1 }'))
ip4=$(filter_ip4_address $(dig $DIG_R +short +timeout=2 +tries=2 $noidnout -t a "$1" 2>/dev/null | awk '/^[0-9]/ { print $1 }'))
fi
if [[ -z "$ip4" ]] && "$HAS_HOST"; then
ip4=$(filter_ip4_address $(host -t a "$1" 2>/dev/null | awk '/address/ { print $NF }'))
@ -20107,12 +20118,12 @@ get_aaaa_record() {
if "$HAS_AVAHIRESOLVE"; then
ip6=$(filter_ip6_address $(avahi-resolve -6 -n "$1" 2>/dev/null | awk '{ print $2 }'))
elif "$HAS_DIG"; then
ip6=$(filter_ip6_address $(dig @ff02::fb -p 5353 -t aaaa +short +notcp "$NODE"))
ip6=$(filter_ip6_address $(dig $DIG_R @ff02::fb -p 5353 -t aaaa +short +notcp "$NODE"))
else
fatal "Local hostname given but no 'avahi-resolve' or 'dig' available." $ERR_DNSBIN
fi
elif "$HAS_DIG"; then
ip6=$(filter_ip6_address $(dig +short +timeout=2 +tries=2 $noidnout -t aaaa "$1" 2>/dev/null | awk '/^[0-9]/ { print $1 }'))
ip6=$(filter_ip6_address $(dig $DIG_R +short +timeout=2 +tries=2 $noidnout -t aaaa "$1" 2>/dev/null | awk '/^[0-9]/ { print $1 }'))
elif "$HAS_HOST"; then
ip6=$(filter_ip6_address $(host -t aaaa "$1" | awk '/address/ { print $NF }'))
elif "$HAS_DRILL"; then
@ -20148,7 +20159,7 @@ get_caa_rr_record() {
# caa_property then has key/value pairs, see https://tools.ietf.org/html/rfc6844#section-3
OPENSSL_CONF=""
if "$HAS_DIG"; then
raw_caa="$(dig +short +timeout=3 +tries=3 $noidnout type257 "$1" 2>/dev/null | awk '{ print $1" "$2" "$3 }')"
raw_caa="$(dig $DIG_R +short +timeout=3 +tries=3 $noidnout type257 "$1" 2>/dev/null | awk '{ print $1" "$2" "$3 }')"
# empty if no CAA record
elif "$HAS_DRILL"; then
raw_caa="$(drill $1 type257 | awk '/'"^${1}"'.*CAA/ { print $5,$6,$7 }')"
@ -20219,7 +20230,7 @@ get_mx_record() {
if "$HAS_HOST"; then
mx="$(host -t MX "$1" 2>/dev/null | awk '/is handled by/ { print $(NF-1), $NF }')"
elif "$HAS_DIG"; then
mx="$(dig +short $noidnout -t MX "$1" 2>/dev/null | awk '/^[0-9]/ { print $1" "$2 }')"
mx="$(dig $DIG_R +short $noidnout -t MX "$1" 2>/dev/null | awk '/^[0-9]/ { print $1" "$2 }')"
elif "$HAS_DRILL"; then
mx="$(drill mx $1 | awk '/IN[ \t]MX[ \t]+/ { print $(NF-1), $NF }')"
elif "$HAS_NSLOOKUP"; then
@ -20246,7 +20257,7 @@ get_txt_record() {
if "$HAS_HOST"; then
record="$(host -t TXT "$1" 2>/dev/null | awk -F\" '/descriptive text/ { print $(NF-1) }')"
elif "$HAS_DIG"; then
record="$(dig +short $noidnout -t TXT "$1" 2>/dev/null)"
record="$(dig $DIG_R +short $noidnout -t TXT "$1" 2>/dev/null)"
elif "$HAS_DRILL"; then
record="$(drill txt $1 | awk -F\" '/^[a-z0-9].*TXT/ { print $(NF-1) }')"
elif "$HAS_NSLOOKUP"; then
@ -20346,11 +20357,11 @@ determine_rdns() {
if "$HAS_AVAHIRESOLVE"; then
rDNS=$(avahi-resolve -a $nodeip 2>/dev/null | awk '{ print $2 }')
elif "$HAS_DIG"; then
rDNS=$(dig -x $nodeip @224.0.0.251 -p 5353 +notcp +noall +answer +short | awk '{ print $1 }')
rDNS=$(dig $DIG_R -x $nodeip @224.0.0.251 -p 5353 +notcp +noall +answer +short | awk '{ print $1 }')
fi
elif "$HAS_DIG"; then
# 1+2 should suffice. It's a compromise for if e.g. network is down but we have a docker/localhost server
rDNS=$(dig -x $nodeip +timeout=1 +tries=2 +noall +answer +short | awk '{ print $1 }') # +short returns also CNAME, e.g. openssl.org
rDNS=$(dig $DIG_R -x $nodeip +timeout=1 +tries=2 +noall +answer +short | awk '{ print $1 }') # +short returns also CNAME, e.g. openssl.org
elif "$HAS_HOST"; then
rDNS=$(host -t PTR $nodeip 2>/dev/null | awk '/pointer/ { print $NF }')
elif "$HAS_DRILL"; then
@ -21353,7 +21364,7 @@ nmap_to_plain_file() {
local target_fname=""
local oneline=""
local ip hostdontcare round_brackets ports_specs starttls
local tmp port host_spec protocol dontcare dontcare1
local tmp port host_spec protocol ssl_hint dontcare dontcare1
#FIXME: IPv6 is missing here
# Ok, since we are here we are sure to have an nmap file. To avoid questions we make sure it's the right format too
@ -21368,17 +21379,12 @@ nmap_to_plain_file() {
else
fatal "Nmap file $FNAME is not in grep(p)able format (-oG filename.g(n)map)" $ERR_FNAMEPARSE
fi
# strip extension and create output file *.txt in same folder
# create ${FNAME%.*}.txt in $TEMPDIR
target_fname="${FNAME%.*}.txt"
> "${target_fname}"
if [[ $? -ne 0 ]]; then
# try to just create ${FNAME%.*}.txt in the same dir as the gnmap file failed.
# backup is using one in $TEMPDIR
target_fname="${target_fname##*\/}" # strip path (Unix)
target_fname="${target_fname##*\\}" # strip path (Dos)
target_fname="$TEMPDIR/$target_fname"
> "${target_fname}" || fatal "Cannot create \"${target_fname}\"" $ERR_FCREATE
fi
target_fname="${target_fname##*\/}" # strip path (Unix)
target_fname="${target_fname##*\\}" # strip path (Dos)
target_fname="$TEMPDIR/$target_fname"
> "${target_fname}" || fatal "Cannot create \"${target_fname}\"" $ERR_FCREATE
# Line x: "Host: AAA.BBB.CCC.DDD (<FQDN>) Status: Up"
# Line x+1: "Host: AAA.BBB.CCC.DDD (<FQDN>) Ports: 443/open/tcp//https///"
@ -21399,11 +21405,15 @@ nmap_to_plain_file() {
while read -r oneline; do
# 25/open/tcp//smtp//<banner>/,
[[ "$oneline" =~ '/open/tcp/' ]] || continue # no open tcp for this port on this IP --> move on
IFS=/ read -r port dontcare protocol dontcare1 <<< "$oneline"
starttls="$(ports2starttls $port)"
[[ $? -eq 1 ]] && continue # nmap got a port but we don't know how to speak to
[[ "$DEBUG" -ge 1 ]] && echo "${starttls}$host_spec:$port"
echo "${starttls}${host_spec}:${port}" >>"$target_fname"
IFS=/ read -r port dontcare protocol ssl_hint dontcare1 <<< "$oneline"
if [[ "$ssl_hint" =~ ^(ssl|https) ]] || [[ "$dontcare1" =~ ^(ssl|https) ]]; then
echo "${host_spec}:${port}" >>"$target_fname"
else
starttls="$(ports2starttls $port)"
[[ $? -eq 1 ]] && continue # nmap got a port but we don't know how to speak to
[[ "$DEBUG" -ge 1 ]] && echo "${starttls}$host_spec:$port"
echo "${starttls}${host_spec}:${port}" >>"$target_fname"
fi
done < <(tr ',' '\n' <<< "$ports_specs")
done < "$FNAME"
[[ "$DEBUG" -ge 1 ]] && echo

View File

@ -1,43 +1,167 @@
#!/usr/bin/env bash
set -e
# Utility which converts grepable nmap outout to testssl's file input
# It is just borrowed from testssl.sh
# License see testssl.sh
# utility which converts grepable nmap outout to testssl's file input
echo A | sed -E 's/A//' >/dev/null 2>&1 && \
declare -r HAS_SED_E=true || \
declare -r HAS_SED_E=false
usage() {
cat << EOF
usage:
"$0 filename<.gmap>": looks for filename/filename.gmap and converts into basename \$(filename)-testssl.txt"
"$0 filename<.gmap>" "scan option": same as before, only adds testssl.sh scan option in front of IPs"
"$0 <filename>": looks for <filename> (nmap gmap format) and converts into basename \$(filename)-testssl.txt"
EOF
exit 0
}
[ -z "$1" ] && usage
FNAME="$1"
OPT2ADD="${2:-}"
fatal () {
echo "$1" >&2
exit $2
}
if ! grep -q gmap <<< "$FNAME"; then
FNAME="$FNAME.gmap"
fi
[ ! -e $FNAME ] && echo "$FNAME not readable" && exit 2
is_ipv4addr() {
local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
local ipv4address="$octet\\.$octet\\.$octet\\.$octet"
[[ -z "$1" ]] && return 1
TARGET_FNAME=${FNAME%.*}-testssl.txt
# Check that $1 contains an IPv4 address and nothing else
[[ "$1" =~ $ipv4address ]] && [[ "$1" == $BASH_REMATCH ]] && \
return 0 || \
return 1
}
# test whether there's more than one "open" per line
while read -r oneline; do
if [ $(echo "${oneline}" | tr ',' '\n' | grep -wc 'open') -gt 1 ]; then
# not supported currently
echo "$FNAME contains at least on one line more than 1x\"open\""
exit 3
filter_ip4_address() {
local a
for a in "$@"; do
if ! is_ipv4addr "$a"; then
continue
fi
if "$HAS_SED_E"; then
sed -E 's/[^[:digit:].]//g' <<< "$a" | sed -e '/^$/d'
else
sed -r 's/[^[:digit:].]//g' <<< "$a" | sed -e '/^$/d'
fi
done
}
# arg1: a host name. Returned will be 0-n IPv4 addresses
# watch out: $1 can also be a cname! --> all checked
get_a_record() {
local ip4=""
local noidnout=""
ip4=$(filter_ip4_address $(dig -r +short +timeout=2 +tries=2 -t a "$1" 2>/dev/null | awk '/^[0-9]/ { print $1 }'))
if [[ -z "$ip4" ]]; then
ip4=$(filter_ip4_address $(host -t a "$1" 2>/dev/null | awk '/address/ { print $NF }'))
fi
done < "$FNAME"
if [[ -z "$ip4" ]]; then
ip4=$(filter_ip4_address $(drill a "$1" | awk '/ANSWER SECTION/,/AUTHORITY SECTION/ { print $NF }' | awk '/^[0-9]/'))
fi
echo "$ip4"
}
ports2starttls() {
local tcp_port=$1
local ret=0
# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
case $tcp_port in
21) echo "-t ftp " ;;
23) echo "-t telnet " ;;
119|433) echo "-t nntp " ;; # to come
25|587) echo "-t smtp " ;;
110) echo "-t pop3 " ;;
143) echo "-t imap " ;;
389) echo "-t ldap ";;
3306) echo "-t mysql " ;;
5222) echo "-t xmpp " ;; # domain of jabber server maybe needed
5432) echo "-t postgres " ;;
563) ;; # NNTPS
636) ;; # LDAP
1443|8443|443|981) ;; # HTTPS
465) ;; # HTTPS | SMTP
631) ;; # CUPS
853) ;; # DNS over TLS
995|993) ;; # POP3|IMAP
3389) ;; # RDP
*) ret=1 ;; # we don't know this ports so we rather do not scan it
esac
return $ret
}
nmap_to_plain_file () {
local fname="$1"
local target_fname=""
local oneline=""
local ip hostdontcare round_brackets ports_specs starttls
local tmp port host_spec protocol ssl_hint dontcare dontcare1
# Ok, since we are here we are sure to have an nmap file. To avoid questions we make sure it's the right format too
if [[ "$(head -1 "$fname")" =~ ( -oG )(.*) ]] || [[ "$(head -1 "$fname")" =~ ( -oA )(.*) ]] ; then
# yes, greppable
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" 250
else
fatal "strange, nmap grepable misses \"Status\"" 251
fi
else
fatal "Nmap file $fname is not in grep(p)able format (-oG filename.g(n)map)" 250
fi
target_fname="${fname%.*}"-testssl.txt
[[ -e $target_fname ]] && fatal "$target_fname already exists" 3
> "${target_fname}" || fatal "Cannot create \"${target_fname}\"" 252
# Line x: "Host: AAA.BBB.CCC.DDD (<FQDN>) Status: Up"
# Line x+1: "Host: AAA.BBB.CCC.DDD (<FQDN>) Ports: 443/open/tcp//https///"
# (or): Host: AAA.BBB.CCC.DDD (<FQDN>) Ports: 22/open/tcp//ssh//<banner>/, 25/open/tcp//smtp//<banner>/, 443/open/tcp//ssl|http//<banner>
while read -r hostdontcare ip round_brackets tmp ports_specs; do
[[ "$ports_specs" =~ "Status: " ]] && continue # we don't need this
[[ "$ports_specs" =~ '/open/tcp/' ]] || continue # no open tcp at all for this IP --> move
host_spec="$ip"
fqdn="${round_brackets/\(/}"
fqdn="${fqdn/\)/}"
if [[ -n "$fqdn" ]]; then
tmp="$(get_a_record "$fqdn")"
if [[ "$tmp" == "$ip" ]]; then
host_spec="$fqdn"
fi
fi
while read -r oneline; do
# 25/open/tcp//smtp//<banner>/,
[[ "$oneline" =~ '/open/tcp/' ]] || continue # no open tcp for this port on this IP --> move on
IFS=/ read -r port dontcare protocol ssl_hint dontcare1 <<< "$oneline"
if [[ "$ssl_hint" =~ ^(ssl|https) ]] || [[ "$dontcare1" =~ ^(ssl|https) ]]; then
echo "${host_spec}:${port}" >>"$target_fname"
else
starttls="$(ports2starttls $port)"
[[ $? -eq 1 ]] && continue # nmap got a port but we don't know how to speak to
echo "${starttls}${host_spec}:${port}" >>"$target_fname"
fi
done < <(tr ',' '\n' <<< "$ports_specs")
done < "$fname"
[[ -s "$target_fname" ]] || fatal "Couldn't find any open port in $fname" 253
echo "$target_fname written successfully"
return 0
}
[[ -z "$1" ]] && usage
FNAME="$1"
[[ ! -e $FNAME ]] && echo "$FNAME not readable" && exit 2
nmap_to_plain_file "$FNAME"
awk '/\<open\>/ { print "'"${OPT2ADD}"' " $2":"$5 }' "$FNAME" | sed 's/\/open.*$//g' >"$TARGET_FNAME"
exit $?
# vim:ts=5:sw=5:expandtab