From c93f06c1c6d9fc7d5a3b15a7a95d482344d79daf Mon Sep 17 00:00:00 2001 From: Dirk Date: Wed, 27 Mar 2024 17:30:15 +0100 Subject: [PATCH 01/12] Start working on DNS HTTPS RR (RFC 9460) - Initial commit, saving work - Simple test: just copied get_caa_rrecord Also renamed Just copied get_caa_rr_record to get_caa_rrecord to remove the redundant r --- testssl.sh | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index f09339605..6da2d9adf 100755 --- a/testssl.sh +++ b/testssl.sh @@ -9776,17 +9776,32 @@ certificate_info() { fi outln + + +#FIXME: declare vars, put somewhere else + out "$indent"; pr_bold " DNS HTTPS RR"; out " (experimental) " + jsonID="DNS_HTTPS_rrecord" + https_rr_node="$NODE" + https_rr="" + while [[ -z "$https_rr" ]] && [[ -n "$https_rr_node" ]]; do + https_rr="$(get_https_rrecord $https_rr_node)" + tmp=${PIPESTATUS[@]} + [[ $DEBUG -ge 4 ]] && echo "get_https_rrecord: $tmp" + [[ $https_rr_node =~ '.'$ ]] || https_rr_node+="." + https_rr_node=${https_rr_node#*.} + done + out "$indent"; pr_bold " OCSP must staple extension "; must_staple "$json_postfix" "$provides_stapling" "$cert_txt" out "$indent"; pr_bold " DNS CAA RR"; out " (experimental) " - jsonID="DNS_CAArecord" + jsonID="DNS_CAA_rrecord" caa_node="$NODE" caa="" while [[ -z "$caa" ]] && [[ -n "$caa_node" ]]; do - caa="$(get_caa_rr_record $caa_node)" + caa="$(get_caa_rrecord $caa_node)" tmp=${PIPESTATUS[@]} - [[ $DEBUG -ge 4 ]] && echo "get_caa_rr_record: $tmp" + [[ $DEBUG -ge 4 ]] && echo "get_https_caa_rr_record: $tmp" [[ $caa_node =~ '.'$ ]] || caa_node+="." caa_node=${caa_node#*.} done @@ -21257,7 +21272,8 @@ get_aaaa_record() { # RFC6844: DNS Certification Authority Authorization (CAA) Resource Record # arg1: domain to check for -get_caa_rr_record() { +# +get_caa_rrecord() { local raw_caa="" local hash len line local -i len_caa_property @@ -21338,6 +21354,97 @@ get_caa_rr_record() { return 0 } +# See https://www.rfc-editor.org/rfc/rfc9460.html: +# Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records) +# arg1: domain to check for +# +get_https_rrecord() { + local raw_https="" + local hash len line + local -i len_https_property + local https_property_name + local https_property_value + local saved_openssl_conf="$OPENSSL_CONF" + local all_https="" + local noidnout="" + + "$HAS_DIG_NOIDNOUT" && noidnout="+noidnout" + + [[ -n "$NODNS" ]] && return 2 # if minimum DNS lookup was instructed, leave here + + # if there's a type65 record there are two output formats here, mostly depending on age of distribution + # roughly that's the difference between text and binary format + + # 1) 'google.com has HTTPS record 1 . alpn="h2,h3" ' + # 2) 'google.com has TYPE65 record \# 13 0001000001000602683202683 ' + +#FIXME \/ + # for dig +short the output always starts with '0 issue [..]' or '\# 19 [..]' so we normalize thereto to keep https_flag, https_property + # https_property then has key/value pairs, see https://tools.ietf.org/html/rfc6844#section-3 + OPENSSL_CONF="" + if "$HAS_DIG"; then + raw_https="$(dig $DIG_R +search +short +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null | awk '{ print $1" "$2" "$3 }')" + # empty if no CAA record + elif "$HAS_DRILL"; then + raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*HTTPS/ { print $5,$6,$7 }')" + elif "$HAS_HOST"; then + raw_https="$(host -t type65 $1)" + if grep -Ewvq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then + raw_https="$(sed -e 's/^.*has HTTPS record //' -e 's/^.*has TYPE65 record //' <<< "$raw_https")" + fi + elif "$HAS_NSLOOKUP"; then + raw_https="$(strip_lf "$(nslookup -type=type65 $1 | grep -w rdata_65)")" + if [[ -n "$raw_https" ]]; then + raw_https="$(sed 's/^.*rdata_65 = //' <<< "$raw_https")" + fi + else + return 1 + # No dig, drill, host, or nslookup --> complaint was elsewhere already + fi + OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134 + debugme echo $raw_https + + + if [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then + +#FIXME \/ + # for posteo we get this binary format returned e.g. for old dig versions: + # \# 19 0005697373756567656F74727573742E636F6D + # \# 23 0009697373756577696C6467656F74727573742E636F6D + # \# 34 0005696F6465666D61696C746F3A686F73746D617374657240706F73 74656F2E6465 + # # len https @ p o s t e o . d e + while read hash len line ;do + if [[ "${line:0:2}" == "00" ]]; then # probably the https flag, always 00, so we don't keep this + len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do type casting, for posteo we have 05 or 09 here as a string + len_https_property=$((len_https_property*2)) # =>word! Now get name from 4th and value from 4th+len position... + line="${line/ /}" # especially with iodefs there's a blank in the string which we just skip + https_property_name="$(hex2ascii ${line:4:$len_https_property})" + https_property_value="$(hex2ascii "${line:$((4+len_https_property)):100}")" + # echo "${https}=${https}" + all_https+="${https_property_name}=${https_property_value}\n" + else + outln "please report unknown CAA RR $line with flag @ $NODE" + return 7 + fi + done <<< "$raw_https" + sort <<< "$(safe_echo "$all_https")" + return 0 + elif grep -q '"' <<< "$raw_https"; then + raw_https=${raw_https//\"/} # strip all ". Now we should have flag, name, value + #https_property_name="$(awk '{ print $2 }' <<< "$raw_https")" + #https_property_value="$(awk '{ print $3 }' <<< "$raw_https)" + safe_echo "$(sort <<< "$(awk '{ print $2"="$3 }' <<< "$raw_https")")" + return 0 + else + # no https record + return 1 + +# to do: +# 4: check whether $1 is a CNAME and take this + return 0 +} + + # arg1: domain to check for. Returned will be the MX record as a string get_mx_record() { local mx="" From e6bdcee1420c20c510be5947f74712397c011cbe Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 30 Mar 2024 17:17:14 +0100 Subject: [PATCH 02/12] First proper output for HTTPS RR - moved function + output to the very top (still not the right place) - raw_https now should contain the output in any case, binhex parse needs to be completed - fixed bug that CAA records were queried when it was instructed to minimize/skip or use proxy only --- testssl.sh | 174 +++++++++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 78 deletions(-) diff --git a/testssl.sh b/testssl.sh index 6da2d9adf..fa04eb8e2 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2329,6 +2329,7 @@ s_client_options() { # determines whether the port has an HTTP service running or not (plain TLS, no STARTTLS) # arg1 could be the protocol determined as "working". IIS6 needs that. +# sets global $SERVICE # service_detection() { local -i was_killed @@ -2404,7 +2405,7 @@ service_detection() { ;; esac - outln "\n" + outln tmpfile_handle ${FUNCNAME[0]}.txt return 0 } @@ -9776,21 +9777,6 @@ certificate_info() { fi outln - - -#FIXME: declare vars, put somewhere else - out "$indent"; pr_bold " DNS HTTPS RR"; out " (experimental) " - jsonID="DNS_HTTPS_rrecord" - https_rr_node="$NODE" - https_rr="" - while [[ -z "$https_rr" ]] && [[ -n "$https_rr_node" ]]; do - https_rr="$(get_https_rrecord $https_rr_node)" - tmp=${PIPESTATUS[@]} - [[ $DEBUG -ge 4 ]] && echo "get_https_rrecord: $tmp" - [[ $https_rr_node =~ '.'$ ]] || https_rr_node+="." - https_rr_node=${https_rr_node#*.} - done - out "$indent"; pr_bold " OCSP must staple extension "; must_staple "$json_postfix" "$provides_stapling" "$cert_txt" @@ -9798,37 +9784,39 @@ certificate_info() { jsonID="DNS_CAA_rrecord" caa_node="$NODE" caa="" - while [[ -z "$caa" ]] && [[ -n "$caa_node" ]]; do - caa="$(get_caa_rrecord $caa_node)" - tmp=${PIPESTATUS[@]} - [[ $DEBUG -ge 4 ]] && echo "get_https_caa_rr_record: $tmp" - [[ $caa_node =~ '.'$ ]] || caa_node+="." - caa_node=${caa_node#*.} - done - if [[ -n "$caa" ]]; then - pr_svrty_good "available"; out " - please check for match with \"Issuer\" below" - if [[ $(count_lines "$caa") -eq 1 ]]; then - out ": " - else - outln; out "$spaces" - fi - while read caa; do - if [[ -n "$caa" ]]; then - all_caa+="$caa, " - fi - done <<< "$caa" - all_caa=${all_caa%, } # strip trailing comma - pr_italic "$(out_row_aligned_max_width "$all_caa" "$indent " $TERM_WIDTH)" - fileout "${jsonID}${json_postfix}" "OK" "$all_caa" - elif [[ -n "$NODNS" ]]; then + if [[ -n "$NODNS" ]]; then out "(instructed to minimize/skip DNS queries)" fileout "${jsonID}${json_postfix}" "INFO" "check skipped as instructed" elif "$DNS_VIA_PROXY"; then out "(instructed to use the proxy for DNS only)" fileout "${jsonID}${json_postfix}" "INFO" "check skipped as instructed (proxy)" else - pr_svrty_low "not offered" - fileout "${jsonID}${json_postfix}" "LOW" "--" + while [[ -z "$caa" ]] && [[ -n "$caa_node" ]]; do + caa="$(get_caa_rrecord $caa_node)" + tmp=${PIPESTATUS[@]} + [[ $DEBUG -ge 4 ]] && echo "get_https_caa_rr_record: $tmp" + [[ $caa_node =~ '.'$ ]] || caa_node+="." + caa_node=${caa_node#*.} + done + if [[ -n "$caa" ]]; then + pr_svrty_good "available"; out " - please check for match with \"Issuer\" below" + if [[ $(count_lines "$caa") -eq 1 ]]; then + out ": " + else + outln; out "$spaces" + fi + while read caa; do + if [[ -n "$caa" ]]; then + all_caa+="$caa, " + fi + done <<< "$caa" + all_caa=${all_caa%, } # strip trailing comma + pr_italic "$(out_row_aligned_max_width "$all_caa" "$indent " $TERM_WIDTH)" + fileout "${jsonID}${json_postfix}" "OK" "$all_caa" + else + pr_svrty_low "not offered" + fileout "${jsonID}${json_postfix}" "LOW" "--" + fi fi outln @@ -21272,7 +21260,7 @@ get_aaaa_record() { # RFC6844: DNS Certification Authority Authorization (CAA) Resource Record # arg1: domain to check for -# +#FIXME: should be refactored, see get_https_rrecord() get_caa_rrecord() { local raw_caa="" local hash len line @@ -21349,7 +21337,7 @@ get_caa_rrecord() { return 1 fi -# to do: +#TODO: # 4: check whether $1 is a CNAME and take this return 0 } @@ -21372,47 +21360,54 @@ get_https_rrecord() { [[ -n "$NODNS" ]] && return 2 # if minimum DNS lookup was instructed, leave here - # if there's a type65 record there are two output formats here, mostly depending on age of distribution - # roughly that's the difference between text and binary format + # Ff there's a type65 record there are 2x3 output formats, mostly depending on age of distribution + # -- roughly that's the difference between text and binary format -- and the type of DNS client + # for host: # 1) 'google.com has HTTPS record 1 . alpn="h2,h3" ' # 2) 'google.com has TYPE65 record \# 13 0001000001000602683202683 ' -#FIXME \/ - # for dig +short the output always starts with '0 issue [..]' or '\# 19 [..]' so we normalize thereto to keep https_flag, https_property - # https_property then has key/value pairs, see https://tools.ietf.org/html/rfc6844#section-3 + # for drill and dig it's like + #1) google.com. 18665 IN TYPE65 \# 13 00010000010006026832026833 + #2) google.com. 18301 IN HTTPS 1 . alpn="h2,h3" + + # nslookup: + # 1) dev.testssl.sh rdata_65 = 1 . alpn="h2" + # 2) dev.testssl.sh rdata_65 = \# 10 00010000010003026832 + + # we normalize the output during the following so that's either 1) 1 . alpn="h2" or 2) \# 10 00010000010003026832 or empty + OPENSSL_CONF="" + # Read either answer 1) or 2) into raw_https. Should be empty if no such record if "$HAS_DIG"; then - raw_https="$(dig $DIG_R +search +short +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null | awk '{ print $1" "$2" "$3 }')" - # empty if no CAA record + raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)" + # emtpy if there's no such record elif "$HAS_DRILL"; then - raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*HTTPS/ { print $5,$6,$7 }')" + raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*TYPE65/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards + # emtpy if there's no such record elif "$HAS_HOST"; then raw_https="$(host -t type65 $1)" - if grep -Ewvq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then + if grep -Ewq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then + raw_https="" + else raw_https="$(sed -e 's/^.*has HTTPS record //' -e 's/^.*has TYPE65 record //' <<< "$raw_https")" fi elif "$HAS_NSLOOKUP"; then - raw_https="$(strip_lf "$(nslookup -type=type65 $1 | grep -w rdata_65)")" - if [[ -n "$raw_https" ]]; then - raw_https="$(sed 's/^.*rdata_65 = //' <<< "$raw_https")" - fi + raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")" + # emtpy if there's no such record else return 1 # No dig, drill, host, or nslookup --> complaint was elsewhere already fi OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134 - debugme echo $raw_https - - if [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then - -#FIXME \/ - # for posteo we get this binary format returned e.g. for old dig versions: - # \# 19 0005697373756567656F74727573742E636F6D - # \# 23 0009697373756577696C6467656F74727573742E636F6D - # \# 34 0005696F6465666D61696C746F3A686F73746D617374657240706F73 74656F2E6465 - # # len https @ p o s t e o . d e + if [[ -z "$raw_https" ]]; then + : + elif [[ -n "$raw_https" ]]; then + safe_echo "$raw_https" + elif [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then + # now this is binary / hex encoded like \# 10 00010000010003026832 +#FIXME: this probably doesn't work yet and intentionally won't be reached yet while read hash len line ;do if [[ "${line:0:2}" == "00" ]]; then # probably the https flag, always 00, so we don't keep this len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do type casting, for posteo we have 05 or 09 here as a string @@ -21428,18 +21423,11 @@ get_https_rrecord() { fi done <<< "$raw_https" sort <<< "$(safe_echo "$all_https")" - return 0 - elif grep -q '"' <<< "$raw_https"; then - raw_https=${raw_https//\"/} # strip all ". Now we should have flag, name, value - #https_property_name="$(awk '{ print $2 }' <<< "$raw_https")" - #https_property_value="$(awk '{ print $3 }' <<< "$raw_https)" - safe_echo "$(sort <<< "$(awk '{ print $2"="$3 }' <<< "$raw_https")")" - return 0 else - # no https record - return 1 + echo "fixme" + fi -# to do: +#TODO: # 4: check whether $1 is a CNAME and take this return 0 } @@ -22160,6 +22148,34 @@ determine_optimal_proto() { } + +dns_https_rr () { + local jsonID="DNS_HTTPS_rrecord" + local https_rr="" + + out "$indent"; pr_bold " DNS HTTPS RR"; out " (experim.) " + if [[ -n "$NODNS" ]]; then + out "(instructed to minimize/skip DNS queries)" + fileout "${jsonID}" "INFO" "check skipped as instructed" + elif "$DNS_VIA_PROXY"; then + out "(instructed to use the proxy for DNS only)" + fileout "${jsonID}" "INFO" "check skipped as instructed (proxy)" + else + https_rr="$(get_https_rrecord $NODE)" + if [[ -n "$https_rr" ]]; then + pr_svrty_good "yes" ; out " " + prln_italic "$(out_row_aligned_max_width "$https_rr" "$indent " $TERM_WIDTH)" + fileout "${jsonID}" "OK" "$https_rr" + else + outln "--" + fileout "${jsonID}" "INFO" " no resource record found" + fi + fi + +} + + + # arg1 (optional): ftp smtp, lmtp, pop3, imap, sieve, xmpp, xmpp-server, telnet, ldap, postgres, mysql, irc, nntp (maybe with trailing s) # determine_service() { @@ -22198,8 +22214,11 @@ determine_service() { fi GET_REQ11="GET $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\n${basicauth_header}${reqheader}Accept-Encoding: identity\r\nAccept: */*\r\nConnection: Close\r\n\r\n" determine_optimal_proto - # returns always 0: + # returns always 0 and sets $SERVICE service_detection $OPTIMAL_PROTO + if [[ $SERVICE == HTTP ]]; then + dns_https_rr + fi else # STARTTLS if [[ "$1" == postgres ]] || [[ "$1" == sieve ]]; then protocol="$1" @@ -22284,7 +22303,6 @@ determine_service() { # It comes handy later also for STARTTLS injection to define this global. When we do banner grabbing # or replace service_detection() we might not need that anymore SERVICE=$protocol - fi tmpfile_handle ${FUNCNAME[0]}.txt From 4ededc21bc2d29344b9620183ff26968b142e738 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 30 Mar 2024 17:27:10 +0100 Subject: [PATCH 03/12] fix codespell --- .github/workflows/codespell.yml | 2 +- testssl.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index c41d33748..96cf3dc15 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -13,4 +13,4 @@ jobs: - uses: codespell-project/actions-codespell@master with: skip: ca_hashes.txt,tls_data.txt,*.pem,OPENSSL-LICENSE.txt,CREDITS.md,openssl.cnf - ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull + ignore_words_list: borken,gost,ciph,ba,bloc,isnt,chello,fo,alle,anull,experim diff --git a/testssl.sh b/testssl.sh index fa04eb8e2..0d9ead978 100755 --- a/testssl.sh +++ b/testssl.sh @@ -21381,10 +21381,10 @@ get_https_rrecord() { # Read either answer 1) or 2) into raw_https. Should be empty if no such record if "$HAS_DIG"; then raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)" - # emtpy if there's no such record + # empty if there's no such record elif "$HAS_DRILL"; then raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*TYPE65/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards - # emtpy if there's no such record + # empty if there's no such record elif "$HAS_HOST"; then raw_https="$(host -t type65 $1)" if grep -Ewq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then @@ -21394,7 +21394,7 @@ get_https_rrecord() { fi elif "$HAS_NSLOOKUP"; then raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")" - # emtpy if there's no such record + # empty if there's no such record else return 1 # No dig, drill, host, or nslookup --> complaint was elsewhere already From e26e6657f887ce795b1669cd006d5853182b1341 Mon Sep 17 00:00:00 2001 From: Dirk Date: Thu, 5 Sep 2024 18:45:52 +0200 Subject: [PATCH 04/12] Intro section improvements, placement of DNS RR output - intro section has now bold keys and plain values - DNS RR is now below rDNS, if servive is HTTP Open: when ASSUME_HTTP is set and no services was detected, this needs to be handled --- testssl.sh | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/testssl.sh b/testssl.sh index 634f24613..efe0fdc04 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2373,24 +2373,30 @@ service_detection() { debugme head -50 $TMPFILE | sed -e '//,$d' -e '//,$d' -e '/ trying HTTP checks" SERVICE=HTTP fileout "${jsonID}" "DEBUG" "Couldn't determine service -- ASSUME_HTTP set" elif [[ "$CLIENT_AUTH" == required ]] && [[ -z $MTLS ]]; then out " certificate-based authentication without providing client certificate and private key => skipping all HTTP checks" - echo "certificate-based authentication without providing client certificate and private key => skipping all HTTP checks" >$TMPFILE + echo "certificate-based authentication without providing client certificate and private key => skipping all HTTP checks" >$TMPFILE fileout "${jsonID}" "INFO" "certificate-based authentication without providing client certificate and private key => skipping all HTTP checks" else out " Couldn't determine what's running on port $PORT" @@ -2399,7 +2405,7 @@ service_detection() { out " -- ASSUME_HTTP set though" fileout "${jsonID}" "DEBUG" "Couldn't determine service -- ASSUME_HTTP set" else - out ", assuming no HTTP service => skipping all HTTP checks" + out ", assuming no HTTP => skipping all HTTP checks" fileout "${jsonID}" "DEBUG" "Couldn't determine service, skipping all HTTP checks" fi fi @@ -22255,9 +22261,6 @@ determine_service() { determine_optimal_proto # returns always 0 and sets $SERVICE service_detection $OPTIMAL_PROTO - if [[ $SERVICE == HTTP ]]; then - dns_https_rr - fi else # STARTTLS if [[ "$1" == postgres ]] || [[ "$1" == sieve ]]; then protocol="$1" @@ -22407,7 +22410,7 @@ display_rdns_etc() { outln "$PROXYIP:$PROXYPORT " fi if [[ $(count_words "$IP46ADDRs") -gt 1 ]]; then - out " Further IP addresses: $CORRECT_SPACES" + pr_bold " Further IP addresses:"; out " $CORRECT_SPACES" for ip in $IP46ADDRs; do if [[ "$ip" == "$NODEIP" ]] || [[ "[$ip]" == "$NODEIP" ]]; then continue @@ -22428,11 +22431,12 @@ display_rdns_etc() { outln " A record via: $CORRECT_SPACES supplied IP \"$CMDLINE_IP\"" fi fi + pr_bold " rDNS " if [[ "$rDNS" =~ instructed ]]; then - out "$(printf " %-23s " "rDNS ($nodeip):")" + out "$(printf "%-19s" "($nodeip):")" out "$rDNS" elif [[ -n "$rDNS" ]]; then - out "$(printf " %-23s " "rDNS ($nodeip):")" + out "$(printf "%-19s" "($nodeip):")" out "$(out_row_aligned_max_width "$rDNS" " $CORRECT_SPACES" $TERM_WIDTH)" fi } From 4a71ccb29817f5b43e7056832dfc5c7e921c4cab Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Jan 2025 16:57:37 +0100 Subject: [PATCH 05/12] update baselein ... probably need to be done later again when we move the entry to protocols --- t/baseline_data/default_testssl.csvfile | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/t/baseline_data/default_testssl.csvfile b/t/baseline_data/default_testssl.csvfile index bb91a052e..7e75c899c 100644 --- a/t/baseline_data/default_testssl.csvfile +++ b/t/baseline_data/default_testssl.csvfile @@ -1,4 +1,6 @@ "id","fqdn/ip","port","severity","finding","cve","cwe" +"engine_problem","/","443","WARN","No engine or GOST support via engine with your /opt/homebrew/bin/openssl","","" +"DNS_HTTPS_rrecord","testssl.sh/81.169.166.184","443","OK","\# 10 00010000010003026832","","" "service","testssl.sh/81.169.166.184","443","INFO","HTTP","","" "pre_128cipher","testssl.sh/81.169.166.184","443","INFO","No 128 cipher limit bug","","" "SSLv2","testssl.sh/81.169.166.184","443","OK","not offered","","" @@ -19,8 +21,8 @@ "cipherlist_STRONG_NOFS","testssl.sh/81.169.166.184","443","OK","offered","","" "cipherlist_STRONG_FS","testssl.sh/81.169.166.184","443","OK","offered","","" "cipher_order-tls1","testssl.sh/81.169.166.184","443","OK","server","","" -"cipher-tls1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" -"cipher-tls1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" "cipher-tls1_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" "cipher-tls1_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" "cipher-tls1_x39","testssl.sh/81.169.166.184","443","LOW","TLSv1 x39 DHE-RSA-AES256-SHA DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA","","" @@ -28,8 +30,8 @@ "cipher-tls1_x35","testssl.sh/81.169.166.184","443","LOW","TLSv1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA","","" "cipherorder_TLSv1","testssl.sh/81.169.166.184","443","INFO","ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA AES256-SHA","","" "cipher_order-tls1_1","testssl.sh/81.169.166.184","443","OK","server","","" -"cipher-tls1_1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" -"cipher-tls1_1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" "cipher-tls1_1_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" "cipher-tls1_1_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" "cipher-tls1_1_x39","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x39 DHE-RSA-AES256-SHA DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA","","" @@ -37,13 +39,13 @@ "cipher-tls1_1_x35","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA","","" "cipherorder_TLSv1_1","testssl.sh/81.169.166.184","443","INFO","ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA AES256-SHA","","" "cipher_order-tls1_2","testssl.sh/81.169.166.184","443","OK","server","","" -"cipher-tls1_2_xc030","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","","" -"cipher-tls1_2_xc02f","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","","" +"cipher-tls1_2_xc030","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","","" +"cipher-tls1_2_xc02f","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","","" "cipher-tls1_2_x9f","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9f DHE-RSA-AES256-GCM-SHA384 DH 2048 AESGCM 256 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","","" "cipher-tls1_2_x9e","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9e DHE-RSA-AES128-GCM-SHA256 DH 2048 AESGCM 128 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","","" -"cipher-tls1_2_xc028","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc028 ECDHE-RSA-AES256-SHA384 ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","","" -"cipher-tls1_2_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" -"cipher-tls1_2_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_2_xc028","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc028 ECDHE-RSA-AES256-SHA384 ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","","" +"cipher-tls1_2_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc014 ECDHE-RSA-AES256-SHA ECDH 253 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_2_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc013 ECDHE-RSA-AES128-SHA ECDH 253 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" "cipher-tls1_2_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" "cipher-tls1_2_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" "cipher-tls1_2_x6b","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x6b DHE-RSA-AES256-SHA256 DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","","" @@ -70,7 +72,7 @@ "FS_TLS13_sig_algs","testssl.sh/81.169.166.184","443","INFO","RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512","","" "HTTP_status_code","testssl.sh/81.169.166.184","443","INFO","200 OK ('/')","","" "HTTP_clock_skew","testssl.sh/81.169.166.184","443","INFO","0 seconds from localtime","","" -"HTTP_headerTime","testssl.sh/81.169.166.184","443","INFO","1737570310","","" +"HTTP_headerTime","testssl.sh/81.169.166.184","443","INFO","1737993118","","" "HSTS_time","testssl.sh/81.169.166.184","443","OK","362 days (=31337000 seconds) > 15552000 seconds","","" "HSTS_subdomains","testssl.sh/81.169.166.184","443","INFO","only for this domain","","" "HSTS_preload","testssl.sh/81.169.166.184","443","INFO","domain is NOT marked for preloading","","" From 256b24ea484f2f775a6d9d1cc27d2d6bcb97ecdd Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Jan 2025 21:36:40 +0100 Subject: [PATCH 06/12] rename + fix baseline file --- t/baseline_data/testssl.csv | 144 ++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 t/baseline_data/testssl.csv diff --git a/t/baseline_data/testssl.csv b/t/baseline_data/testssl.csv new file mode 100644 index 000000000..0741cbb27 --- /dev/null +++ b/t/baseline_data/testssl.csv @@ -0,0 +1,144 @@ +"id","fqdn/ip","port","severity","finding","cve","cwe" + +"DNS_HTTPS_rrecord","testssl.sh/81.169.166.184","443","OK","1 . alpn='h2'","","" +"service","testssl.sh/81.169.166.184","443","INFO","HTTP","","" +"pre_128cipher","testssl.sh/81.169.166.184","443","INFO","No 128 cipher limit bug","","" +"SSLv2","testssl.sh/81.169.166.184","443","OK","not offered","","" +"SSLv3","testssl.sh/81.169.166.184","443","OK","not offered","","" +"TLS1","testssl.sh/81.169.166.184","443","LOW","offered (deprecated)","","" +"TLS1_1","testssl.sh/81.169.166.184","443","LOW","offered (deprecated)","","" +"TLS1_2","testssl.sh/81.169.166.184","443","OK","offered","","" +"TLS1_3","testssl.sh/81.169.166.184","443","OK","offered with final","","" +"NPN","testssl.sh/81.169.166.184","443","INFO","offered with h2, http/1.1 (advertised)","","" +"ALPN_HTTP2","testssl.sh/81.169.166.184","443","OK","h2","","" +"ALPN","testssl.sh/81.169.166.184","443","INFO","http/1.1","","" +"cipherlist_NULL","testssl.sh/81.169.166.184","443","OK","not offered","","CWE-327" +"cipherlist_aNULL","testssl.sh/81.169.166.184","443","OK","not offered","","CWE-327" +"cipherlist_EXPORT","testssl.sh/81.169.166.184","443","OK","not offered","","CWE-327" +"cipherlist_LOW","testssl.sh/81.169.166.184","443","OK","not offered","","CWE-327" +"cipherlist_3DES_IDEA","testssl.sh/81.169.166.184","443","INFO","not offered","","CWE-310" +"cipherlist_OBSOLETED","testssl.sh/81.169.166.184","443","LOW","offered","","CWE-310" +"cipherlist_STRONG_NOFS","testssl.sh/81.169.166.184","443","OK","offered","","" +"cipherlist_STRONG_FS","testssl.sh/81.169.166.184","443","OK","offered","","" +"cipher_order-tls1","testssl.sh/81.169.166.184","443","OK","server","","" +"cipher-tls1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" +"cipher-tls1_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" +"cipher-tls1_x39","testssl.sh/81.169.166.184","443","LOW","TLSv1 x39 DHE-RSA-AES256-SHA DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_x33","testssl.sh/81.169.166.184","443","LOW","TLSv1 x33 DHE-RSA-AES128-SHA DH 2048 AES 128 TLS_DHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_x35","testssl.sh/81.169.166.184","443","LOW","TLSv1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA","","" +"cipherorder_TLSv1","testssl.sh/81.169.166.184","443","INFO","ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA AES256-SHA","","" +"cipher_order-tls1_1","testssl.sh/81.169.166.184","443","OK","server","","" +"cipher-tls1_1_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_1_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_1_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" +"cipher-tls1_1_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" +"cipher-tls1_1_x39","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x39 DHE-RSA-AES256-SHA DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_1_x33","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x33 DHE-RSA-AES128-SHA DH 2048 AES 128 TLS_DHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_1_x35","testssl.sh/81.169.166.184","443","LOW","TLSv1.1 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA","","" +"cipherorder_TLSv1_1","testssl.sh/81.169.166.184","443","INFO","ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA AES256-SHA","","" +"cipher_order-tls1_2","testssl.sh/81.169.166.184","443","OK","server","","" +"cipher-tls1_2_xc030","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","","" +"cipher-tls1_2_xc02f","testssl.sh/81.169.166.184","443","OK","TLSv1.2 xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 256 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","","" +"cipher-tls1_2_x9f","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9f DHE-RSA-AES256-GCM-SHA384 DH 2048 AESGCM 256 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","","" +"cipher-tls1_2_x9e","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9e DHE-RSA-AES128-GCM-SHA256 DH 2048 AESGCM 128 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","","" +"cipher-tls1_2_xc028","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc028 ECDHE-RSA-AES256-SHA384 ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","","" +"cipher-tls1_2_xc014","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc014 ECDHE-RSA-AES256-SHA ECDH 256 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_2_xc013","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 xc013 ECDHE-RSA-AES128-SHA ECDH 256 AES 128 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_2_x88","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x88 DHE-RSA-CAMELLIA256-SHA DH 2048 Camellia 256 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","","" +"cipher-tls1_2_x45","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x45 DHE-RSA-CAMELLIA128-SHA DH 2048 Camellia 128 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","","" +"cipher-tls1_2_x6b","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x6b DHE-RSA-AES256-SHA256 DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","","" +"cipher-tls1_2_x39","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x39 DHE-RSA-AES256-SHA DH 2048 AES 256 TLS_DHE_RSA_WITH_AES_256_CBC_SHA","","" +"cipher-tls1_2_x67","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x67 DHE-RSA-AES128-SHA256 DH 2048 AES 128 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256","","" +"cipher-tls1_2_x33","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x33 DHE-RSA-AES128-SHA DH 2048 AES 128 TLS_DHE_RSA_WITH_AES_128_CBC_SHA","","" +"cipher-tls1_2_x9d","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9d AES256-GCM-SHA384 RSA AESGCM 256 TLS_RSA_WITH_AES_256_GCM_SHA384","","" +"cipher-tls1_2_x9c","testssl.sh/81.169.166.184","443","OK","TLSv1.2 x9c AES128-GCM-SHA256 RSA AESGCM 128 TLS_RSA_WITH_AES_128_GCM_SHA256","","" +"cipher-tls1_2_x3d","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x3d AES256-SHA256 RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA256","","" +"cipher-tls1_2_x35","testssl.sh/81.169.166.184","443","LOW","TLSv1.2 x35 AES256-SHA RSA AES 256 TLS_RSA_WITH_AES_256_CBC_SHA","","" +"cipherorder_TLSv1_2","testssl.sh/81.169.166.184","443","INFO","ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA AES256-GCM-SHA384 AES128-GCM-SHA256 AES256-SHA256 AES256-SHA","","" +"cipher_order-tls1_3","testssl.sh/81.169.166.184","443","OK","server","","" +"cipher-tls1_3_x1302","testssl.sh/81.169.166.184","443","OK","TLSv1.3 x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384","","" +"cipher-tls1_3_x1303","testssl.sh/81.169.166.184","443","OK","TLSv1.3 x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256","","" +"cipher-tls1_3_x1301","testssl.sh/81.169.166.184","443","OK","TLSv1.3 x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256","","" +"cipherorder_TLSv1_3","testssl.sh/81.169.166.184","443","INFO","TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256","","" +"prioritize_chacha_TLSv1_3","testssl.sh/81.169.166.184","443","INFO","false","","" +"cipher_order","testssl.sh/81.169.166.184","443","OK","server","","" +"FS","testssl.sh/81.169.166.184","443","OK","offered","","" +"FS_ciphers","testssl.sh/81.169.166.184","443","INFO","TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-SHA256 DHE-RSA-AES256-SHA DHE-RSA-CAMELLIA256-SHA TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-SHA256 DHE-RSA-AES128-SHA DHE-RSA-CAMELLIA128-SHA","","" +"FS_ECDHE_curves","testssl.sh/81.169.166.184","443","OK","prime256v1 secp384r1 secp521r1 X25519 X448","","" +"DH_groups","testssl.sh/81.169.166.184","443","OK","Unknown DH group (2048 bits)","","" +"FS_TLS12_sig_algs","testssl.sh/81.169.166.184","443","INFO","RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA+SHA224","","" +"FS_TLS13_sig_algs","testssl.sh/81.169.166.184","443","INFO","RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512","","" +"HTTP_status_code","testssl.sh/81.169.166.184","443","INFO","200 OK ('/')","","" +"HTTP_clock_skew","testssl.sh/81.169.166.184","443","INFO","0 seconds from localtime","","" +"HTTP_headerTime","testssl.sh/81.169.166.184","443","INFO","1738009918","","" +"HSTS_time","testssl.sh/81.169.166.184","443","OK","362 days (=31337000 seconds) > 15552000 seconds","","" +"HSTS_subdomains","testssl.sh/81.169.166.184","443","INFO","only for this domain","","" +"HSTS_preload","testssl.sh/81.169.166.184","443","INFO","domain is NOT marked for preloading","","" +"HPKP","testssl.sh/81.169.166.184","443","INFO","No support for HTTP Public Key Pinning","","" +"banner_server","testssl.sh/81.169.166.184","443","INFO","Never trust a banner","","" +"banner_application","testssl.sh/81.169.166.184","443","INFO","X-Powered-By: A portion of humor","","" +"cookie_count","testssl.sh/81.169.166.184","443","INFO","0 at '/'","","" +"X-Frame-Options","testssl.sh/81.169.166.184","443","OK","DENY","","" +"X-Content-Type-Options","testssl.sh/81.169.166.184","443","OK","nosniff","","" +"Content-Security-Policy","testssl.sh/81.169.166.184","443","OK","script-src 'unsafe-inline'; style-src 'unsafe-inline' 'self'; object-src 'self'; base-uri 'none'; form-action 'none'; img-src 'self' ; default-src 'self'; frame-ancestors 'self'; upgrade-insecure-requests;","","" +"Cross-Origin-Opener-Policy","testssl.sh/81.169.166.184","443","INFO","same-origin-allow-popups","","" +"Cross-Origin-Resource-Policy","testssl.sh/81.169.166.184","443","INFO","same-site","","" +"banner_reverseproxy","testssl.sh/81.169.166.184","443","INFO","--","","CWE-200" +"heartbleed","testssl.sh/81.169.166.184","443","OK","not vulnerable, no heartbeat extension","CVE-2014-0160","CWE-119" +"CCS","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2014-0224","CWE-310" +"ticketbleed","testssl.sh/81.169.166.184","443","OK","no session ticket extension","CVE-2016-9244","CWE-200" +"ROBOT","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2017-17382 CVE-2017-17427 CVE-2017-17428 CVE-2017-13098 CVE-2017-1000385 CVE-2017-13099 CVE-2016-6883 CVE-2012-5081 CVE-2017-6168","CWE-203" +"secure_renego","testssl.sh/81.169.166.184","443","OK","supported","","CWE-310" +"secure_client_renego","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2011-1473","CWE-310" +"CRIME_TLS","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2012-4929","CWE-310" +"BREACH","testssl.sh/81.169.166.184","443","OK","not vulnerable, no gzip/deflate/compress/br HTTP compression - only supplied '/' tested","CVE-2013-3587","CWE-310" +"POODLE_SSL","testssl.sh/81.169.166.184","443","OK","not vulnerable, no SSLv3","CVE-2014-3566","CWE-310" +"fallback_SCSV","testssl.sh/81.169.166.184","443","OK","supported","","" +"SWEET32","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2016-2183 CVE-2016-6329","CWE-327" +"FREAK","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2015-0204","CWE-310" +"DROWN","testssl.sh/81.169.166.184","443","OK","not vulnerable on this host and port","CVE-2016-0800 CVE-2016-0703","CWE-310" +"DROWN_hint","testssl.sh/81.169.166.184","443","INFO","Make sure you don't use this certificate elsewhere with SSLv2 enabled services, see https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=5B4BC205947AED96ECB1879F2668F7F69D696C143BA8D1C69DBB4DC873C92AE9","CVE-2016-0800 CVE-2016-0703","CWE-310" +"LOGJAM","testssl.sh/81.169.166.184","443","OK","not vulnerable, no DH EXPORT ciphers,","CVE-2015-4000","CWE-310" +"LOGJAM-common_primes","testssl.sh/81.169.166.184","443","OK","--","CVE-2015-4000","CWE-310" +"BEAST_CBC_TLS1","testssl.sh/81.169.166.184","443","MEDIUM","ECDHE-RSA-AES256-SHA ECDHE-RSA-AES128-SHA DHE-RSA-CAMELLIA256-SHA DHE-RSA-CAMELLIA128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA AES256-SHA","CVE-2011-3389","CWE-20" +"BEAST","testssl.sh/81.169.166.184","443","LOW","VULNERABLE -- but also supports higher protocols TLSv1.1 TLSv1.2 (likely mitigated)","CVE-2011-3389","CWE-20" +"LUCKY13","testssl.sh/81.169.166.184","443","LOW","potentially vulnerable, uses TLS CBC ciphers","CVE-2013-0169","CWE-310" +"winshock","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2014-6321","CWE-94" +"RC4","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2013-2566 CVE-2015-2808","CWE-310" +"clientsimulation-android_60","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256","","" +"clientsimulation-android_70","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-android_81","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-android_90","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-android_X","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-android_11","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-android_12","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-chrome_79_win10","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-chrome_101_win10","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-firefox_66_win81","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-firefox_100_win10","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-ie_6_xp","testssl.sh/81.169.166.184","443","INFO","No connection","","" +"clientsimulation-ie_8_win7","testssl.sh/81.169.166.184","443","INFO","TLSv1.0 ECDHE-RSA-AES256-SHA","","" +"clientsimulation-ie_8_xp","testssl.sh/81.169.166.184","443","INFO","No connection","","" +"clientsimulation-ie_11_win7","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 DHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-ie_11_win81","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 DHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-ie_11_winphone81","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-SHA","","" +"clientsimulation-ie_11_win10","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-edge_15_win10","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-edge_101_win10_21h2","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-safari_121_ios_122","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-safari_130_osx_10146","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-safari_154_osx_1231","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-java_7u25","testssl.sh/81.169.166.184","443","INFO","TLSv1.0 ECDHE-RSA-AES128-SHA","","" +"clientsimulation-java_8u161","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-java1102","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-java1703","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-go_1178","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-libressl_283","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-openssl_102e","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-openssl_110l","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-openssl_111d","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-openssl_303","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" +"clientsimulation-apple_mail_16_0","testssl.sh/81.169.166.184","443","INFO","TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384","","" +"clientsimulation-thunderbird_91_9","testssl.sh/81.169.166.184","443","INFO","TLSv1.3 TLS_AES_256_GCM_SHA384","","" From 5af98b67dadf98ff079f56456fe69e3f4b25f7b7 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Jan 2025 21:37:48 +0100 Subject: [PATCH 07/12] rename baseline file --- t/61_diff_testsslsh.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/61_diff_testsslsh.t b/t/61_diff_testsslsh.t index 8532e8f00..9779893f2 100755 --- a/t/61_diff_testsslsh.t +++ b/t/61_diff_testsslsh.t @@ -15,7 +15,7 @@ use Text::Diff; my $tests = 0; my $prg="./testssl.sh"; -my $baseline_csv="./t/baseline_data/default_testssl.csvfile"; +my $baseline_csv="./t/baseline_data/testssl.csv"; my $cat_csv="tmp.csv"; my $check2run="-p -s -P --fs -h -U -c -q --ip=one --color 0 --csvfile $cat_csv"; my $uri="testssl.sh"; From 01682617e546dd25891635f7801a98526e516d1d Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Jan 2025 22:19:42 +0100 Subject: [PATCH 08/12] remove empty line --- t/baseline_data/testssl.csv | 1 - 1 file changed, 1 deletion(-) diff --git a/t/baseline_data/testssl.csv b/t/baseline_data/testssl.csv index 0741cbb27..cfd062abb 100644 --- a/t/baseline_data/testssl.csv +++ b/t/baseline_data/testssl.csv @@ -1,5 +1,4 @@ "id","fqdn/ip","port","severity","finding","cve","cwe" - "DNS_HTTPS_rrecord","testssl.sh/81.169.166.184","443","OK","1 . alpn='h2'","","" "service","testssl.sh/81.169.166.184","443","INFO","HTTP","","" "pre_128cipher","testssl.sh/81.169.166.184","443","INFO","No 128 cipher limit bug","","" From 08accf9abefbb24c0e5cea45309b18dad6a1e450 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Jan 2025 23:39:03 +0100 Subject: [PATCH 09/12] update to raw_https But there's lot of work to do --> push to later --- testssl.sh | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/testssl.sh b/testssl.sh index 67fa39d7c..0f7019207 100755 --- a/testssl.sh +++ b/testssl.sh @@ -21514,6 +21514,7 @@ get_aaaa_record() { echo "$ip6" } + # RFC6844: DNS Certification Authority Authorization (CAA) Resource Record # arg1: domain to check for #FIXME: should be refactored, see get_https_rrecord() @@ -21545,12 +21546,16 @@ get_caa_rrecord() { raw_caa="$(drill $1 type257 | awk '/'"^${1}"'.*CAA/ { print $5,$6,$7 }')" elif "$HAS_HOST"; then raw_caa="$(host -t type257 $1)" - if grep -Ewvq "has no CAA|has no TYPE257" <<< "$raw_caa"; then - raw_caa="$(sed -e 's/^.*has CAA record //' -e 's/^.*has TYPE257 record //' <<< "$raw_caa")" + if [[ "$raw_caa" =~ "has no CAA|has no TYPE257" ]]; then + raw_caa="" + else + raw_caa="${raw_caa/$1 has CAA record /}" + raw_caa="${raw_caa/$1 has TYPE257 record /}" fi elif "$HAS_NSLOOKUP"; then raw_caa="$(strip_lf "$(nslookup -type=type257 $1 | grep -w rdata_257)")" if [[ -n "$raw_caa" ]]; then + #FIXME: modernize here or see HTTPS RR raw_caa="$(sed 's/^.*rdata_257 = //' <<< "$raw_caa")" fi else @@ -21631,22 +21636,23 @@ get_https_rrecord() { # 1) dev.testssl.sh rdata_65 = 1 . alpn="h2" # 2) dev.testssl.sh rdata_65 = \# 10 00010000010003026832 - # we normalize the output during the following so that's either 1) 1 . alpn="h2" or 2) \# 10 00010000010003026832 or empty + # we normalize the output during the following so that's e.g. 1 . alpn="h2" OPENSSL_CONF="" - # Read either answer 1) or 2) into raw_https. Should be empty if no such record + # Read either answer 1) or 2) into raw_https. Should be empty if there's no such record if "$HAS_DIG"; then raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)" # empty if there's no such record elif "$HAS_DRILL"; then - raw_https="$(drill $1 type65 | awk '/'"^${1}"'.*TYPE65/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards + raw_https="$(drill $1 type65 | grep -v '^;;' | awk '/'"^${1}"'.*HTTPS/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards # empty if there's no such record elif "$HAS_HOST"; then raw_https="$(host -t type65 $1)" - if grep -Ewq "has no HTTPS|has no TYPE65" <<< "$raw_https"; then + if [[ "$raw_https" =~ "has no HTTPS|has no TYPE65" ]]; then raw_https="" else - raw_https="$(sed -e 's/^.*has HTTPS record //' -e 's/^.*has TYPE65 record //' <<< "$raw_https")" + raw_https="${raw_https/$1 has HTTPS record /}" + raw_https="${raw_https/$1 has TYPE65 record /}" fi elif "$HAS_NSLOOKUP"; then raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")" @@ -21658,15 +21664,13 @@ get_https_rrecord() { OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134 if [[ -z "$raw_https" ]]; then - : - elif [[ -n "$raw_https" ]]; then - safe_echo "$raw_https" + return 1 elif [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then - # now this is binary / hex encoded like \# 10 00010000010003026832 -#FIXME: this probably doesn't work yet and intentionally won't be reached yet while read hash len line ;do - if [[ "${line:0:2}" == "00" ]]; then # probably the https flag, always 00, so we don't keep this - len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do type casting, for posteo we have 05 or 09 here as a string + # \# 10 00010000010003026832 +#FIXME: the following doesn't really work + if [[ "${line:0:2}" == 00 ]]; then # probably the https flag, always 00, so we don't keep this + len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do some kind of type casting len_https_property=$((len_https_property*2)) # =>word! Now get name from 4th and value from 4th+len position... line="${line/ /}" # especially with iodefs there's a blank in the string which we just skip https_property_name="$(hex2ascii ${line:4:$len_https_property})" @@ -21674,17 +21678,14 @@ get_https_rrecord() { # echo "${https}=${https}" all_https+="${https_property_name}=${https_property_value}\n" else - outln "please report unknown CAA RR $line with flag @ $NODE" + outln "please report unknown HTTPS RR $line with flag @ $NODE" return 7 fi done <<< "$raw_https" sort <<< "$(safe_echo "$all_https")" else - echo "fixme" + safe_echo "$raw_https" fi - -#TODO: -# 4: check whether $1 is a CNAME and take this return 0 } From 593247b0eb3baba6c0f8fd46df6cb8e787f69893 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 14 Jul 2025 13:06:00 +0200 Subject: [PATCH 10/12] save work for HTTPS RR (this needs to be re-done)... - add *_HTTPS globals to ensure we can make use of newer DNS binaries - set them appropriately in check_resolver_bins() - parser for those scenarios in get_https_rrecord() (to be tested) - start working on the binary format ~ RFC 3597 as fallback - lots of temporary comments to make it better understandable Worked so far for testssl.net, dev.testssl.sh --- testssl.sh | 192 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 48 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0f7019207..17943e3f2 100755 --- a/testssl.sh +++ b/testssl.sh @@ -375,6 +375,11 @@ HAS_IDN=false HAS_IDN2=false HAS_AVAHIRESOLVE=false HAS_DIG_NOIDNOUT=false +HAS_DIG_HTTPS=false # *_HTTPS: whether the binaries support HTTPS RR directly +HAS_DRILL_HTTPS=false +HAS_HOST_HTTPS=false +HAS_NSLOOKUP_HTTPS=false + HAS_XXD=false OSSL_CIPHERS_S="" @@ -21392,6 +21397,8 @@ get_local_a() { # check_resolver_bins() { local saved_openssl_conf="$OPENSSL_CONF" + local testhost=localhost + local str="" OPENSSL_CONF="" # see https://github.com/testssl/testssl.sh/issues/134 type -p dig &> /dev/null && HAS_DIG=true @@ -21415,12 +21422,36 @@ check_resolver_bins() { HAS_DIG_NOIDNOUT=true fi fi + + # Pre-checking the following for HTTPS RR, see get_https_rrecord() + if "$HAS_DIG"; then + str=$(dig +short $testhost HTTPS) + if [[ -z "$str" ]] && [[ ! "$str" =~ 127.0.0.1 ]] ; then + HAS_DIG_HTTPS=true + fi + elif "$HAS_DRILL"; then + if drill $testhost HTTPS | grep -Eq 'IN.*HTTPS'; then + HAS_DRILL_HTTPS=true + fi + elif "$HAS_HOST"; then + host -t HTTPS $testhost 2>&1 | grep -q 'invalid type' + if [[ $? -ne 0 ]]; then + HAS_HOST_HTTPS=true + fi + elif "$HAS_NSLOOKUP"; then + nslookup -type=HTTPS $testhost | grep -q 'unknown query type' + if [[ $? -ne 0 ]]; then + HAS_NSLOOKUP_HTTPS=true + fi + fi + OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/testssl/testssl.sh/issues/134 return 0 } # 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 saved_openssl_conf="$OPENSSL_CONF" @@ -21469,6 +21500,7 @@ get_a_record() { # arg1: a host name. Returned will be 0-n IPv6 addresses # watch out: $1 can also be a cname! --> all checked +# get_aaaa_record() { local ip6="" local saved_openssl_conf="$OPENSSL_CONF" @@ -21518,6 +21550,7 @@ get_aaaa_record() { # RFC6844: DNS Certification Authority Authorization (CAA) Resource Record # arg1: domain to check for #FIXME: should be refactored, see get_https_rrecord() +# get_caa_rrecord() { local raw_caa="" local hash len line @@ -21603,8 +21636,8 @@ get_caa_rrecord() { return 0 } -# See https://www.rfc-editor.org/rfc/rfc9460.html: -# Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records) +# Service Binding and Parameter Specification via the DNS (SVCB and HTTPS Resource Records). +# https://www.rfc-editor.org/rfc/rfc9460.html # arg1: domain to check for # get_https_rrecord() { @@ -21616,12 +21649,41 @@ get_https_rrecord() { local saved_openssl_conf="$OPENSSL_CONF" local all_https="" local noidnout="" + local svc_priority="" "$HAS_DIG_NOIDNOUT" && noidnout="+noidnout" [[ -n "$NODNS" ]] && return 2 # if minimum DNS lookup was instructed, leave here - # Ff there's a type65 record there are 2x3 output formats, mostly depending on age of distribution + # There's a) the possibility to query HTTPS RR records directly like "dig +short HTTPS dev.testssl.sh", + # "drill HTTPS FQDN" or "nslookup -type=HTTPS FQDN". This works for newer binaries only, unfortunately. + # On top of that b) there's also an extended format which e.g. cloudflare uses: + # $ host -t type65 testssl.net + # testssl.net has TYPE65 record \# 136 00010000010006026833026832000400086815229AAC43CDE7000500 470045FE0D0041A70020002057F87361C7B5A3B8CD3C028892690D35 2863623DAD4E03D33B231A4C3C8BB02B0004000100010012636C6F75 64666C6172652D6563682E636F6D0000000600202606470030310000 00000000AC43CDE72606470030360000000000006815229A + # $ host -t HTTPS testssl.net + # testssl.net has HTTPS record 1 . alpn="h3,h2" ipv4hint=104.21.34.154,172.67.205.231 ech=AEX+DQBBpwAgACBX+HNhx7WjuM08AoiSaQ01KGNiPa1OA9M7IxpMPIuwKwAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:3031::ac43:cde7,2606:4700:3036::6815:229a + # ECH ist the e)ncrypted c)lient h)ello --> for esni (https://datatracker.ietf.org/doc/draft-ietf-tls-esni/) + # Nice descrption: https://www.netmeister.org/blog/https-rrs.html + + # Thus we try first whether we can query the HTTPS records directly as this gives us that already + # in clear text and also we can avoid to parse the encoded format. We'll do that as a fallback but + # at this moment we're trying to scrape only the values alpn from it, if they come first. + + OPENSSL_CONF="" + if "$HAS_DIG_HTTPS"; then + text_httpsrr=$(dig +short +search +timeout=3 +tries=3 $noidnout HTTPS "$1" 2>/dev/null) + elif "$HAS_DRILL_HTTPS"; then + text_httpsrr=$(drill -Q HTTPS $1 2>/dev/null) + elif "$HAS_HOST_HTTPS"; then + text_httpsrr=$(host -t HTTPS $1 2>/dev/null) + text_httpsrr=${text_httpsrr#*record } + elif "$HAS_NSLOOKUP_HTTPS"; then # from 4th field onwards \/ + text_httpsrr=$(nslookup -type=HTTPS $1 | awk '/'"^${1}"'.*rdata_65// { print substr($0,index($0,$4)) }') + fi + + # Now we need to try parsing the raw output + + # If there's a type65 record there are 2x3 output formats, mostly depending on age of distribution # -- roughly that's the difference between text and binary format -- and the type of DNS client # for host: @@ -21638,54 +21700,88 @@ get_https_rrecord() { # we normalize the output during the following so that's e.g. 1 . alpn="h2" - OPENSSL_CONF="" - # Read either answer 1) or 2) into raw_https. Should be empty if there's no such record - if "$HAS_DIG"; then - raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)" - # empty if there's no such record - elif "$HAS_DRILL"; then - raw_https="$(drill $1 type65 | grep -v '^;;' | awk '/'"^${1}"'.*HTTPS/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards - # empty if there's no such record - elif "$HAS_HOST"; then - raw_https="$(host -t type65 $1)" - if [[ "$raw_https" =~ "has no HTTPS|has no TYPE65" ]]; then - raw_https="" - else - raw_https="${raw_https/$1 has HTTPS record /}" - raw_https="${raw_https/$1 has TYPE65 record /}" - fi - elif "$HAS_NSLOOKUP"; then - raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")" - # empty if there's no such record - else - return 1 - # No dig, drill, host, or nslookup --> complaint was elsewhere already - fi - OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134 +# https://datatracker.ietf.org/doc/rfc9460/?include_text=1 - if [[ -z "$raw_https" ]]; then - return 1 - elif [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then - while read hash len line ;do - # \# 10 00010000010003026832 -#FIXME: the following doesn't really work - if [[ "${line:0:2}" == 00 ]]; then # probably the https flag, always 00, so we don't keep this - len_https_property=$(printf "%0d" "$((10#${line:2:2}))") # get len and do some kind of type casting - len_https_property=$((len_https_property*2)) # =>word! Now get name from 4th and value from 4th+len position... - line="${line/ /}" # especially with iodefs there's a blank in the string which we just skip - https_property_name="$(hex2ascii ${line:4:$len_https_property})" - https_property_value="$(hex2ascii "${line:$((4+len_https_property)):100}")" - # echo "${https}=${https}" - all_https+="${https_property_name}=${https_property_value}\n" +#set -x + if [[ -n "$text_httpsrr" ]]; then + safe_echo "$text_httpsrr" + else + if "$HAS_DIG"; then + raw_https="$(dig $DIG_R +short +search +timeout=3 +tries=3 $noidnout type65 "$1" 2>/dev/null)" + # empty if there's no such record + elif "$HAS_DRILL"; then + raw_https="$(drill $1 type65 | grep -v '^;;' | awk '/'"^${1}"'.*TYPE65/ { print substr($0,index($0,$5)) }' )" # from 5th field onwards + # empty if there's no such record + elif "$HAS_HOST"; then + raw_https="$(host -t type65 $1)" + if [[ "$raw_https" =~ "has no HTTPS|has no TYPE65" ]]; then + raw_https="" else - outln "please report unknown HTTPS RR $line with flag @ $NODE" - return 7 + raw_https="${raw_https/$1 has HTTPS record /}" + raw_https="${raw_https/$1 has TYPE65 record /}" fi - done <<< "$raw_https" - sort <<< "$(safe_echo "$all_https")" - else - safe_echo "$raw_https" + elif "$HAS_NSLOOKUP"; then + raw_https="$(strip_lf "$(nslookup -type=type65 $1 | awk '/'"^${1}"'.*rdata_65/ { print substr($0,index($0,$4)) }' )")" + # empty if there's no such record + else + return 1 + # No dig, drill, host, or nslookup --> complaint was elsewhere already + fi + OPENSSL_CONF="$saved_openssl_conf" # see https://github.com/drwetter/testssl.sh/issues/134 + +# Format probably: https://www.rfc-editor.org/rfc/rfc3597 (plus updates) + +# dig +short +search +timeout=3 +tries=3 +noidnout type65 dev.testssl.sh +# 1 . alpn="h2" port=443 ipv6hint=2a01:238:4308:a920:1000:0:b:1337 +# +# 36 000100000100030268320003000201BB000600102A0102384308A920 10000000000B1337 +# alpn| L h 2 443 2a010238... L=len + +# dig +short +search +timeout=3 +tries=3 +noidnout HTTPS testssl.net (split over a couple of lines) +# +# 1. alpn="h3,h2" ipv4hint=104.21.34.154,172.67.205.231 +# 136 00010000010006026833026832000400086815229AAC43CDE7000500 470045FE0D0041F3002000202BD0935ED66980C1862F2570C0D6014D +# alpn| L h 3 L h 2 |IPv4#1||IPv4#2| + +# ech=AEX+DQBBzgAgACBQGA9EFbz+PkJAXSXtcqJluxLlhxIgzhJ+GhTtRd4nJQAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:3031::ac43:cde7,2606:4700:3036::6815:229a +# 733A7CFAAEA5E4DD9CA43D4C24199E330004000100010012636C6F75 64666C6172652D6563682E636F6D0000000600202606470030310000 00000000AC43CDE72606470030360000000000006815229A +# | cloudflare-ech.com | IPv6#1 #IPv6#2 + + if [[ -z "$raw_https" ]]; then + return 1 + elif [[ "$raw_https" =~ \#\ [0-9][0-9] ]]; then + while read hash len line ;do + # \# 10 00010000010003026832 + if [[ "${line:0:4}" == 0001 ]]; then # marker to proceed, belongs to SvcPriority, see rfc9460, 2.1 + svc_priority=$(printf "%0d" "$((10#${line:2:2}))") # 1 is most often, (probbaly not needed) type casting. 0 is alias + if [[ ${line:8:2} != 01 ]]; then # Then comes SvcParamKeys, see rfc 14.3.2 which should be alpn=-1 + continue # If the first element is not alpn, next iteration of loop will fail. + fi # Should we care as SvcParamKey!=alpn doesn't seems not very common? + + xlen_https_property=${line:12:2} # length of alpn entries + https_property_value=${line:16:4} + https_property_name=$(hex2ascii $https_property_value) + if [[ $xlen_https_property != 03 ]]; then # 06 would be another entry + https_property_value=${line:22:4} #FIXME: we can't cope with three entries yet + https_property_name="${https_property_name},$(hex2ascii $https_property_value)" + fi + echo $https_property_name + +# len_https_property=$((len_https_property*2)) # =>word! Now get name from 4th and value from 4th+len position... +# line="${line/ /}" # especially with iodefs there's a blank in the string which we just skip +# https_property_name="$(hex2ascii ${line:4:$len_https_property})" +# https_property_value="$(hex2ascii "${line:$((4+len_https_property)):100}")" + else + outln "please report unknown HTTPS RR $line with flag @ $NODE" + return 7 + fi + done <<< "$raw_https" + else + safe_echo "$raw_https" + fi fi +#set +x + return 0 } @@ -22413,6 +22509,7 @@ determine_optimal_proto() { dns_https_rr () { local jsonID="DNS_HTTPS_rrecord" local https_rr="" + local indent="" out "$indent"; pr_bold " DNS HTTPS RR"; out " (experim.) " if [[ -n "$NODNS" ]]; then @@ -22432,7 +22529,6 @@ dns_https_rr () { fileout "${jsonID}" "INFO" " no resource record found" fi fi - } From d17c17bb6f88fb6db3b49589a6f30e8c60618ad3 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 14 Jul 2025 13:17:25 +0200 Subject: [PATCH 11/12] Fix spelling --- testssl.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 17943e3f2..8ee2d6eaa 100755 --- a/testssl.sh +++ b/testssl.sh @@ -21651,9 +21651,8 @@ get_https_rrecord() { local noidnout="" local svc_priority="" - "$HAS_DIG_NOIDNOUT" && noidnout="+noidnout" - [[ -n "$NODNS" ]] && return 2 # if minimum DNS lookup was instructed, leave here + "$HAS_DIG_NOIDNOUT" && noidnout="+noidnout" # There's a) the possibility to query HTTPS RR records directly like "dig +short HTTPS dev.testssl.sh", # "drill HTTPS FQDN" or "nslookup -type=HTTPS FQDN". This works for newer binaries only, unfortunately. @@ -21662,8 +21661,8 @@ get_https_rrecord() { # testssl.net has TYPE65 record \# 136 00010000010006026833026832000400086815229AAC43CDE7000500 470045FE0D0041A70020002057F87361C7B5A3B8CD3C028892690D35 2863623DAD4E03D33B231A4C3C8BB02B0004000100010012636C6F75 64666C6172652D6563682E636F6D0000000600202606470030310000 00000000AC43CDE72606470030360000000000006815229A # $ host -t HTTPS testssl.net # testssl.net has HTTPS record 1 . alpn="h3,h2" ipv4hint=104.21.34.154,172.67.205.231 ech=AEX+DQBBpwAgACBX+HNhx7WjuM08AoiSaQ01KGNiPa1OA9M7IxpMPIuwKwAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:3031::ac43:cde7,2606:4700:3036::6815:229a - # ECH ist the e)ncrypted c)lient h)ello --> for esni (https://datatracker.ietf.org/doc/draft-ietf-tls-esni/) - # Nice descrption: https://www.netmeister.org/blog/https-rrs.html + # ECH is the encrypted client hello --> for esni (https://datatracker.ietf.org/doc/draft-ietf-tls-esni/) + # Nice descirption: https://www.netmeister.org/blog/https-rrs.html # Thus we try first whether we can query the HTTPS records directly as this gives us that already # in clear text and also we can avoid to parse the encoded format. We'll do that as a fallback but From 93458bb79ef50d88d329d0803422ca1b68e02d49 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 14 Jul 2025 13:19:34 +0200 Subject: [PATCH 12/12] ... now really ;-) --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 8ee2d6eaa..55889ad7a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -21662,7 +21662,7 @@ get_https_rrecord() { # $ host -t HTTPS testssl.net # testssl.net has HTTPS record 1 . alpn="h3,h2" ipv4hint=104.21.34.154,172.67.205.231 ech=AEX+DQBBpwAgACBX+HNhx7WjuM08AoiSaQ01KGNiPa1OA9M7IxpMPIuwKwAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA= ipv6hint=2606:4700:3031::ac43:cde7,2606:4700:3036::6815:229a # ECH is the encrypted client hello --> for esni (https://datatracker.ietf.org/doc/draft-ietf-tls-esni/) - # Nice descirption: https://www.netmeister.org/blog/https-rrs.html + # Nice description: https://www.netmeister.org/blog/https-rrs.html # Thus we try first whether we can query the HTTPS records directly as this gives us that already # in clear text and also we can avoid to parse the encoded format. We'll do that as a fallback but @@ -21752,7 +21752,7 @@ get_https_rrecord() { while read hash len line ;do # \# 10 00010000010003026832 if [[ "${line:0:4}" == 0001 ]]; then # marker to proceed, belongs to SvcPriority, see rfc9460, 2.1 - svc_priority=$(printf "%0d" "$((10#${line:2:2}))") # 1 is most often, (probbaly not needed) type casting. 0 is alias + svc_priority=$(printf "%0d" "$((10#${line:2:2}))") # 1 is most often, (probably not needed) type casting. 0 is alias if [[ ${line:8:2} != 01 ]]; then # Then comes SvcParamKeys, see rfc 14.3.2 which should be alpn=-1 continue # If the first element is not alpn, next iteration of loop will fail. fi # Should we care as SvcParamKey!=alpn doesn't seems not very common?