Merge branch '3.2' into bump_version

This commit is contained in:
Dirk 2025-01-24 14:41:21 +01:00
commit bf75a91bc7

View File

@ -248,6 +248,7 @@ OPENSSL2=${OPENSSL2:-/usr/bin/openssl} # This will be openssl version >=1.1.1 (
OPENSSL2_HAS_TLS_1_3=false # If we run with supplied binary AND $OPENSSL2 supports TLS 1.3 this will be set to true OPENSSL2_HAS_TLS_1_3=false # If we run with supplied binary AND $OPENSSL2 supports TLS 1.3 this will be set to true
OSSL_SHORTCUT=${OSSL_SHORTCUT:-true} # If you don't want automagically switch from $OPENSSL to $OPENSSL2 for TLS 1.3-only hosts, set this to false OSSL_SHORTCUT=${OSSL_SHORTCUT:-true} # If you don't want automagically switch from $OPENSSL to $OPENSSL2 for TLS 1.3-only hosts, set this to false
OPENSSL_LOCATION="" OPENSSL_LOCATION=""
OPENSSL_NOTIMEOUT="" # Needed for renegotiation tests
IKNOW_FNAME=false IKNOW_FNAME=false
FIRST_FINDING=true # is this the first finding we are outputting to file? FIRST_FINDING=true # is this the first finding we are outputting to file?
JSONHEADER=true # include JSON headers and footers in HTML file, if one is being created JSONHEADER=true # include JSON headers and footers in HTML file, if one is being created
@ -17159,7 +17160,7 @@ run_ticketbleed() {
# #
run_renego() { run_renego() {
local legacycmd="" proto="$OPTIMAL_PROTO" local legacycmd="" proto="$OPTIMAL_PROTO"
local sec_renego sec_client_renego local sec_renego
local -i ret=0 local -i ret=0
local cve="" local cve=""
local cwe="CWE-310" local cwe="CWE-310"
@ -17251,110 +17252,113 @@ run_renego() {
elif [[ "$CLIENT_AUTH" == required ]] && [[ -z "$MTLS" ]]; then elif [[ "$CLIENT_AUTH" == required ]] && [[ -z "$MTLS" ]]; then
prln_warning "not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested" prln_warning "not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested"
fileout "$jsonID" "WARN" "not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested" fileout "$jsonID" "WARN" "not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested"
sec_client_renego=1
else else
# We will need $ERRFILE for mitigation detection # We will need $ERRFILE for mitigation detection
if [[ $ERRFILE =~ dev.null ]]; then if [[ $ERRFILE =~ dev.null ]]; then
ERRFILE=$TEMPDIR/errorfile.txt || exit $ERR_FCREATE ERRFILE=$TEMPDIR/errorfile.txt || exit $ERR_FCREATE
# cleanup previous run if any (multiple IP)
rm -f $ERRFILE
restore_errfile=1 restore_errfile=1
else else
restore_errfile=0 restore_errfile=0
fi fi
# We need up to two tries here, as some LiteSpeed servers don't answer on "R" and block. Thus first try in the background [[ "$SERVICE" != HTTP ]] && ssl_reneg_attempts=1
# msg enables us to look deeper into it while debugging # We try again if server is HTTP. This could be either a node.js server or something else.
echo R | $OPENSSL s_client $(s_client_options "$proto $BUGS $legacycmd $STARTTLS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>>$ERRFILE & # Mitigations (default values) for:
wait_kill $! $HEADER_MAXSLEEP # - node.js allows 3x R and then blocks. So then 4x should be tested.
if [[ $? -eq 3 ]]; then # - F5 BIG-IP ADS allows 5x R and then blocks. So then 6x should be tested.
pr_svrty_good "likely not vulnerable (OK)"; outln ", timed out" # it hung # - Stormshield allows 9x and then blocks. So then 10x should be tested.
fileout "$jsonID" "OK" "likely not vulnerable (timed out)" "$cve" "$cwe" # This way we save a couple seconds as we weeded out the ones which are more robust
sec_client_renego=1 # Amount of times tested before breaking is set in SSL_RENEG_ATTEMPTS.
else
# second try in the foreground as we are sure now it won't hang # Clear the log to not get the content of previous run before the execution of the new one.
(echo R; sleep 1) | $OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>>$ERRFILE # (Used in the loop tests before s_client invocation)
sec_client_renego=$? echo -n > $TMPFILE
# 0 means client is renegotiating & doesn't return an error --> vuln! echo -n > $ERRFILE
# 1 means client tried to renegotiating but the server side errored then. You still see RENEGOTIATING in the output # RENEGOTIATING wait loop watchdog file
if tail -5 $TMPFILE| grep -qa '^closed'; then touch $TEMPDIR/allowed_to_loop
# Exemption from above: server closed the connection but return value was zero # If we dont wait for the session to be established on slow server, we will try to re-negotiate
# See https://github.com/testssl/testssl.sh/issues/1725 and referenced issue @haproxy # too early losing all the attempts before the session establishment as OpenSSL will not buffer them
sec_client_renego=1 # (only the first will be till the establishement of the session).
(j=0; while [[ $(grep -ac '^SSL-Session:' $TMPFILE) -ne 1 ]] && [[ $j -lt 30 ]]; do sleep $ssl_reneg_wait; ((j++)); done; \
# Connection could be closed by the server with 0 return value. We do one more iteration to not close
# s_client STDIN too early as the close could come at any time and race with the tear down of s_client.
# See https://github.com/drwetter/testssl.sh/issues/2590
# In this case the added iteration is harmless as it will just spin in backgroup
for ((i=0; i <= ssl_reneg_attempts; i++ )); do sleep $ssl_reneg_wait; echo R 2>/dev/null; k=0; \
# 0 means client is renegotiating & doesn't return an error --> vuln!
# 1 means client tried to renegotiating but the server side errored then. You still see RENEGOTIATING in the output
# Exemption from above: server closed the connection but return value was zero
# See https://github.com/drwetter/testssl.sh/issues/1725 and referenced issue @haproxy
while [[ $(grep -ac '^RENEGOTIATING' $ERRFILE) -ne $((i+1)) ]] && [[ -f $TEMPDIR/allowed_to_loop ]] \
&& [[ $(tail -1 $ERRFILE | grep -acE '^(RENEGOTIATING|depth|verify|notAfter)') -eq 1 ]] \
&& [[ $k -lt 120 ]]; \
do sleep $ssl_reneg_wait; ((k++)); if (tail -5 $TMPFILE| grep -qa '^closed'); then break; fi; done; \
done) | \
$OPENSSL_NOTIMEOUT s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE &
pid=$!
( sleep $((ssl_reneg_attempts*3+3)) && kill $pid && touch $TEMPDIR/was_killed ) >&2 2>/dev/null &
watcher=$!
# Trick to get the return value of the openssl command, output redirection and a timeout.
# Yes, some target hang/block after some tries (some LiteSpeed servers don't answer at all on "R" and block).
wait $pid
tmp_result=$?
pkill -HUP -P $watcher
wait $watcher
# Stop any background wait loop
rm -f $TEMPDIR/allowed_to_loop
# If we are here, we have done the loop. Count the effective renego attempts.
# It could be less than the numbers of "for" iterations (minus one) in case of late server close.
loop_reneg=$(grep -ac '^RENEGOTIATING' $ERRFILE)
# As above, some servers close the connection and return value is zero
if (tail -5 $TMPFILE | grep -qa '^closed'); then
tmp_result=1
fi
# timeout reached ?
if [[ -f $TEMPDIR/was_killed ]]; then
tmp_result=2
rm -f $TEMPDIR/was_killed
fi
if [[ $tmp_result -eq 1 ]] && [[ loop_reneg -eq 1 ]]; then
tmp_result=3
fi
if [[ $SERVICE != HTTP ]]; then
# theoretic possible case
if [[ $loop_reneg -eq 2 ]]; then
tmp_result=0
fi fi
case "$sec_client_renego" in case $tmp_result in
0) # We try again if server is HTTP. This could be either a node.js server or something else. 0) pr_svrty_medium "VULNERABLE (NOT ok)"; outln ", potential DoS threat"
# Mitigations (default values) for: fileout "$jsonID" "MEDIUM" "VULNERABLE, potential DoS threat" "$cve" "$cwe" "$hint"
# - node.js allows 3x R and then blocks. So then 4x should be tested. ;;
# - F5 BIG-IP ADS allows 5x R and then blocks. So then 6x should be tested. 1|3) prln_svrty_good "not vulnerable (OK)"
# - Stormshield allows 9x and then blocks. So then 10x should be tested. fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
# This way we save a couple seconds as we weeded out the ones which are more robust ;;
# Amount of times tested before breaking is set in SSL_RENEG_ATTEMPTS. 2) pr_svrty_good "likely not vulnerable (OK)"; outln ", timed out ($((${ssl_reneg_attempts}*3+3))s)" # it hung
if [[ $SERVICE != HTTP ]]; then fileout "$jsonID" "OK" "likely not vulnerable (timed out)" "$cve" "$cwe"
pr_svrty_medium "VULNERABLE (NOT ok)"; outln ", potential DoS threat" ;;
fileout "$jsonID" "MEDIUM" "VULNERABLE, potential DoS threat" "$cve" "$cwe" "$hint" *) prln_warning "FIXME (bug): $sec_client_renego"
else fileout "$jsonID" "DEBUG" "FIXME (bug) $sec_client_renego - Please report" "$cve" "$cwe"
# Clear the log to not get the content of previous run before the execution of the new one. ret=1
echo -n > $TMPFILE ;;
#RENEGOTIATING wait loop watchdog file esac
touch $TEMPDIR/allowed_to_loop else
# If we dont wait for the session to be established on slow server, we will try to re-negotiate case $tmp_result in
# too early losing all the attempts before the session establishment as OpenSSL will not buffer them 0) pr_svrty_high "VULNERABLE (NOT ok)"; outln ", DoS threat ($ssl_reneg_attempts attempts)"
# (only the first will be till the establishement of the session). fileout "$jsonID" "HIGH" "VULNERABLE, DoS threat" "$cve" "$cwe" "$hint"
(j=0; while [[ $(grep -ac '^SSL-Session:' $TMPFILE) -ne 1 ]] && [[ $j -lt 30 ]]; do sleep $ssl_reneg_wait; ((j++)); done; \ ;;
for ((i=0; i < ssl_reneg_attempts; i++ )); do sleep $ssl_reneg_wait; echo R; k=0; \ 1) pr_svrty_good "not vulnerable (OK)"; outln " -- mitigated (disconnect after $loop_reneg/$ssl_reneg_attempts attempts)"
while [[ $(grep -ac '^RENEGOTIATING' $ERRFILE) -ne $((i+3)) ]] && [[ -f $TEMPDIR/allowed_to_loop ]] \ fileout "$jsonID" "OK" "not vulnerable, mitigated" "$cve" "$cwe"
&& [[ $(tail -n1 $ERRFILE |grep -acE '^(RENEGOTIATING|depth|verify|notAfter)') -eq 1 ]] \ ;;
&& [[ $k -lt 120 ]]; \ 3) prln_svrty_good "not vulnerable (OK)"
do sleep $ssl_reneg_wait; ((k++)); if (tail -5 $TMPFILE| grep -qa '^closed'); then sleep 1; break; fi; done; \ fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
done) | \ ;;
$OPENSSL s_client $(s_client_options "$proto $legacycmd $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>>$ERRFILE & 2) pr_svrty_good "not vulnerable (OK)"; \
pid=$! outln " -- mitigated ($loop_reneg successful reneg within ${ssl_reneg_attempts} in $((${ssl_reneg_attempts}*3+3))s(timeout))"
( sleep $((ssl_reneg_attempts*3)) && kill $pid && touch $TEMPDIR/was_killed ) >&2 2>/dev/null & fileout "$jsonID" "OK" "not vulnerable, mitigated" "$cve" "$cwe"
watcher=$! ;;
# Trick to get the return value of the openssl command, output redirection and a timeout. *) prln_warning "FIXME (bug): $sec_client_renego ($ssl_reneg_attempts tries)"
# Yes, some target hang/block after some tries. fileout "$jsonID" "DEBUG" "FIXME (bug $ssl_reneg_attempts tries) $sec_client_renego" "$cve" "$cwe"
wait $pid ret=1
tmp_result=$? ;;
pkill -HUP -P $watcher
wait $watcher
rm -f $TEMPDIR/allowed_to_loop
# If we are here, we have done two successful renegotiation (-2) and do the loop
loop_reneg=$(($(grep -ac '^RENEGOTIATING' $ERRFILE)-2))
# As above, some servers close the connection and return value is zero
if (tail -5 $TMPFILE| grep -qa '^closed'); then
tmp_result=1
fi
if [[ -f $TEMPDIR/was_killed ]]; then
tmp_result=2
rm -f $TEMPDIR/was_killed
fi
case $tmp_result in
0) pr_svrty_high "VULNERABLE (NOT ok)"; outln ", DoS threat ($ssl_reneg_attempts attempts)"
fileout "$jsonID" "HIGH" "VULNERABLE, DoS threat" "$cve" "$cwe" "$hint"
;;
1) pr_svrty_good "not vulnerable (OK)"; outln " -- mitigated (disconnect after $loop_reneg/$ssl_reneg_attempts attempts)"
fileout "$jsonID" "OK" "not vulnerable, mitigated" "$cve" "$cwe"
;;
2) pr_svrty_good "not vulnerable (OK)"; \
outln " -- mitigated ($loop_reneg successful reneg within ${ssl_reneg_attempts} in $((${ssl_reneg_attempts}*3))s(timeout))"
fileout "$jsonID" "OK" "not vulnerable, mitigated" "$cve" "$cwe"
;;
*) prln_warning "FIXME (bug): $sec_client_renego ($ssl_reneg_attempts tries)"
fileout "$jsonID" "DEBUG" "FIXME (bug $ssl_reneg_attempts tries) $sec_client_renego" "$cve" "$cwe"
ret=1
;;
esac
fi
;;
1)
prln_svrty_good "not vulnerable (OK)"
fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
;;
*)
prln_warning "FIXME (bug): $sec_client_renego"
fileout "$jsonID" "DEBUG" "FIXME (bug) $sec_client_renego - Please report" "$cve" "$cwe"
ret=1
;;
esac esac
fi fi
fi fi
@ -20530,6 +20534,8 @@ find_openssl_binary() {
fi fi
fi fi
OPENSSL_NOTIMEOUT=$OPENSSL
if ! "$do_mass_testing"; then if ! "$do_mass_testing"; then
if [[ -n $OPENSSL_TIMEOUT ]]; then if [[ -n $OPENSSL_TIMEOUT ]]; then
OPENSSL="$TIMEOUT_CMD $OPENSSL_TIMEOUT $OPENSSL" OPENSSL="$TIMEOUT_CMD $OPENSSL_TIMEOUT $OPENSSL"