From f8d3df77475dc60507b1a0933479949b505d92a2 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 28 Jul 2025 15:37:35 +0200 Subject: [PATCH 1/3] Make QUIC protocol detction more reliable The site from that billioniare who made nazi gestures delivers a UDP response without proper TLS handshake. This led to a false positive as if the site supports QUIC via h3. This PR makes the detection of QUIC more robust by adding a certificate check and also take better the return values from `wait_kill()` into account. It also introduces a function to remove any non printable chars (depending on the LC_ALL var): `filter_printable()` Also `sanitze_http_header()` doesn't operate anymore on a global variable which is kind of not best practise as it is easily to avoid here. --- testssl.sh | 59 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/testssl.sh b/testssl.sh index afc53bc..dfd8455 100755 --- a/testssl.sh +++ b/testssl.sh @@ -877,14 +877,20 @@ strip_spaces() { echo "${1// /}" } -# https://web.archive.org/web/20121022051228/http://codesnippets.joyent.com/posts/show/1816 strip_leading_space() { + # https://web.archive.org/web/20121022051228/http://codesnippets.joyent.com/posts/show/1816 printf "%s" "${1#"${1%%[![:space:]]*}"}" } + strip_trailing_space() { printf "%s" "${1%"${1##*[![:space:]]}"}" } +filter_printable() { + # redir of stderr as Mac's sed might throw an error + sed -i 's/[^[:print:]]//g' $1 2>/dev/null +} + is_number() { [[ "$1" =~ ^[1-9][0-9]*$ ]] && \ return 0 || \ @@ -904,8 +910,8 @@ strip_quote() ( )" ) -# Converts a string containing PEM encoded data to one line. pem_to_one_line() { + # Converts a string containing PEM encoded data to one line. local pem="$1" local header="" footer="" @@ -2570,18 +2576,20 @@ connectivity_problem() { fi } + sanitze_http_header() { - # sed implementations tested were sometime not fine with header containing x0d x0a (CRLF) which is the usual - # case. Also we use tr here to remove any crtl chars which the server side offers --> possible security problem - # Only allowed now is LF + CR. See #2337. awk, see above, doesn't seem to care -- but not under MacOS. - sed -e '/^$/q' -e '/^[^a-zA-Z_0-9]$/q' $HEADERFILE | tr -d '\000-\011\013\014\016-\037' >$HEADERFILE.tmp - # Now to be more sure we delete from '<' or '{' maybe with a leading blank until the end - sed -e '/^ *<.*$/d' -e '/^ *{.*$/d' $HEADERFILE.tmp >$HEADERFILE - debugme echo -e "---\n $(< $HEADERFILE) \n---" + # sp,e sed implementations tested were sometime not fine with HTTP headers containing x0d x0a (CRLF) which is + # usuallly the case. Also we use tr here to remove any crtl chars which the server side offers --> possible + # security problem. Only allowed now is LF + CR. See #2337. awk, see above, doesn't seem to care -- not under MacOS. + sed -e '/^$/q' -e '/^[^a-zA-Z_0-9]$/q' $1 | tr -d '\000-\011\013\014\016-\037' >$1.tmp + # Now to be more sure we delete from '<' or '{' maybe with a leading blank until the end (HTTP body) + sed -e '/^ *<.*$/d' -e '/^ *{.*$/d' $1.tmp >$1 + debugme echo -e "---\n $(< $1) \n---" } #problems not handled: chunked +# run_http_header() { local header local referer useragent @@ -2611,9 +2619,9 @@ run_http_header() { tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE NOW_TIME=$(date "+%s") HAD_SLEPT=0 - sanitze_http_header + sanitze_http_header $HEADERFILE else - sanitze_http_header + sanitze_http_header $HEADERFILE # 1st GET request hung and needed to be killed. Check whether it succeeded anyway: if grep -Eiaq "XML|HTML|DOCTYPE|HTTP|Connection" $HEADERFILE; then # correct by seconds we slept, HAD_SLEPT comes from wait_kill() @@ -6216,6 +6224,7 @@ sub_quic() { local alpn="" local use_openssl="" local proxy_hint_str="" + local ret="" local sclient_outfile="$TEMPDIR/$NODEIP.quic_connect.txt" local sclient_errfile="$TEMPDIR/$NODEIP.quic_connect_err.txt" local jsonID="QUIC" @@ -6226,7 +6235,7 @@ sub_quic() { pr_bold " QUIC "; if "$HAS2_QUIC" || "$HAS_QUIC"; then - # Proxying QUIC is not supported + # Proxying QUIC seems not supported # The s_client call would block if either the remote side doesn't support QUIC or outbound traffic is blocked if "$HAS2_QUIC"; then use_openssl="$OPENSSL2" @@ -6236,18 +6245,30 @@ sub_quic() { OPENSSL_CONF='' $use_openssl s_client -quic -alpn h3 -connect $NODEIP:$PORT -servername $NODE $sclient_errfile >$sclient_outfile & wait_kill $! $QUIC_WAIT - if [[ $? -ne 0 ]]; then + ret=$? + if [[ $ret -eq 3 ]]; then + # process was killed if [[ -n "$PROXY" ]]; then - proxy_hint_str="(tried directly, is not proxyable): " + proxy_hint_str="(QUIC is not proxyable, tried directly): " fi outln "${proxy_hint_str}not offered or timed out" fileout "$jsonID" "INFO" "$proxy_hint_str not offered" else - pr_svrty_best "offered (OK)" - fileout "$jsonID" "OK" "offered" - alpn="$(awk -F':' '/^ALPN protocol/ { print $2 }' < $sclient_outfile)" - alpn="$(strip_spaces $alpn)" - outln ": $(awk '/^Protocol:/ { print $2 }' < $sclient_outfile) ($alpn)" + # 0 would be process terminated before be killed. Now findout what happened... + filter_printable $sclient_outfile + if [[ $(< $sclient_outfile) =~ CERTIFICATE----- ]]; then + pr_svrty_best "offered (OK)" + fileout "$jsonID" "OK" "offered" + alpn="$(awk -F':' '/^ALPN protocol/ { print $2 }' < $sclient_outfile)" + alpn="$(strip_spaces $alpn)" + outln ": $(awk '/^Protocol:/ { print $2 }' 2>/dev/null < $sclient_outfile) ($alpn)" + elif [[ $(< $sclient_outfile) =~ ^CONNECTED\( ]]; then + outln "not offered (but UDP connection succeeded)" + fileout "$jsonID" "INFO" "not offered (but UDP connection succeeded)" + else + outln "not offered" + fileout "$jsonID" "INFO" "not offered" + fi fi else prln_local_problem "No OpenSSL QUIC support" From 9166fc717497c30b4fd7077402c0ebfea09d29a6 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 28 Jul 2025 15:43:01 +0200 Subject: [PATCH 2/3] Fix typo in comment --- testssl.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index dfd8455..5136b05 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2578,9 +2578,10 @@ connectivity_problem() { sanitze_http_header() { - # sp,e sed implementations tested were sometime not fine with HTTP headers containing x0d x0a (CRLF) which is - # usuallly the case. Also we use tr here to remove any crtl chars which the server side offers --> possible - # security problem. Only allowed now is LF + CR. See #2337. awk, see above, doesn't seem to care -- not under MacOS. + # some sed implementations were sometime not fine with HTTP headers containing x0d x0a (CRLF: usual case) + # Also we use tr here to remove any crtl chars which the server side offers --> possible security problem. + # Only allowed now is LF + CR. See #2337. awk, see above, doesn't seem to care -- not under MacOS. + sed -e '/^$/q' -e '/^[^a-zA-Z_0-9]$/q' $1 | tr -d '\000-\011\013\014\016-\037' >$1.tmp # Now to be more sure we delete from '<' or '{' maybe with a leading blank until the end (HTTP body) sed -e '/^ *<.*$/d' -e '/^ *{.*$/d' $1.tmp >$1 @@ -2588,7 +2589,7 @@ sanitze_http_header() { } -#problems not handled: chunked +# problems not handled: chunked # run_http_header() { local header From 0225bc3604655a2a83218308d8f1dd7cdd259c94 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 28 Jul 2025 15:44:58 +0200 Subject: [PATCH 3/3] typo fix --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 5136b05..408a5fb 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6255,7 +6255,7 @@ sub_quic() { outln "${proxy_hint_str}not offered or timed out" fileout "$jsonID" "INFO" "$proxy_hint_str not offered" else - # 0 would be process terminated before be killed. Now findout what happened... + # 0 would be process terminated before be killed. Now find out what happened... filter_printable $sclient_outfile if [[ $(< $sclient_outfile) =~ CERTIFICATE----- ]]; then pr_svrty_best "offered (OK)"