mirror of
				https://github.com/drwetter/testssl.sh.git
				synced 2025-10-31 13:55:25 +01:00 
			
		
		
		
	Merge pull request #2842 from testssl/opossum
Redo PR for Opossum , see #2838
This commit is contained in:
		| @@ -5,6 +5,7 @@ | |||||||
|  |  | ||||||
| * QUIC protocol check | * QUIC protocol check | ||||||
| * bump SSLlabs rating guide to 2009r | * bump SSLlabs rating guide to 2009r | ||||||
|  | * Check for Opossum vulnerability | ||||||
|  |  | ||||||
| ### Features implemented / improvements in 3.2 | ### Features implemented / improvements in 3.2 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -659,6 +659,9 @@ variable \f[CR]CCS_MAX_WAITSOCK\f[R]. | |||||||
| \f[CR]\-T, \-\-ticketbleed\f[R] Checks for Ticketbleed memory leakage in | \f[CR]\-T, \-\-ticketbleed\f[R] Checks for Ticketbleed memory leakage in | ||||||
| BigIP loadbalancers. | BigIP loadbalancers. | ||||||
| .PP | .PP | ||||||
|  | \f[CR]\-\-OP, \-\-opossum\f[R] Checks for HTTP to HTTPS upgrade | ||||||
|  | vulnerability named Opossum. | ||||||
|  | .PP | ||||||
| \f[CR]\-\-BB, \-\-robot\f[R] Checks for vulnerability to ROBOT / | \f[CR]\-\-BB, \-\-robot\f[R] Checks for vulnerability to ROBOT / | ||||||
| (\f[I]Return Of Bleichenbacher\[cq]s Oracle Threat\f[R]) attack. | (\f[I]Return Of Bleichenbacher\[cq]s Oracle Threat\f[R]) attack. | ||||||
| .PP | .PP | ||||||
| @@ -1312,6 +1315,8 @@ RFC 2246: The TLS Protocol Version 1.0 | |||||||
| .IP \[bu] 2 | .IP \[bu] 2 | ||||||
| RFC 2595: Using TLS with IMAP, POP3 and ACAP | RFC 2595: Using TLS with IMAP, POP3 and ACAP | ||||||
| .IP \[bu] 2 | .IP \[bu] 2 | ||||||
|  | RFC 2817: Upgrading to TLS Within HTTP/1.1 | ||||||
|  | .IP \[bu] 2 | ||||||
| RFC 2818: HTTP Over TLS | RFC 2818: HTTP Over TLS | ||||||
| .IP \[bu] 2 | .IP \[bu] 2 | ||||||
| RFC 2830: Lightweight Directory Access Protocol (v3): Extension for | RFC 2830: Lightweight Directory Access Protocol (v3): Extension for | ||||||
|   | |||||||
| @@ -590,6 +590,8 @@ | |||||||
|         <code>CCS_MAX_WAITSOCK</code>.</p> |         <code>CCS_MAX_WAITSOCK</code>.</p> | ||||||
|         <p><code>-T, --ticketbleed</code> Checks for Ticketbleed memory |         <p><code>-T, --ticketbleed</code> Checks for Ticketbleed memory | ||||||
|         leakage in BigIP loadbalancers.</p> |         leakage in BigIP loadbalancers.</p> | ||||||
|  |         <p><code>--OP, --opossum</code> Checks for HTTP to HTTPS upgrade | ||||||
|  |         vulnerability named Opossum.</p> | ||||||
|         <p><code>--BB, --robot</code> Checks for vulnerability to ROBOT |         <p><code>--BB, --robot</code> Checks for vulnerability to ROBOT | ||||||
|         / (<em>Return Of Bleichenbacher’s Oracle Threat</em>) |         / (<em>Return Of Bleichenbacher’s Oracle Threat</em>) | ||||||
|         attack.</p> |         attack.</p> | ||||||
| @@ -1131,6 +1133,7 @@ | |||||||
|         <ul> |         <ul> | ||||||
|         <li>RFC 2246: The TLS Protocol Version 1.0</li> |         <li>RFC 2246: The TLS Protocol Version 1.0</li> | ||||||
|         <li>RFC 2595: Using TLS with IMAP, POP3 and ACAP</li> |         <li>RFC 2595: Using TLS with IMAP, POP3 and ACAP</li> | ||||||
|  |         <li>RFC 2817: Upgrading to TLS Within HTTP/1.1</li> | ||||||
|         <li>RFC 2818: HTTP Over TLS</li> |         <li>RFC 2818: HTTP Over TLS</li> | ||||||
|         <li>RFC 2830: Lightweight Directory Access Protocol (v3): |         <li>RFC 2830: Lightweight Directory Access Protocol (v3): | ||||||
|         Extension for Transport Layer Security</li> |         Extension for Transport Layer Security</li> | ||||||
|   | |||||||
| @@ -236,9 +236,11 @@ Also for multiple server certificates are being checked for as well as for the c | |||||||
|  |  | ||||||
| `-T, --ticketbleed`             Checks for Ticketbleed memory leakage in BigIP loadbalancers. | `-T, --ticketbleed`             Checks for Ticketbleed memory leakage in BigIP loadbalancers. | ||||||
|  |  | ||||||
| `--BB, --robot`          Checks for vulnerability to ROBOT / (*Return Of Bleichenbacher's Oracle Threat*) attack. | `--OP, --opossum`               Checks for HTTP to HTTPS upgrade vulnerability named Opossum. | ||||||
|  |  | ||||||
| `--SI, --starttls-injection`          Checks for STARTTLS injection vulnerabilities (SMTP, IMAP, POP3 only). `socat` and OpenSSL >=1.1.0 is needed. | `--BB, --robot`                 Checks for vulnerability to ROBOT / (*Return Of Bleichenbacher's Oracle Threat*) attack. | ||||||
|  |  | ||||||
|  | `--SI, --starttls-injection`    Checks for STARTTLS injection vulnerabilities (SMTP, IMAP, POP3 only). `socat` and OpenSSL >=1.1.0 is needed. | ||||||
|  |  | ||||||
| `-R, --renegotiation`           Tests renegotiation vulnerabilities. Currently there's a check for *Secure Renegotiation* and for *Secure Client-Initiated Renegotiation*. Please be aware that vulnerable servers to the latter can likely be DoSed very easily (HTTP). A check for *Insecure Client-Initiated Renegotiation* is not yet implemented. | `-R, --renegotiation`           Tests renegotiation vulnerabilities. Currently there's a check for *Secure Renegotiation* and for *Secure Client-Initiated Renegotiation*. Please be aware that vulnerable servers to the latter can likely be DoSed very easily (HTTP). A check for *Insecure Client-Initiated Renegotiation* is not yet implemented. | ||||||
|  |  | ||||||
| @@ -490,6 +492,7 @@ Please note that for plain TLS-encrypted ports you must not specify the protocol | |||||||
|  |  | ||||||
| * RFC 2246: The TLS Protocol Version 1.0 | * RFC 2246: The TLS Protocol Version 1.0 | ||||||
| * RFC 2595: Using TLS with IMAP, POP3 and ACAP | * RFC 2595: Using TLS with IMAP, POP3 and ACAP | ||||||
|  | * RFC 2817: Upgrading to TLS Within HTTP/1.1 | ||||||
| * RFC 2818: HTTP Over TLS | * RFC 2818: HTTP Over TLS | ||||||
| * RFC 2830: Lightweight Directory Access Protocol (v3): Extension for Transport Layer Security | * RFC 2830: Lightweight Directory Access Protocol (v3): Extension for Transport Layer Security | ||||||
| * RFC 3207: SMTP Service Extension for Secure SMTP over Transport Layer Security | * RFC 3207: SMTP Service Extension for Secure SMTP over Transport Layer Security | ||||||
| @@ -551,7 +554,6 @@ Please note that for plain TLS-encrypted ports you must not specify the protocol | |||||||
|  |  | ||||||
| **etc/client-simulation.txt**  contains client simulation data. | **etc/client-simulation.txt**  contains client simulation data. | ||||||
|  |  | ||||||
|  |  | ||||||
| **etc/cipher-mapping.txt**  provides a mandatory file with mapping from OpenSSL cipher suites names to the ones from IANA / used in the RFCs. | **etc/cipher-mapping.txt**  provides a mandatory file with mapping from OpenSSL cipher suites names to the ones from IANA / used in the RFCs. | ||||||
|  |  | ||||||
| **etc/tls_data.txt**        provides a mandatory file for ciphers (bash sockets) and key material. | **etc/tls_data.txt**        provides a mandatory file for ciphers (bash sockets) and key material. | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ $edited_html =~ s/'/'/g; | |||||||
|  |  | ||||||
| $diff = diff \$edited_html, \$out; | $diff = diff \$edited_html, \$out; | ||||||
|  |  | ||||||
| cmp_ok($edited_html, "eq", $out, "Checking if HTML file matches terminal output") or | ok($edited_html eq $out, "Checking if HTML file matches terminal output") or | ||||||
|      diag ("\n%s\n", "$diff"); |      diag ("\n%s\n", "$diff"); | ||||||
|  |  | ||||||
| $tests++; | $tests++; | ||||||
| @@ -82,7 +82,7 @@ $debughtml =~ s/.*Using bash .*\n//g; | |||||||
|  |  | ||||||
| $diff = diff \$debughtml, \$html; | $diff = diff \$debughtml, \$html; | ||||||
|  |  | ||||||
| cmp_ok($debughtml, "eq", $html, "Checking if HTML file created with --debug 4 matches HTML file created without --debug") or | ok($debughtml eq $html, "Checking if HTML file created with --debug 4 matches HTML file created without --debug") or | ||||||
|      diag ("\n%s\n", "$diff"); |      diag ("\n%s\n", "$diff"); | ||||||
| $tests++; | $tests++; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -90,6 +90,7 @@ | |||||||
| "heartbleed","testssl.sh/81.169.166.184","443","OK","not vulnerable, no heartbeat extension","CVE-2014-0160","CWE-119" | "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" | "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" | "ticketbleed","testssl.sh/81.169.166.184","443","OK","no session ticket extension","CVE-2016-9244","CWE-200" | ||||||
|  | "opossum","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2025-49812","CWE-287" | ||||||
| "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" | "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_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" | "secure_client_renego","testssl.sh/81.169.166.184","443","OK","not vulnerable","CVE-2011-1473","CWE-310" | ||||||
|   | |||||||
							
								
								
									
										185
									
								
								testssl.sh
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								testssl.sh
									
									
									
									
									
								
							| @@ -1786,12 +1786,13 @@ filter_input() { | |||||||
|      sed -e 's/#.*$//' -e '/^$/d' <<< "$1" | tr -d '\n' | tr -d '\t' | tr -d '\r' |      sed -e 's/#.*$//' -e '/^$/d' <<< "$1" | tr -d '\n' | tr -d '\t' | tr -d '\r' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # Dl's any URL (arg1) via HTTP 1.1 GET from port 80, arg2: file to store http body. | # Dl any URL (arg1) via HTTP 1.1 GET from port 80 or 443 (curl/wget). arg2: file to store http body. | ||||||
| # Proxy is not honored yet (see cmd line switches) -- except when using curl or wget. | # Proxy is not honored yet (see cmd line switches) -- except when using curl or wget. | ||||||
| # There the environment variable is used automatically | # The PROXY environment variable is used when specified | ||||||
| # Currently it is being used by check_revocation_crl() only. | # Currently this is being used by check_revocation_crl() only. | ||||||
|  | # | ||||||
| http_get() { | http_get() { | ||||||
|      local proto z |      local proto="" foo="" | ||||||
|      local node="" query="" |      local node="" query="" | ||||||
|      local dl="$2" |      local dl="$2" | ||||||
|      local useragent="$UA_STD" |      local useragent="$UA_STD" | ||||||
| @@ -1825,7 +1826,7 @@ http_get() { | |||||||
|           # Worst option: slower and hiccups with chunked transfers. Workaround for the |           # Worst option: slower and hiccups with chunked transfers. Workaround for the | ||||||
|           # latter is using HTTP/1.0. We do not support https here, yet. |           # latter is using HTTP/1.0. We do not support https here, yet. | ||||||
|           # First the URL will be split |           # First the URL will be split | ||||||
|           IFS=/ read -r proto z node query <<< "$1" |           IFS=/ read -r proto foo node query <<< "$1" | ||||||
|           proto=${proto%:} |           proto=${proto%:} | ||||||
|           if [[ "$proto" != http ]]; then |           if [[ "$proto" != http ]]; then | ||||||
|                pr_warning "protocol $proto not supported yet" |                pr_warning "protocol $proto not supported yet" | ||||||
| @@ -1844,7 +1845,7 @@ http_get() { | |||||||
|                     printf -- "%b" "GET $proto://$node/$query HTTP/1.0\r\nUser-Agent: $useragent\r\nHost: $node\r\nAccept: */*\r\n\r\n" >&33 |                     printf -- "%b" "GET $proto://$node/$query HTTP/1.0\r\nUser-Agent: $useragent\r\nHost: $node\r\nAccept: */*\r\n\r\n" >&33 | ||||||
|                fi |                fi | ||||||
|           else |           else | ||||||
|                IFS=/ read -r proto z node query <<< "$1" |                IFS=/ read -r proto foo node query <<< "$1" | ||||||
|                exec 33<>/dev/tcp/$node/80 |                exec 33<>/dev/tcp/$node/80 | ||||||
|                printf -- "%b" "GET /$query HTTP/1.0\r\nUser-Agent: $useragent\r\nHost: $node\r\nAccept: */*\r\n\r\n" >&33 |                printf -- "%b" "GET /$query HTTP/1.0\r\nUser-Agent: $useragent\r\nHost: $node\r\nAccept: */*\r\n\r\n" >&33 | ||||||
|           fi |           fi | ||||||
| @@ -1861,55 +1862,105 @@ http_get() { | |||||||
|      fi |      fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # Outputs the headers when downloading any URL (arg1) via HTTP 1.1 GET from port 80. | # Outputs the HTTP headers via HTTP 1.1 HEAD command via HTTPS and a valid certificate | ||||||
|  | #    arg1 is the URL | ||||||
|  | #    arg2 is optional and could be a request header. curl/wget don't send empty headers otherwise | ||||||
|  | # | ||||||
| # Only works if curl or wget is available. | # Only works if curl or wget is available. | ||||||
| # There the environment variable is used automatically | # The proxy environment variable is used automatically. | ||||||
| # Currently it is being used by check_pwnedkeys() only. | # Currently it is being used by check_pwnedkeys() only | ||||||
| http_get_header() { | # | ||||||
|  | http_head() { | ||||||
|      local proto |      local proto | ||||||
|      local node="" query="" |      local node="" query="" | ||||||
|      local dl="$2" |      local request_header="$2" | ||||||
|      local useragent="$UA_STD" |      local useragent="$UA_STD" | ||||||
|      local jsonID="http_get_header" |      local response_headers="" | ||||||
|      local headers |      local xtra_params="" | ||||||
|      local -i ret |      local -i ret | ||||||
| 
 | 
 | ||||||
|      "$SNEAKY" && useragent="$UA_SNEAKY" |      "$SNEAKY" && useragent="$UA_SNEAKY" | ||||||
| 
 | 
 | ||||||
|      if type -p curl &>/dev/null; then |      if type -p curl &>/dev/null; then | ||||||
|  |           xtra_params="--connect-timeout $HEADER_MAXSLEEP --head -s" | ||||||
|           if [[ -z "$PROXY" ]]; then |           if [[ -z "$PROXY" ]]; then | ||||||
|                headers="$(curl --head -s  --noproxy '*' -A $''"$useragent"'' "$1")" |                response_headers="$(curl $xtra_params --noproxy '*' -H $''"$request_header"'' -A $''"$useragent"'' "$1")" | ||||||
|           else |           else | ||||||
|                # for the sake of simplicity assume the proxy is using http |                # for the sake of simplicity assume the proxy is using http | ||||||
|                headers="$(curl --head -s -x $PROXYIP:$PROXYPORT -A $''"$useragent"'' "$1")" |                response_headers="$(curl $xtra_params -x $PROXYIP:$PROXYPORT -H $''"$request_header"'' -A $''"$useragent"'' "$1")" | ||||||
|           fi |           fi | ||||||
|           ret=$? |           ret=$? | ||||||
|           [[ $ret -eq 0 ]] && tm_out "$headers" |           [[ $ret -eq 0 ]] && tm_out "$response_headers" | ||||||
|           return $ret |           return $ret | ||||||
|      elif type -p wget &>/dev/null; then |      elif type -p wget &>/dev/null; then | ||||||
|  |           xtra_params="--timeout=$HEADER_MAXSLEEP --tries=1 --cache=off" | ||||||
|           # wget has no proxy command line. We need to use http_proxy instead. And for the sake of simplicity |           # wget has no proxy command line. We need to use http_proxy instead. And for the sake of simplicity | ||||||
|           # assume the GET protocol we query is using http -- http_proxy is the $ENV not for the connection TO |           # assume the GET protocol we query is using http -- http_proxy is the $ENV not for the connection TO | ||||||
|           # the proxy, but for the protocol we query THROUGH the proxy |           # the proxy, but for the protocol we query THROUGH the proxy | ||||||
|           if [[ -z "$PROXY" ]]; then |           if [[ -z "$PROXY" ]]; then | ||||||
|                headers="$(wget --no-proxy -q -S -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" |                response_headers="$(wget --no-proxy -q -S $xtra_params --header $''"$request_header"'' -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" | ||||||
|           else |           else | ||||||
|                if [[ -z "$http_proxy" ]]; then |                if [[ -z "$http_proxy" ]]; then | ||||||
|                     headers="$(http_proxy=http://$PROXYIP:$PROXYPORT wget -q -S  -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" |                     response_headers="$(http_proxy=http://$PROXYIP:$PROXYPORT wget -q -S $xtra_params --header $''"$request_header"'' -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" | ||||||
|                else |                else | ||||||
|                     headers="$(wget -q -S -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" |                     response_headers="$(wget -q -S $xtra_params --header $''"$request_header"'' -U $''"$useragent"'' -O /dev/null "$1" 2>&1)" | ||||||
|                fi |                fi | ||||||
|           fi |           fi | ||||||
|           ret=$? |           ret=$? | ||||||
|           [[ $ret -eq 0 ]] && tm_out "$headers" |           [[ $ret -eq 0 ]] && tm_out "$response_headers" | ||||||
|           # wget(1): "8: Server issued an error response.". Happens e.g. when 404 is returned. However also if the call wasn't correct (400) |           # wget(1): "8: Server issued an error response.". Happens e.g. when 404 is returned. However also if the call wasn't correct (400) | ||||||
|           # So we assume for now that everything is submitted correctly. We parse the error code too later |           # So we assume for now that everything is submitted correctly. We parse the error code too later | ||||||
|           [[ $ret -eq 8 ]] && ret=0 && tm_out "$headers" |           [[ $ret -eq 8 ]] && ret=0 && tm_out "$response_headers" | ||||||
|           return $ret |           return $ret | ||||||
|      else |      else | ||||||
|           return 1 |           return 1 | ||||||
|      fi |      fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | # does a simple http head via printf with no proxy, only used by run_opossum() | ||||||
|  | #    arg1: URL | ||||||
|  | #    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 | ||||||
|  | # | ||||||
|  | http_header_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 -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 & | ||||||
|  |      wait_kill $! $HEADER_MAXSLEEP | ||||||
|  |      if [[ $? -ne 0 ]]; then | ||||||
|  |           # not killed | ||||||
|  |           if [[ -n "$PROXY" ]]; then | ||||||
|  |                ret=3 | ||||||
|  |           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>&- | ||||||
|  |      return $ret | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ldap_get() { | ldap_get() { | ||||||
|      local ldif |      local ldif | ||||||
|      local -i success |      local -i success | ||||||
| @@ -1940,6 +1991,7 @@ ldap_get() { | |||||||
| #     1 - key not found in database | #     1 - key not found in database | ||||||
| #     2 - key found in database | #     2 - key found in database | ||||||
| #     7 - network/proxy failure | #     7 - network/proxy failure | ||||||
|  | # | ||||||
| check_pwnedkeys() { | check_pwnedkeys() { | ||||||
|      local cert="$1" |      local cert="$1" | ||||||
|      local cert_key_algo="$2" |      local cert_key_algo="$2" | ||||||
| @@ -1969,7 +2021,7 @@ check_pwnedkeys() { | |||||||
|      fi |      fi | ||||||
|      fingerprint="$($OPENSSL pkey -pubin -outform DER <<< "$pubkey" 2>/dev/null | $OPENSSL dgst -sha256 -hex 2>/dev/null)" |      fingerprint="$($OPENSSL pkey -pubin -outform DER <<< "$pubkey" 2>/dev/null | $OPENSSL dgst -sha256 -hex 2>/dev/null)" | ||||||
|      fingerprint="${fingerprint#*= }" |      fingerprint="${fingerprint#*= }" | ||||||
|      response="$(http_get_header "https://v1.pwnedkeys.com/$fingerprint")" |      response="$(http_head "https://v1.pwnedkeys.com/$fingerprint")" | ||||||
|      # Handle curl's/wget's connectivity exit codes |      # Handle curl's/wget's connectivity exit codes | ||||||
|      case $? in |      case $? in | ||||||
|           4|5|7)     return 7 ;; |           4|5|7)     return 7 ;; | ||||||
| @@ -9927,7 +9979,7 @@ certificate_info() { | |||||||
|           check_pwnedkeys "$HOSTCERT" "$cert_key_algo" "$cert_keysize" |           check_pwnedkeys "$HOSTCERT" "$cert_key_algo" "$cert_keysize" | ||||||
|           case "$?" in |           case "$?" in | ||||||
|                0) outln "not checked"; fileout "pwnedkeys${json_postfix}" "INFO" "not checked" ;; |                0) outln "not checked"; fileout "pwnedkeys${json_postfix}" "INFO" "not checked" ;; | ||||||
|                1) outln "not in database"; fileout "pwnedkeys${json_postfix}" "INFO" "not in database" ;; |                1) pr_svrty_good "not in database"; fileout "pwnedkeys${json_postfix}" "OK" "not in database" ;; | ||||||
|                2) pr_svrty_critical "NOT ok --"; outln " key appears in database"; fileout "pwnedkeys${json_postfix}" "CRITICAL" "private key is known" ;; |                2) pr_svrty_critical "NOT ok --"; outln " key appears in database"; fileout "pwnedkeys${json_postfix}" "CRITICAL" "private key is known" ;; | ||||||
|                7) prln_warning "error querying https://v1.pwnedkeys.com"; fileout "pwnedkeys${json_postfix}" "WARN" "connection error" ;; |                7) prln_warning "error querying https://v1.pwnedkeys.com"; fileout "pwnedkeys${json_postfix}" "WARN" "connection error" ;; | ||||||
|           esac |           esac | ||||||
| @@ -12212,6 +12264,7 @@ code2network() { | |||||||
| # sockets inspired by https://blog.chris007.de/using-bash-for-network-socket-operation/ | # sockets inspired by https://blog.chris007.de/using-bash-for-network-socket-operation/ | ||||||
| # ARG1: hexbytes separated by commas, with a leading comma | # ARG1: hexbytes separated by commas, with a leading comma | ||||||
| # ARG2: seconds to sleep | # ARG2: seconds to sleep | ||||||
|  | # | ||||||
| socksend_clienthello() { | socksend_clienthello() { | ||||||
|      local data="" |      local data="" | ||||||
| 
 | 
 | ||||||
| @@ -12230,6 +12283,7 @@ socksend_clienthello() { | |||||||
| 
 | 
 | ||||||
| # ARG1: hexbytes -- preceded by x -- separated by commas, with a leading comma | # ARG1: hexbytes -- preceded by x -- separated by commas, with a leading comma | ||||||
| # ARG2: seconds to sleep | # ARG2: seconds to sleep | ||||||
|  | # | ||||||
| socksend() { | socksend() { | ||||||
|      local data line |      local data line | ||||||
| 
 | 
 | ||||||
| @@ -17339,6 +17393,7 @@ run_ccs_injection(){ | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # see https://blog.filippo.io/finding-ticketbleed/ |  https://filippo.io/ticketbleed/ | # see https://blog.filippo.io/finding-ticketbleed/ |  https://filippo.io/ticketbleed/ | ||||||
|  | # | ||||||
| run_ticketbleed() { | run_ticketbleed() { | ||||||
|      local tls_hexcode tls_proto="" |      local tls_hexcode tls_proto="" | ||||||
|      local sessticket_tls="" session_tckt_tls="" |      local sessticket_tls="" session_tckt_tls="" | ||||||
| @@ -17363,7 +17418,7 @@ run_ticketbleed() { | |||||||
|      pr_bold " Ticketbleed"; out " ($cve), experiment.  " |      pr_bold " Ticketbleed"; out " ($cve), experiment.  " | ||||||
| 
 | 
 | ||||||
|      if [[ "$SERVICE" != HTTP ]] && [[ "$CLIENT_AUTH" != required ]]; then |      if [[ "$SERVICE" != HTTP ]] && [[ "$CLIENT_AUTH" != required ]]; then | ||||||
|           outln "(applicable only for HTTPS)" |           outln "(applicable only for HTTP service)" | ||||||
|           fileout "$jsonID" "INFO" "not applicable, not HTTP" "$cve" "$cwe" |           fileout "$jsonID" "INFO" "not applicable, not HTTP" "$cve" "$cwe" | ||||||
|           return 0 |           return 0 | ||||||
|      fi |      fi | ||||||
| @@ -17625,6 +17680,55 @@ run_ticketbleed() { | |||||||
|      return $ret |      return $ret | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | # https://opossum-attack.com/, TLS Upgrade via old RFC 2817 | ||||||
|  | # | ||||||
|  | run_opossum() { | ||||||
|  |      local cve='CVE-2025-49812' | ||||||
|  |      local jsonID="opossum" | ||||||
|  |      local cwe="CWE-287" | ||||||
|  |      local -i ret=0 | ||||||
|  |      local uri=$URI | ||||||
|  |      local service="$SERVICE" | ||||||
|  |      local response="" | ||||||
|  | 
 | ||||||
|  |      [[ -n "$STARTTLS" ]] && return 0 | ||||||
|  |      [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for Opossum vulnerability " && outln | ||||||
|  |      pr_bold " Opossum"; out " ($cve)                  " | ||||||
|  | 
 | ||||||
|  |      # we're trying to connect also if ASSUME_HTTP is not set, there should be either one of following hints though | ||||||
|  |      if [[ -z $service ]]; then | ||||||
|  |           [[ $uri =~ ^http ]] && service=HTTP                    # https provided as target/URL | ||||||
|  |           [[ "$CLIENT_AUTH" == required ]] && service=HTTP       # also try when client auth is requested (we dont use it over cleartext) | ||||||
|  |      fi | ||||||
|  |      case $service in | ||||||
|  |           HTTP) | ||||||
|  |                uri=${URI/https:\/\//} | ||||||
|  |                response=$(http_header_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 | ||||||
|  |                if [[ $response =~ Upgrade:\ TLS ]]; then | ||||||
|  |                     prln_svrty_high "VULNERABLE (NOT ok)" | ||||||
|  |                     fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" | ||||||
|  |                else | ||||||
|  |                     prln_svrty_good "not vulnerable (OK)" | ||||||
|  |                     fileout "$jsonID" "OK" "not vulnerable $append" "$cve" "$cwe" | ||||||
|  |                fi | ||||||
|  |           ;; | ||||||
|  |           IMAP|FTP|POP3|SMTP|LMTP|NNTP) | ||||||
|  |                outln "(implemented currently for HTTP only)" | ||||||
|  |                fileout "$jsonID" "INFO" "not yet implemented" "$cve" "$cwe" | ||||||
|  |                ;; | ||||||
|  |           *)   outln "(applicable only for HTTP service)" | ||||||
|  |                fileout "$jsonID" "INFO" "not applicable, not HTTP" "$cve" "$cwe" | ||||||
|  |                ;; | ||||||
|  |      esac | ||||||
|  |      return $ret | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Overview @ http://www.exploresecurity.com/wp-content/uploads/custom/SSL_manual_cheatsheet.html | # Overview @ http://www.exploresecurity.com/wp-content/uploads/custom/SSL_manual_cheatsheet.html | ||||||
| # | # | ||||||
| run_renego() { | run_renego() { | ||||||
| @@ -21257,6 +21361,7 @@ single check as <options>  ("$PROG_NAME URI" does everything except -E and -g): | |||||||
|      -H, --heartbleed              tests for Heartbleed vulnerability |      -H, --heartbleed              tests for Heartbleed vulnerability | ||||||
|      -I, --ccs, --ccs-injection    tests for CCS injection vulnerability |      -I, --ccs, --ccs-injection    tests for CCS injection vulnerability | ||||||
|      -T, --ticketbleed             tests for Ticketbleed vulnerability in BigIP loadbalancers |      -T, --ticketbleed             tests for Ticketbleed vulnerability in BigIP loadbalancers | ||||||
|  |      --OP, --opossum               tests for Opossum vulnerability | ||||||
|      --BB, --robot                 tests for Return of Bleichenbacher's Oracle Threat (ROBOT) vulnerability |      --BB, --robot                 tests for Return of Bleichenbacher's Oracle Threat (ROBOT) vulnerability | ||||||
|      --SI, --starttls-injection    tests for STARTTLS injection issues |      --SI, --starttls-injection    tests for STARTTLS injection issues | ||||||
|      -R, --renegotiation           tests for renegotiation vulnerabilities |      -R, --renegotiation           tests for renegotiation vulnerabilities | ||||||
| @@ -23980,6 +24085,7 @@ initialize_globals() { | |||||||
|      do_breach=false |      do_breach=false | ||||||
|      do_ccs_injection=false |      do_ccs_injection=false | ||||||
|      do_ticketbleed=false |      do_ticketbleed=false | ||||||
|  |      do_opossum=false | ||||||
|      do_robot=false |      do_robot=false | ||||||
|      do_cipher_per_proto=false |      do_cipher_per_proto=false | ||||||
|      do_crime=false |      do_crime=false | ||||||
| @@ -24028,6 +24134,7 @@ set_scanning_defaults() { | |||||||
|      do_heartbleed="$OFFENSIVE" |      do_heartbleed="$OFFENSIVE" | ||||||
|      do_ccs_injection="$OFFENSIVE" |      do_ccs_injection="$OFFENSIVE" | ||||||
|      do_ticketbleed="$OFFENSIVE" |      do_ticketbleed="$OFFENSIVE" | ||||||
|  |      do_opossum=true | ||||||
|      do_robot="$OFFENSIVE" |      do_robot="$OFFENSIVE" | ||||||
|      do_crime=true |      do_crime=true | ||||||
|      do_freak=true |      do_freak=true | ||||||
| @@ -24048,9 +24155,9 @@ set_scanning_defaults() { | |||||||
|      do_tls_fallback_scsv=true |      do_tls_fallback_scsv=true | ||||||
|      do_client_simulation=true |      do_client_simulation=true | ||||||
|      if "$OFFENSIVE"; then |      if "$OFFENSIVE"; then | ||||||
|           VULN_COUNT=17 |           VULN_COUNT=18 | ||||||
|      else |      else | ||||||
|           VULN_COUNT=13 |           VULN_COUNT=14 | ||||||
|      fi |      fi | ||||||
|      do_rating=true |      do_rating=true | ||||||
| } | } | ||||||
| @@ -24061,10 +24168,10 @@ count_do_variables() { | |||||||
|      local -i true_nr=0 |      local -i true_nr=0 | ||||||
| 
 | 
 | ||||||
|      for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ |      for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ | ||||||
|                do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_starttls_injection do_grease do_robot do_renego \ |           do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_starttls_injection do_grease \ | ||||||
|                do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv do_winshock \ |           do_opossum do_robot do_renego do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv \ | ||||||
|                do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do |           do_winshock  do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do | ||||||
|                     "${!gbl}" && ((true_nr++)) |                "${!gbl}" && ((true_nr++)) | ||||||
|      done |      done | ||||||
|      return $true_nr |      return $true_nr | ||||||
| } | } | ||||||
| @@ -24074,10 +24181,10 @@ debug_globals() { | |||||||
|      local gbl |      local gbl | ||||||
| 
 | 
 | ||||||
|      for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ |      for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \ | ||||||
|                do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_starttls_injection do_grease do_robot do_renego \ |           do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_starttls_injection do_grease\ | ||||||
|                do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv do_winshock \ |           do_opossum do_robot do_renego do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv \ | ||||||
|                do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do |           do_winshock do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do | ||||||
|           printf "%-22s = %s\n" $gbl "${!gbl}" |                printf "%-22s = %s\n" $gbl "${!gbl}" | ||||||
|      done |      done | ||||||
|      # ${!var} is an indirect expansion, see https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html |      # ${!var} is an indirect expansion, see https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html | ||||||
|      # Example: https://stackoverflow.com/questions/8515411/what-is-indirect-expansion-what-does-var-mean#8515492 |      # Example: https://stackoverflow.com/questions/8515411/what-is-indirect-expansion-what-does-var-mean#8515492 | ||||||
| @@ -24283,6 +24390,7 @@ parse_cmd_line() { | |||||||
|                     do_heartbleed="$OFFENSIVE" |                     do_heartbleed="$OFFENSIVE" | ||||||
|                     do_ccs_injection="$OFFENSIVE" |                     do_ccs_injection="$OFFENSIVE" | ||||||
|                     do_ticketbleed="$OFFENSIVE" |                     do_ticketbleed="$OFFENSIVE" | ||||||
|  |                     do_opossum=true | ||||||
|                     do_robot="$OFFENSIVE" |                     do_robot="$OFFENSIVE" | ||||||
|                     do_renego=true |                     do_renego=true | ||||||
|                     do_crime=true |                     do_crime=true | ||||||
| @@ -24299,9 +24407,9 @@ parse_cmd_line() { | |||||||
|                     do_rc4=true |                     do_rc4=true | ||||||
|                     do_starttls_injection=true |                     do_starttls_injection=true | ||||||
|                     if "$OFFENSIVE"; then |                     if "$OFFENSIVE"; then | ||||||
|                          VULN_COUNT=17 |                          VULN_COUNT=18 | ||||||
|                     else |                     else | ||||||
|                          VULN_COUNT=13 |                          VULN_COUNT=14 | ||||||
|                     fi |                     fi | ||||||
|                     ;; |                     ;; | ||||||
|                --ids-friendly) |                --ids-friendly) | ||||||
| @@ -24319,6 +24427,10 @@ parse_cmd_line() { | |||||||
|                     do_ticketbleed=true |                     do_ticketbleed=true | ||||||
|                     ((VULN_COUNT++)) |                     ((VULN_COUNT++)) | ||||||
|                     ;; |                     ;; | ||||||
|  |                --OP|--opossum) | ||||||
|  |                     do_opossum=true | ||||||
|  |                     ((VULN_COUNT++)) | ||||||
|  |                     ;; | ||||||
|                -BB|--BB|--robot) |                -BB|--BB|--robot) | ||||||
|                     do_robot=true |                     do_robot=true | ||||||
|                     ;; |                     ;; | ||||||
| @@ -24984,6 +25096,7 @@ lets_roll() { | |||||||
|                "$do_heartbleed" && { run_heartbleed; ret=$(($? + ret)); stopwatch run_heartbleed; } |                "$do_heartbleed" && { run_heartbleed; ret=$(($? + ret)); stopwatch run_heartbleed; } | ||||||
|                "$do_ccs_injection" && { run_ccs_injection; ret=$(($? + ret)); stopwatch run_ccs_injection; } |                "$do_ccs_injection" && { run_ccs_injection; ret=$(($? + ret)); stopwatch run_ccs_injection; } | ||||||
|                "$do_ticketbleed" && { run_ticketbleed; ret=$(($? + ret)); stopwatch run_ticketbleed; } |                "$do_ticketbleed" && { run_ticketbleed; ret=$(($? + ret)); stopwatch run_ticketbleed; } | ||||||
|  |                "$do_opossum" && { run_opossum; ret=$(($? + ret)); stopwatch run_opossum; } | ||||||
|                "$do_robot" && { run_robot; ret=$(($? + ret)); stopwatch run_robot; } |                "$do_robot" && { run_robot; ret=$(($? + ret)); stopwatch run_robot; } | ||||||
|                "$do_renego" && { run_renego; ret=$(($? + ret)); stopwatch run_renego; } |                "$do_renego" && { run_renego; ret=$(($? + ret)); stopwatch run_renego; } | ||||||
|                "$do_crime" && { run_crime; ret=$(($? + ret)); stopwatch run_crime; } |                "$do_crime" && { run_crime; ret=$(($? + ret)); stopwatch run_crime; } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Dirk Wetter
					Dirk Wetter