From cd7a8878789a8e0fc096af83e679074fc7acf692 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 18 Jul 2025 18:18:30 +0200 Subject: [PATCH 1/6] Fix port problem for Opossum This fixes #2847 . It was falsely assumed that the http head command blocks when port 80 is not available but actucally the exec for the socket is the culprit. This PR changes that so that the exec is put in the background. Another change is that $node is still used but the port is stripped of which lead to the problem raised in #2847. We use $node instead of $NODE has we can recycle the `http_head[er]_printf()` later. `http_header_printf()`was renamed to `http_head_printf()` as there's also an `http_head()` and an `http_get()` --- testssl.sh | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/testssl.sh b/testssl.sh index b4cf211..c4f9fe6 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1922,40 +1922,43 @@ http_head() { # arg2: extra http header # # return codes: -# 0: all fine -# 1: server dind't respond within HEADER_MAXSLEEP -# 3: server dind't respond within HEADER_MAXSLEEP and PROXY was defined +# 0: all fine (response header is returned as string) +# 1: server didn't respond within HEADER_MAXSLEEP +# 3: server didn't respond within HEADER_MAXSLEEP and PROXY was defined # -http_header_printf() { +http_head_printf() { local request_header="$2" local useragent="$UA_STD" - local tmpfile=$TEMPDIR/$NODE.$NODEIP.http_header_printf.log - local errfile=$TEMPDIR/$NODE.$NODEIP.http_header_printf-err.log + local tmpfile=$TEMPDIR/$NODE.$NODEIP.http_head_printf.log + local errfile=$TEMPDIR/$NODE.$NODEIP.http_head_printf-err.log local -i ret=0 local proto="" foo="" node="" query="" [[ $DEBUG -eq 0 ]] && errfile=/dev/null IFS=/ read -r proto foo node query <<< "$1" - exec 33<>/dev/tcp/$node/80 - printf -- "%b" "HEAD ${proto}//${node}/${query} HTTP/1.1\r\nUser-Agent: ${useragent}\r\nHost: ${node}\r\n${request_header}\r\nAccept: */*\r\n\r\n\r\n" >&33 2>$errfile & + node=${node%:*} + # $node works here good as it connects via IPv6 first, then IPv4 + bash -c "exec 33<>/dev/tcp/$node/80" >/dev/null & wait_kill $! $HEADER_MAXSLEEP if [[ $? -ne 0 ]]; then - # not killed + # not killed --> socket open. Now we connect to the virtual host "$node" + printf -- "%b" "HEAD ${proto}//${node}/${query} HTTP/1.1\r\nUser-Agent: ${useragent}\r\nHost: ${node}\r\n${request_header}\r\nAccept: */*\r\n\r\n\r\n" >&33 2>$errfile + ret=0 + if [[ $DEBUG -eq 0 ]] ; then + cat <&33 + else + cat <&33 >$tmpfile + cat $tmpfile + fi + else if [[ -n "$PROXY" ]]; then ret=3 + else + ret=1 fi - ret=1 - else - ret=0 fi - if [[ $DEBUG -eq 0 ]] ; then - cat <&33 - else - cat <&33 >$tmpfile - cat $tmpfile - fi - exec 33<&- + exec 33<&- exec 33>&- return $ret } @@ -1963,9 +1966,6 @@ http_header_printf() { ldap_get() { local ldif - local -i success - local crl="$1" - local tmpfile="$2" local jsonID="$3" if type -p curl &>/dev/null; then @@ -17703,7 +17703,7 @@ run_opossum() { case $service in HTTP) uri=${URI/https:\/\//} - response=$(http_header_printf http://${uri} 'Upgrade: TLS/1.0\r\n\r\nClose\r\n') + response=$(http_head_printf http://${uri} 'Upgrade: TLS/1.0\r\n\r\nClose\r\n') # In any case we use $response but we handle the return codes case $? in 0) ret=0 ;; From 9743a9646263a92b2dded123953d669c46efce99 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 18 Jul 2025 18:30:08 +0200 Subject: [PATCH 2/6] fix indentation --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index c4f9fe6..455b892 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1958,7 +1958,7 @@ http_head_printf() { ret=1 fi fi - exec 33<&- + exec 33<&- exec 33>&- return $ret } From 9e29b35e9e0352d6dd40eea78a90b38b60336fa6 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 18 Jul 2025 19:06:42 +0200 Subject: [PATCH 3/6] open socket error message, not stdout --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 455b892..d903c55 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1939,7 +1939,7 @@ http_head_printf() { IFS=/ read -r proto foo node query <<< "$1" node=${node%:*} # $node works here good as it connects via IPv6 first, then IPv4 - bash -c "exec 33<>/dev/tcp/$node/80" >/dev/null & + bash -c "exec 33<>/dev/tcp/$node/80" 2>/dev/null & wait_kill $! $HEADER_MAXSLEEP if [[ $? -ne 0 ]]; then # not killed --> socket open. Now we connect to the virtual host "$node" From 0d63a56c80b529395eaabc372ca061494bc44c2c Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 18 Jul 2025 20:17:12 +0200 Subject: [PATCH 4/6] Fix logic error and mind the sub shell (fd) --- testssl.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index d903c55..e8bb7bf 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1938,10 +1938,12 @@ http_head_printf() { IFS=/ read -r proto foo node query <<< "$1" node=${node%:*} - # $node works here good as it connects via IPv6 first, then IPv4 - bash -c "exec 33<>/dev/tcp/$node/80" 2>/dev/null & + # $node works here good as it connects via IPv6 first, then IPv4. + # This is a subshell, so fd 8 is not inherited + bash -c "exec 8<>/dev/tcp/$node/80" 2>/dev/null & wait_kill $! $HEADER_MAXSLEEP - if [[ $? -ne 0 ]]; then + if [[ $? -eq 0 ]]; then + exec 33<>/dev/tcp/$node/80 # not killed --> socket open. Now we connect to the virtual host "$node" printf -- "%b" "HEAD ${proto}//${node}/${query} HTTP/1.1\r\nUser-Agent: ${useragent}\r\nHost: ${node}\r\n${request_header}\r\nAccept: */*\r\n\r\n\r\n" >&33 2>$errfile ret=0 From ea3cc3789f107892dfb5171070ed8bb9d7ae070e Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 19 Jul 2025 13:40:03 +0200 Subject: [PATCH 5/6] handle UI output better when conn to port 80 failed --- testssl.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index e8bb7bf..1468996 100755 --- a/testssl.sh +++ b/testssl.sh @@ -17707,16 +17707,22 @@ run_opossum() { uri=${URI/https:\/\//} response=$(http_head_printf http://${uri} 'Upgrade: TLS/1.0\r\n\r\nClose\r\n') # In any case we use $response but we handle the return codes - case $? in - 0) ret=0 ;; - 1|3) ret=7 ;; # got stuck - esac + # 0: connection was fine, 1 or 3: no http connection + ret=$? if [[ $response =~ Upgrade:\ TLS ]]; then prln_svrty_high "VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" - else + elif [[ $ret -eq 0 ]]; then prln_svrty_good "not vulnerable (OK)" - fileout "$jsonID" "OK" "not vulnerable $append" "$cve" "$cwe" + fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe" + else + if [[ $ret -eq 3 ]]; then + prln_local_problem "direct connection to port 80 failed, better try without proxy" + fileout "$jsonID" "WARN" "direct connection to port 80 failed, try w/o no proxy" "$cve" "$cwe" + else + outln "connection to port 80 failed" + fileout "$jsonID" "INFO" "connection to port 80 failed" "$cve" "$cwe" + fi fi ;; IMAP|FTP|POP3|SMTP|LMTP|NNTP) From e09d79aad910550d54aaeb7a4e02f06d5563a99b Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Sun, 20 Jul 2025 15:40:35 +0200 Subject: [PATCH 6/6] Fix error message on UI when testing Opossum As `wait_kill()` returns with 0 when a TCP reset is encountered and the process is not killed, we need to open the socket again in a sub shell. Which is safe in the foreground. If then the subshell returns with 0 we can safely connect to port 80. --- testssl.sh | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/testssl.sh b/testssl.sh index 1468996..89efaa2 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1942,26 +1942,31 @@ http_head_printf() { # This is a subshell, so fd 8 is not inherited bash -c "exec 8<>/dev/tcp/$node/80" 2>/dev/null & wait_kill $! $HEADER_MAXSLEEP - if [[ $? -eq 0 ]]; then - exec 33<>/dev/tcp/$node/80 - # not killed --> socket open. Now we connect to the virtual host "$node" - printf -- "%b" "HEAD ${proto}//${node}/${query} HTTP/1.1\r\nUser-Agent: ${useragent}\r\nHost: ${node}\r\n${request_header}\r\nAccept: */*\r\n\r\n\r\n" >&33 2>$errfile - ret=0 - if [[ $DEBUG -eq 0 ]] ; then - cat <&33 + if [[ $? -ne 3 ]]; then + # process with pid !$ wasn't killed but was that a reject? So we try again + # to make sure there wasn't a TCP reset + bash -c "exec 8<>/dev/tcp/$node/80" 2>/dev/null + if [[ $? -eq 0 ]]; then + exec 33<>/dev/tcp/$node/80 + # not killed --> socket open. Now we connect to the virtual host "$node" + printf -- "%b" "HEAD ${proto}//${node}/${query} HTTP/1.1\r\nUser-Agent: ${useragent}\r\nHost: ${node}\r\n${request_header}\r\nAccept: */*\r\n\r\n\r\n" >&33 2>$errfile + ret=0 + if [[ $DEBUG -eq 0 ]] ; then + cat <&33 + else + cat <&33 >$tmpfile + cat $tmpfile + fi else - cat <&33 >$tmpfile - cat $tmpfile - fi - else - if [[ -n "$PROXY" ]]; then - ret=3 - else - ret=1 + if [[ -n "$PROXY" ]]; then + ret=3 + else + ret=1 + fi fi + exec 33<&- + exec 33>&- fi - exec 33<&- - exec 33>&- return $ret }