From 385485d39b096f21347c9aea3dfa6820c59d8b37 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Jan 2020 11:33:11 +0100 Subject: [PATCH 001/211] More friendly phrased. Incl. soon to follow coding convention --- CONTRIBUTING.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48acd15..540ad04 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,17 @@ ### Contributions / participation -is always welcome. +is always welcome! -Note please that following is strongly requested: +Note please the following: -* One PR per feature or bug fix or improvement. -* Document your PR properly, both in the PR and/or commit message and in the code. -* Please test your changes thoroughly as reliability is important for this project. -* Follow the length [coding guideline](https://github.com/drwetter/testssl.sh/wiki/Coding-Style). +* Please read at least the [coding convention](https://github.com/drwetter/testssl.sh/Coding_Convention.md). +* One PR per feature or bug fix or improvement. Please do not mix issues. +* Document your PR, both in the PR and/or commit message and in the code. +* Please test your changes thoroughly as reliability is important for this project. You may want to check different servers with different settings. +* Travis runs automatically when anything is committed/PR'd. You should check any complains from Travis. Beforehand you can check with `prove -v`. +* If it's a new feature please consider writing a unit test for it. You can use e.g. `t/20_baseline_ipv4_http.t` as a template. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start. +* If it's a new feature it would need to be documented in the appropriate section in `help()` and in `~/doc/testssl.1.md` - -If it's a new feature please consider writing a unit test for it. There's a directory ~/t/ which Travis runs automatically when anything is committed. You can use e.g. `20_baseline_ipv4_http.t` as a template. There's also [general documentation for Test::More](https://perldoc.perl.org/Test/More.html). +For questions just open an issue. From 6c892afecd5881011fae39a3ba004c2e9f21ad35 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Jan 2020 13:09:05 +0100 Subject: [PATCH 002/211] Move from wiki hereto plus sorting+rephrasing --- Coding_Convention.md | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Coding_Convention.md diff --git a/Coding_Convention.md b/Coding_Convention.md new file mode 100644 index 0000000..445cc49 --- /dev/null +++ b/Coding_Convention.md @@ -0,0 +1,72 @@ +## Coding Style + +### PR + +You'd make the life of the maintainers easier if you submit only _one_ patch with _one_ functional change per PR. + +### General + * Portability is important: Don't use highly system depended binaries (`rpm`, `ip/ifconfig`, ..) as it is not portable. Or it would require lots of efforts (like C's #ifdefs) to be portable plus the code gets ugly. + * Don't use additional binaries. + * If you really, really need to use an additional binary make sure it's available on the system before calling it. (Example: see `timeout`.) + * Keep in mind that binaries might come in different flavors. Especially with ``sed`` you need to be careful as GNU sed is only 80% compatible with BSD sed (`sed -i`,` \n`, `\t`, etc.). + * Avoid checking for the operating system when using a feature of a binary or an OS. E.g. FreeBSD or MacOSX may or may not have GNU binaries installed, so it's better to check a capability of the binary instead. See how `HAS_*` variables are set. + + +### Documentation + +Some people really read that ! New features would need to be documented in the appropriate section in `help()` and in `~/doc/testssl.1.md`. + +### Coding + +#### Shell / bash + +Bash is actually quite powerful -- not only with respect to sockets. It's not as mighty as perl or python, but there are a lot of neat features. Here's how you make use of them. + +* Don't use backticks anymore, use `$(..)` instead +* Use double square `[[]]` instead of single square `[]` brackets +* The [BashPitfalls](http://mywiki.wooledge.org/BashPitfalls) is a good read! +* Whenever possible try to avoid `tr` `sed` `awk` and use bash internal functions instead, see e.g. [bash shell parameter substitution](http://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html). It slower as it forks, fopens and pipes back the result. +* `read` often can replace `awk`: `IFS=, read -ra a b c <<< "$line_with_comma"` +* Bash can also deal perfectly with regular expressions, see e.g. [here](https://www.networkworld.com/article/2693361/unix-tip-using-bash-s-regular-expressions.html) and [here](https://unix.stackexchange.com/questions/421460/bash-regex-and-https-regex101-com). You can as well have a look @ `is_ipv4addr()` or `is_ipv6addr()`. +* If you still need to use any of `tr`, `sed` and `awk`: try to avoid a mix of several external binaries e.g. if you can achieve the same with e.g. `awk`. +* Be careful with very advanced bash features. Mac OS X is still using bash version 3 ([http://tldp.org/LDP/abs/html/bashver4.html](differences)) +* Always use a return value for a function/method. 0 means all is fine. +* Make use of [shellcheck](https://github.com/koalaman/shellcheck) if possible + + +#### Shell / testssl.sh specific +* Make use the short functions / methods (code starts from `###### START helper function definitions`) like + * `count_words()` / `count_lines()` / `count_ciphers()` + * `strip_lf()` / `strip_spaces()` + * `toupper()` / `tolower()` + * `newline_to_spaces()` + * `is_number()` / `is_ipv4addr()` + * .. and much more +* Security: + * Watch out for any input especially (but not only) supplied from the server. Input should never be trusted. + * Unless you're really sure where the values come from, variables need to be put in quotes. + * You can use `safe_echo()` when processing input which does some input validation. + * Use ``out()`` or similar output functions when writing something back to the user. +* Use `$OPENSSL` instead of `openssl`. The latter is highly system depended and also $OPENSSL is a binary which capabilities are checked internally before using it, independent whether the supplied one is being used or another one. + +#### Variables +* Use "speaking variables" but don't overdo it with the length +* No camelCase please. We distinguish between lowercase and uppercase only + * Global variables + * use them only when really necessary + * in CAPS + * initialize them + * use ``readonly`` and use typing (variable types) if possible +* Local variables + * are lower case + * declare them before usage (`local`) + * initialize them + +### Misc + +* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDITIONAL_CA_FILES`) +* Test before doing a PR! Best if you check with two bad and two good examples which should then work as expected. Maybe compare results e.g. with SSLlabs. +* Unit tests are done automatically done with Perl using Travis. The trigger is `~/.travis.yml`. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start. You are encouraged to write own checks. You can use e.g. `t/20_baseline_ipv4_http.t` as an example. +* If it's an OpenSSL feature you want to use and it could be not available for older OpenSSL versions testssl.sh needs to find out whether OpenSSL has that feature. Best do this with OpenSSL itself and not by checking the version as some vendors do backports. See the examples for `HAS_SSL2` or proxy option check of OpenSSL in `check_proxy()`. +* If a feature of OpenSSL is not available you need to tell this the user by using `pr_warning*()`. Or accordingly with `fatal()` if a continuation of the program doesn't make sense anymore. + From 7d3ff19442a0523fd2588f39f6eb4ee32d6b3a4a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Jan 2020 14:24:22 +0100 Subject: [PATCH 003/211] Notes wrt [[, references to bash hackers wiki --- Coding_Convention.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Coding_Convention.md b/Coding_Convention.md index 445cc49..933b30c 100644 --- a/Coding_Convention.md +++ b/Coding_Convention.md @@ -20,16 +20,17 @@ Some people really read that ! New features would need to be documented in the a #### Shell / bash -Bash is actually quite powerful -- not only with respect to sockets. It's not as mighty as perl or python, but there are a lot of neat features. Here's how you make use of them. +Bash is actually quite powerful -- not only with respect to sockets. It's not as mighty as perl or python, but there are a lot of neat features. Here's how you make use of them. Besides those short hints here there's a wealth of information of there. One good resource is the [bash hackers wiki](https://wiki.bash-hackers.org/start). * Don't use backticks anymore, use `$(..)` instead -* Use double square `[[]]` instead of single square `[]` brackets +* Use double square `[[]]` brackets (_conditional expressions)_ instead of single square `[]` brackets +* In double square brackets avoid quoting at the right hand side if not necessary, see [bash hackers wiki](https://wiki.bash-hackers.org/syntax/ccmd/conditional_expression). For regex matching (`=~`) you shouldn't quote at all. * The [BashPitfalls](http://mywiki.wooledge.org/BashPitfalls) is a good read! * Whenever possible try to avoid `tr` `sed` `awk` and use bash internal functions instead, see e.g. [bash shell parameter substitution](http://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html). It slower as it forks, fopens and pipes back the result. * `read` often can replace `awk`: `IFS=, read -ra a b c <<< "$line_with_comma"` * Bash can also deal perfectly with regular expressions, see e.g. [here](https://www.networkworld.com/article/2693361/unix-tip-using-bash-s-regular-expressions.html) and [here](https://unix.stackexchange.com/questions/421460/bash-regex-and-https-regex101-com). You can as well have a look @ `is_ipv4addr()` or `is_ipv6addr()`. * If you still need to use any of `tr`, `sed` and `awk`: try to avoid a mix of several external binaries e.g. if you can achieve the same with e.g. `awk`. -* Be careful with very advanced bash features. Mac OS X is still using bash version 3 ([http://tldp.org/LDP/abs/html/bashver4.html](differences)) +* Be careful with very advanced bash features. Mac OS X is still using bash version 3 ([differences](http://tldp.org/LDP/abs/html/bashver4.html), see also [bash hackers wiki](https://wiki.bash-hackers.org/scripting/bashchanges)). * Always use a return value for a function/method. 0 means all is fine. * Make use of [shellcheck](https://github.com/koalaman/shellcheck) if possible From 3cdb16a96924cc6c4dcae2e6976ada06f517884a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Jan 2020 17:42:17 +0100 Subject: [PATCH 004/211] Prepare baseline_ipv4_http as a good example ... ... as indicated in CONTRIBUTING.md / Coding_Convention.md --- t/20_baseline_ipv4_http.t | 68 ++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/t/20_baseline_ipv4_http.t b/t/20_baseline_ipv4_http.t index 3e0bba3..b6412b6 100755 --- a/t/20_baseline_ipv4_http.t +++ b/t/20_baseline_ipv4_http.t @@ -1,54 +1,64 @@ #!/usr/bin/env perl -# Just a functional test, whether there are any problems on the client side -# Probably we could also inspect the JSON for any problems for +# baseline test for testssl, screen and JSON output + +# This is referred by the documentation. + +# We could also inspect the JSON for any problems for # "id" : "scanProblem" # "finding" : "Scan interrupted" use strict; use Test::More; use Data::Dumper; -# use JSON; -# if we need JSON we need to comment this and the lines below in +use JSON; my $tests = 0; my $prg="./testssl.sh"; -my $check2run ="-p -s -P --pfs -S -h -U -q --ip=one --color 0"; -my $uri=""; +my $check2run="-p -s -P --pfs -S -h -U -q --ip=one --color 0"; +my $uri="google.com"; my $socket_out=""; my $openssl_out=""; # Blacklists we use to trigger an error: my $socket_regex_bl='(e|E)rror|\.\/testssl\.sh: line |(f|F)atal'; my $openssl_regex_bl='(e|E)rror|(f|F)atal|\.\/testssl\.sh: line |Oops|s_client connect problem'; +my $json_regex_bl='(id".*:\s"scanProblem"|severity".*:\s"FATAL"|"Scan interrupted")'; -# my $socket_json=""; -# my $openssl_json=""; -# $check2run="--jsonfile tmp.json $check2run"; +my $socket_json=""; +my $openssl_json=""; +$check2run="--jsonfile tmp.json $check2run"; die "Unable to open $prg" unless -f $prg; -$uri="google.com"; - -# unlink "tmp.json"; -printf "\n%s\n", "Baseline unit test IPv4 via sockets --> $uri ..."; -$socket_out = `./testssl.sh $check2run $uri 2>&1`; -# $socket_json = json('tmp.json'); -unlike($socket_out, qr/$socket_regex_bl/, ""); -$tests++; - -# unlink "tmp.json"; -printf "\n%s\n", "Baseline unit test IPv4 via OpenSSL --> $uri ..."; -$openssl_out = `./testssl.sh --ssl-native $check2run $uri 2>&1`; -# $openssl_json = json('tmp.json'); -# With Google only we encounter an error as they return a 0 char with openssl, so we white list this pattern here: -$openssl_out =~ s/testssl.*warning: command substitution: ignored null byte in input\n//g; -unlike($openssl_out, qr/$openssl_regex_bl/, ""); -$tests++; - - -done_testing($tests); +# Provide proper start conditions unlink "tmp.json"; +# Title +printf "\n%s\n", "Baseline unit test IPv4 against \"$uri\""; + +#1 +$socket_out = `$prg $check2run $uri 2>&1`; +$socket_json = json('tmp.json'); +unlink "tmp.json"; +unlike($socket_out, qr/$socket_regex_bl/, "via sockets, terminal output"); +$tests++; +unlike($socket_json, qr/$json_regex_bl/, "via sockets JSON output"); +$tests++; + +#2 +$openssl_out = `$prg --ssl-native $check2run $uri 2>&1`; +$openssl_json = json('tmp.json'); +unlink "tmp.json"; +# With Google only we somtimes encounter an error as they return a 0 char with openssl, so we white list this pattern here: +# It should be fixed in the code though so we comment this out +# $openssl_out =~ s/testssl.*warning: command substitution: ignored null byte in input\n//g; +unlike($openssl_out, qr/$openssl_regex_bl/, "via OpenSSL"); +$tests++; +unlike($openssl_json, qr/$json_regex_bl/, "via OpenSSL JSON output"); +$tests++; + +done_testing($tests); +printf "\n"; sub json($) { From 58353d35220f99fdae6b5e5e0f2c55027b6499be Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 6 Mar 2020 14:25:07 -0500 Subject: [PATCH 005/211] Fix typo in emphasize_stuff_in_headers() This commit fixes a typo in emphasize_stuff_in_headers() wherer ${yellow} was used rather than ${html_yellow} in the creation of the HTML output. --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 48e0da4..577c330 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2812,13 +2812,13 @@ emphasize_stuff_in_headers(){ -e "s/X-Runtime/${html_yellow}X-Runtime${html_off}/g" \ -e "s/X-Rack-Cache/${html_yellow}X-Rack-Cache${html_off}/g" \ -e "s/X-Pingback/${html_yellow}X-Pingback${html_off}/g" \ - -e "s/X-Permitted-Cross-Domain-Policies/${yellow}X-Permitted-Cross-Domain-Policies${html_off}/g" \ + -e "s/X-Permitted-Cross-Domain-Policies/${html_yellow}X-Permitted-Cross-Domain-Policies${html_off}/g" \ -e "s/X-AspNet-Version/${html_yellow}X-AspNet-Version${html_off}/g")" \ -e "s/x-note/${html_yellow}x-note${html_off}/g" \ -e "s/X-Global-Transaction-ID/${html_yellow}X-Global-Transaction-ID${html_off}/g" \ -e "s/x-global-transaction-id/${html_yellow}x-global-transaction-id${html_off}/g" \ -e "s/Alt-Svc/${html_yellow}Alt-Svc${html_off}/g" \ - -e "s/system-wsgw-management-loopback/${yellow}system-wsgw-management-loopback${html_off}/g" + -e "s/system-wsgw-management-loopback/${html_yellow}system-wsgw-management-loopback${html_off}/g" #FIXME: this is double code. The pattern to emphasize would fit better into # one function. # Also we need another function like run_other_header as otherwise "Link" "Alt-Svc" will never be found. From 8242607d94f8c6d2ba5325f5c91bb9fa81ba3f49 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 6 Mar 2020 22:06:13 +0100 Subject: [PATCH 006/211] Fix output for BEAST when no SSL3 or TLS LF added --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 27c4a40..5b54268 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16737,7 +16737,7 @@ run_beast(){ if [[ $sclient_success -ne 0 ]]; then # protocol supported? if "$continued"; then # second round: we hit TLS1 if "$HAS_SSL3" || "$using_sockets"; then - pr_svrty_good "not vulnerable (OK)" ; out ", no SSL3 or TLS1" + pr_svrty_good "not vulnerable (OK)" ; outln ", no SSL3 or TLS1" fileout "$jsonID" "OK" "not vulnerable, no SSL3 or TLS1" "$cve" "$cwe" else prln_svrty_good "no TLS1 (OK)" From 1a3c01899fe5bf3af167b93111f3ac814620f4c4 Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 17 Mar 2020 14:34:00 +0100 Subject: [PATCH 007/211] fix basicauth bug where a newline was added to the user:password string --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index a54b9d7..f156c8b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -19606,7 +19606,7 @@ determine_service() { ua="$UA_SNEAKY" || \ ua="$UA_STD" if [[ -n "$BASICAUTH" ]]; then - basicauth_header="Authorization: Basic $($OPENSSL base64 <<< "$BASICAUTH" 2>/dev/null)\r\n" + basicauth_header="Authorization: Basic $(echo -n "$BASICAUTH" | $OPENSSL base64 2>/dev/null)\r\n" fi GET_REQ11="GET $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\n${basicauth_header}Accept-Encoding: identity\r\nAccept: text/*\r\nConnection: Close\r\n\r\n" # returns always 0: From 7fffe53d0a9148ebe47529a5f0e0b3e70decc744 Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 18 Mar 2020 13:53:58 +0100 Subject: [PATCH 008/211] replace echo with the safe_echo function --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index f156c8b..322d13f 100755 --- a/testssl.sh +++ b/testssl.sh @@ -19606,7 +19606,7 @@ determine_service() { ua="$UA_SNEAKY" || \ ua="$UA_STD" if [[ -n "$BASICAUTH" ]]; then - basicauth_header="Authorization: Basic $(echo -n "$BASICAUTH" | $OPENSSL base64 2>/dev/null)\r\n" + basicauth_header="Authorization: Basic $(safe_echo "$BASICAUTH" | $OPENSSL base64 2>/dev/null)\r\n" fi GET_REQ11="GET $URL_PATH HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $ua\r\n${basicauth_header}Accept-Encoding: identity\r\nAccept: text/*\r\nConnection: Close\r\n\r\n" # returns always 0: From e7c89cb264edc185ca445eaf0cb5b651e887af99 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 23 Mar 2020 16:53:32 +0100 Subject: [PATCH 009/211] replace printf with tm_out --- testssl.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 322d13f..847c946 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2056,7 +2056,7 @@ service_detection() { # trying with sockets is better than not even trying. tls_sockets "04" "$TLS13_CIPHER" "all+" "" "" false if [[ $? -eq 0 ]]; then - plaintext="$(printf "$GET_REQ11" | hexdump -v -e '16/1 "%02X"')" + plaintext="$(tm_out "$GET_REQ11" | hexdump -v -e '16/1 "%02X"')" plaintext="${plaintext%%[!0-9A-F]*}" send_app_data "$plaintext" if [[ $? -eq 0 ]]; then @@ -2071,7 +2071,7 @@ service_detection() { fi else # SNI is not standardized for !HTTPS but fortunately for other protocols s_client doesn't seem to care - printf "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE & + tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$1 -quiet $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP was_killed=$? fi @@ -2167,12 +2167,12 @@ run_http_header() { pr_bold " HTTP Status Code " [[ -z "$1" ]] && url="/" || url="$1" - printf "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE & + tm_out "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP if [[ $? -eq 0 ]]; then # Issue HTTP GET again as it properly finished within $HEADER_MAXSLEEP and didn't hang. # Doing it again in the foreground to get an accurate header time - printf "$GET_REQ11" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") >$HEADERFILE 2>$ERRFILE + 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") HTTP_TIME=$(awk -F': ' '/^date:/ { print $2 } /^Date:/ { print $2 }' $HEADERFILE) HAD_SLEPT=0 From 31a9dafe94e5f11cc85dbcc6fe1b0f25ad2cb9e8 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 23 Mar 2020 17:39:14 +0100 Subject: [PATCH 010/211] replace printf with tm_out one further place --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 847c946..01f5d2d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -15733,7 +15733,7 @@ run_breach() { [[ "$NODE" =~ google ]] && referer="https://yandex.ru/" # otherwise we have a false positive for google.com useragent="$UA_STD" $SNEAKY && useragent="$UA_SNEAKY" - printf "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: gzip,deflate,compress\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & + tm_out "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: gzip,deflate,compress\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & wait_kill $! $HEADER_MAXSLEEP was_killed=$? # !=0 was killed result=$(awk '/^Content-Encoding/ { print $2 }' $TMPFILE) From 5ab73d1a1a66e431743dea127adf89cc08e91690 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 25 Mar 2020 12:53:28 -0400 Subject: [PATCH 011/211] Fix bug in setting DISPLAY_CIPHERNAMES The permitted values for $DISPLAY_CIPHERNAMES are "rfc-only", "openssl-only", "openssl", and "rfc". However, get_install_dir() incorrectly sets $DISPLAY_CIPHERNAMES to "no-rfc" if it cannot find the $CIPHERS_BY_STRENGTH_FILE. ("no-rfc" is the string users would specify at the command line for the --mapping option, but not the value that $DISPLAY_CIPHERNAMES is set to internally). --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 01f5d2d..1a7a34d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -17931,7 +17931,7 @@ get_install_dir() { fi if [[ ! -r "$CIPHERS_BY_STRENGTH_FILE" ]]; then - DISPLAY_CIPHERNAMES="no-rfc" + DISPLAY_CIPHERNAMES="openssl-only" debugme echo "$CIPHERS_BY_STRENGTH_FILE" prln_warning "\nATTENTION: No cipher mapping file found!" outln "Please note from 2.9 on $PROG_NAME needs files in \"\$TESTSSL_INSTALL_DIR/etc/\" to function correctly." From d177a90bbee0057cbd2a39c762dde89430d8e043 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 25 Mar 2020 15:28:08 -0400 Subject: [PATCH 012/211] Adjust pr_cipher_quality ratings This commit makes several changes to the way that ciphers are rated by pr_cipher_quality: * It upgrades SEED ciphers to considered as strong as the corresponding AES ciphers. * It downgrades ciphers that use AEAD, but that use a non-FS key exchange (TLS_DH_*, TLS_ECDH*, TLS_PSK_WITH_*) from best to good, thus giving them the same rating as AEAD ciphers that use static RSA (TLS_RSA_*). * It downgrades some CBC ciphers to low (4) that are currently rated as neither good nor bad (5). * It modifies the ratings created using OpenSSL names to provide the same ratings as those created using RFC names. --- testssl.sh | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/testssl.sh b/testssl.sh index 1a7a34d..9610979 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5948,7 +5948,7 @@ pr_cipher_quality() { # We have an OpenSSL name and can't convert it to the RFC name which is rarely # the case, see "prepare_arrays()" and "./etc/cipher-mapping.txt" case "$cipher" in - *NULL*|EXP*|ADH*) + *NULL*|EXP*|ADH*|AECDH*) pr_svrty_critical "$text" return 1 ;; @@ -5956,20 +5956,34 @@ pr_cipher_quality() { pr_svrty_high "$text" return 2 ;; - AES256-GCM-SHA384|AES128-GCM-SHA256|AES256-CCM|AES128-CCM|ARIA256-GCM-SHA384|ARIA128-GCM-SHA256) + AES256-GCM-SHA384|AES128-GCM-SHA256|AES256-CCM*|AES128-CCM*|ARIA256-GCM-SHA384|ARIA128-GCM-SHA256) # RSA kx and e.g. GCM isn't certainly the best pr_svrty_good "$text" return 6 ;; + *CBC3*|*3DES*|*IDEA*) + pr_svrty_medium "$text" + return 3 + ;; + *DES*) + pr_svrty_high "$text" + return 2 + ;; + PSK-*GCM*|PSK-*CCM*|RSA-PSK-*GCM*|RSA-PSK-CHACHA20-POLY1305|PSK-CHACHA20-POLY1305|PSK-ARIA*-GCM-SHA*|RSA-PSK-ARIA*-GCM-SHA*) + # PSK kx and e.g. GCM isn't certainly the best + pr_svrty_good "$text" + return 6 + ;; + DH-*GCM*|ECDH-*GCM*) + # static DH or ECDH kx and GCM isn't certainly the best + pr_svrty_good "$text" + return 6 + ;; *GCM*|*CCM*|*CHACHA20*) pr_svrty_best "$text" return 7 ;; #best ones - *CBC3*|*SEED*|*3DES*|*IDEA*) - pr_svrty_medium "$text" - return 3 - ;; - ECDHE*AES*|DHE*AES*SHA*|*CAMELLIA*SHA) + *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*) pr_svrty_low "$text" return 4 ;; @@ -6000,28 +6014,23 @@ pr_cipher_quality() { pr_svrty_high "$text" return 2 ;; - *CBC3*|*SEED*|*3DES*|*IDEA*) + *CBC3*|*3DES*|*IDEA*) pr_svrty_medium "$text" return 3 ;; - TLS_RSA_*) - if [[ "$cipher" =~ CBC ]]; then - pr_svrty_low "$text" - return 4 - else - pr_svrty_good "$text" - # RSA kx and e.g. GCM isn't certainly the best - return 6 - fi + *CBC*) + pr_svrty_low "$text" + return 4 + ;; + TLS_RSA_*|TLS_DH_*|TLS_ECDH_*|TLS_PSK_WITH_*) + pr_svrty_good "$text" + # RSA, or static DH, ECDH, or PSK kx and e.g. GCM isn't certainly the best + return 6 ;; *GCM*|*CCM*|*CHACHA20*) pr_svrty_best "$text" return 7 ;; - *ECDHE*AES*CBC*|*DHE*AES*SHA*|*RSA*AES*SHA*|*CAMELLIA*SHA*) - pr_svrty_low "$text" - return 4 - ;; *) out "$text" return 5 From e15aea47909dd7729c86833b0067e4a1d51c1e43 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 25 Mar 2020 15:57:00 -0400 Subject: [PATCH 013/211] Modify pr_cipher_quality to handle ARIA This commit fixes the way pr_cipher_quality handles the OpenSSL names of some ARIA ciphers that either provide no authentication or that use CBC padding. --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 9610979..03d2970 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5948,7 +5948,7 @@ pr_cipher_quality() { # We have an OpenSSL name and can't convert it to the RFC name which is rarely # the case, see "prepare_arrays()" and "./etc/cipher-mapping.txt" case "$cipher" in - *NULL*|EXP*|ADH*|AECDH*) + *NULL*|EXP*|ADH*|AECDH*|*anon*) pr_svrty_critical "$text" return 1 ;; @@ -5983,7 +5983,7 @@ pr_cipher_quality() { pr_svrty_best "$text" return 7 ;; #best ones - *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*) + *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*|*CBC*) pr_svrty_low "$text" return 4 ;; From 72dae035b5f31dc63dc8da31dcba67aa485bf4b6 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 25 Mar 2020 16:07:22 -0400 Subject: [PATCH 014/211] Remove redundant entries This commit removes two entries from a "case" test that were already covered by a previous entry. --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 03d2970..a40a1ce 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5969,7 +5969,7 @@ pr_cipher_quality() { pr_svrty_high "$text" return 2 ;; - PSK-*GCM*|PSK-*CCM*|RSA-PSK-*GCM*|RSA-PSK-CHACHA20-POLY1305|PSK-CHACHA20-POLY1305|PSK-ARIA*-GCM-SHA*|RSA-PSK-ARIA*-GCM-SHA*) + PSK-*GCM*|PSK-*CCM*|RSA-PSK-*GCM*|RSA-PSK-CHACHA20-POLY1305|PSK-CHACHA20-POLY1305) # PSK kx and e.g. GCM isn't certainly the best pr_svrty_good "$text" return 6 From 333ccdfb41911e2554d8a5a50bbdd7ee10fac840 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Wed, 1 Apr 2020 12:40:56 +0200 Subject: [PATCH 015/211] Badges from shields.io / Monitoring Links --- Readme.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index eb7d9b4..fdceef6 100644 --- a/Readme.md +++ b/Readme.md @@ -1,8 +1,11 @@ ## Intro -[![Build Status](https://travis-ci.org/drwetter/testssl.sh.svg?branch=master)](https://travis-ci.org/drwetter/testssl.sh) + +[![Travis CI Status](https://travis-ci.org/drwetter/testssl.sh.svg?branch=3.1dev)](https://travis-ci.org/drwetter/testssl.sh) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drwetter/testssl.sh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![License](https://img.shields.io/github/license/drwetter/testssl.sh)](https://github.com/drwetter/testssl.sh/LICENSE) +[![Docker](https://img.shields.io/docker/pulls/drwetter/testssl.sh)](https://github.com/drwetter/testssl.sh/blob/3.1dev/Dockerfile.md) `testssl.sh` is a free command line tool which checks a server's service on any port for the support of TLS/SSL ciphers, protocols as well as some @@ -35,7 +38,7 @@ this program and where to get this program from. testssl.sh is working on every Linux/BSD distribution out of the box. Latest by 2.9dev most of the limitations of disabled features from the openssl client are gone -due to bash-socket-based checks. As a result you can also use e.g. LibreSSL or OpenSSL +due to bash-socket-based checks. As a result you can also use e.g. LibreSSL or OpenSSL >= 1.1.1 . testssl.sh also works on other unixoid system out of the box, supposed they have `/bin/bash` >= version 3.2 and standard tools like sed and awk installed. An implicit (silent) check for binaries is done when you start testssl.sh . System V needs probably @@ -49,7 +52,7 @@ You can download testssl.sh by cloning this git repository: git clone --depth 1 https://github.com/drwetter/testssl.sh.git -Or help yourself downloading the ZIP archive [https://github.com/drwetter/testssl.sh/archive/3.0.zip](https://github.com/drwetter/testssl.sh/archive/3.0.zip). Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. +Or help yourself downloading the 3.0 ZIP archive [https://github.com/drwetter/testssl.sh/archive/3.0.zip](https://github.com/drwetter/testssl.sh/archive/3.0.zip). Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. #### Docker @@ -114,6 +117,10 @@ Please address questions not specifically to the code of testssl.sh to the respe #### Privacy checker using testssl.sh * https://privacyscore.org +#### Nagios / Icinga Plugins +* https://github.com/dnmvisser/nagios-testssl (Python 3) +* https://gitgud.malvager.net/Wazakindjes/icinga2_plugins/src/master/check_testssl.sh (Shell) + #### Brew package * see [#233](https://github.com/drwetter/testssl.sh/issues/233) and From 40dfd8b53bc65cf89aa5571b9b7a72b2578ccedb Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 1 Apr 2020 11:18:50 -0400 Subject: [PATCH 016/211] Handle GOST ciphers in pr_cipher_quality() This PR modifes pr_cipher_quality() as proposed in #1548 so that GOST ciphers are handled correctly. It changes pr_cipher_quality() so that the OpenSSL name is used in cases in which no RFC name is defined. It also adds a case statement for GOST so that GOST ciphers (that do not use MD5 or Null encryption) are marked as pr_svrty_low (as they are in run_cipherlists) rather than just being assigned the default rating (5). --- testssl.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index a40a1ce..a5138e6 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5937,6 +5937,7 @@ pr_ecdh_curve_quality() { pr_cipher_quality() { local cipher="$1" local text="$2" + local ossl_cipher [[ -z "$1" ]] && return 0 [[ -z "$text" ]] && text="$cipher" @@ -5983,7 +5984,7 @@ pr_cipher_quality() { pr_svrty_best "$text" return 7 ;; #best ones - *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*|*CBC*) + *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*|*CBC*|*GOST*) pr_svrty_low "$text" return 4 ;; @@ -5993,7 +5994,9 @@ pr_cipher_quality() { ;; esac fi + ossl_cipher="$cipher" cipher="$(openssl2rfc "$cipher")" + [[ -z "$cipher" ]] && cipher="$ossl_cipher" fi # Now we look at the RFC cipher names. The sequence matters - as above. @@ -6018,7 +6021,7 @@ pr_cipher_quality() { pr_svrty_medium "$text" return 3 ;; - *CBC*) + *CBC*|*GOST*) pr_svrty_low "$text" return 4 ;; From 08d514622347e83b206540027a4ec7afaea1bfbb Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 1 Apr 2020 11:27:24 -0400 Subject: [PATCH 017/211] Align run_cipherlists() with pr_cipher_quality() This commit modifies run_cipherlists() to align with pr_cipher_quality(). The biggest change made by this commit is that it breaks the current list of STRONG ciphers into two lists: one for AEAD ciphers that offer forward secrecy (STRONG) and one for AEAD ciphers that do not offer forward secrecy (GOOD). The remaining changes are just minor tweaks: * A few ciphers that use MD5 are moved from AVERAGE and 3DES to LOW. * '!AECDH' was added to the OpenSSL description for LOW to catch one cipher in OpenSSL 1.0.2-chacha that offers no authentication that was being included in the LOW list. This commit also changes sub_cipherlists() to change the output when a cipherlist with a rating of 6 is not present. There was a "FIXME" associated with this output, but it didn't matter before since there were no cipherlists with a rating of 6. --- testssl.sh | 60 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/testssl.sh b/testssl.sh index a40a1ce..54f21b2 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5663,9 +5663,10 @@ sub_cipherlists() { pr_svrty_good "offered (OK)" fileout "$jsonID" "OK" "offered" "$cve" "$cwe" else - # FIXME: we penalize the absence of high but don't know the result of strong encryption yet (next) - pr_svrty_medium "not offered" - fileout "$jsonID" "MEDIUM" "not offered" "$cve" "$cwe" + # FIXME: we don't penalize the absence of high, but perhaps + # we should if there is also no strong encryption (next) + out "not offered" + fileout "$jsonID" "INFO" "not offered" "$cve" "$cwe" fi ;; 5) if [[ $sclient_success -eq 0 ]]; then @@ -5772,28 +5773,31 @@ run_cipherlists() { exp_ciphers="00,63, 00,62, 00,61, 00,65, 00,64, 00,60, 00,14, 00,11, 00,19, 00,08, 00,06, 00,27, 00,26, 00,2a, 00,29, 00,0b, 00,0e, 00,17, 00,03, 00,28, 00,2b, 00,ff" sslv2_exp_ciphers="04,00,80, 02,00,80, 00,00,00" - ossl_low_ciphers='LOW:DES:RC2:RC4:!ADH:!EXP:!NULL:!eNULL' - # egrep -w '64|56|RC2|RC4' etc/cipher-mapping.txt | egrep -v 'Au=None|export' - low_ciphers="00,04, 00,05, 00,09, 00,0C, 00,0F, 00,12, 00,15, 00,1E, 00,20, 00,22, 00,24, 00,66, 00,8A, 00,8E, 00,92, C0,02, C0,07, C0,0C, C0,11, C0,33, FE,FE, FF,E1, 00,FF" - sslv2_low_ciphers="01,00,80, 03,00,80, 06,00,40, 06,01,40, 08,00,80, FF,80,00" + ossl_low_ciphers='LOW:DES:RC2:RC4:MD5:!ADH:!EXP:!NULL:!eNULL:!AECDH' + # egrep -w '64|56|RC2|RC4|MD5' etc/cipher-mapping.txt | egrep -v 'Au=None|export' + low_ciphers="00,04, 00,05, 00,09, 00,0C, 00,0F, 00,12, 00,15, 00,1E, 00,20, 00,22, 00, 23, 00,24, 00,25, 00,66, 00,8A, 00,8E, 00,92, C0,02, C0,07, C0,0C, C0,11, C0,33, FF,00, FE,FE, FF,E1, 00,FF" + sslv2_low_ciphers="01,00,80, 03,00,80, 05,00,80, 06,00,40, 06,01,40, 07,00,c0, 08,00,80, FF,80,00" - ossl_tdes_ciphers='3DES:IDEA:!aNULL:!ADH' - # egrep -w '3DES|IDEA' etc/cipher-mapping.txt | grep -v "Au=None" - tdes_ciphers="00,07, 00,0A, 00,0D, 00,10, 00,13, 00,16, 00,1F, 00,21, 00,23, 00,25, 00,8B, 00,8F, 00,93, C0,03, C0,08, C0,0D, C0,12, C0,1A, C0,1B, C0,1C, C0,34, FE,FF, FF,E0, 00,FF" - sslv2_tdes_ciphers="05,00,80, 07,00,c0, 07,01,c0" + ossl_tdes_ciphers='3DES:IDEA:!aNULL:!ADH:!MD5' + # egrep -w '3DES|IDEA' etc/cipher-mapping.txt | egrep -v "Au=None|MD5" + tdes_ciphers="00,07, 00,0A, 00,0D, 00,10, 00,13, 00,16, 00,1F, 00,21, 00,8B, 00,8F, 00,93, C0,03, C0,08, C0,0D, C0,12, C0,1A, C0,1B, C0,1C, C0,34, FE,FF, FF,E0, 00,FF" + sslv2_tdes_ciphers="07,01,c0" - ossl_average_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL' - # egrep -w "256|128" etc/cipher-mapping.txt | egrep -v "Au=None|AEAD|RC2|RC4|IDEA" + ossl_average_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' + # egrep -w "256|128" etc/cipher-mapping.txt | egrep -v "Au=None|AEAD|RC2|RC4|IDEA|MD5" average_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" - # Workaround: If we use sockets and in order not to hit 132+1 ciphers we omit the GOST ciphers if SERVER_SIZE_LIMIT_BUG is true. - # This won't be supported by Cisco ACE anyway. Catch is, if SERVER_SIZE_LIMIT_BUG was not tested for before (only this function is being called) - "$SERVER_SIZE_LIMIT_BUG" || average_ciphers="${average_ciphers}, 00,80, 00,81, FF,00, FF,01, FF,02, FF,03, FF,85" + # Workaround: If we use sockets and in order not to hit 131+1 ciphers we omit the GOST ciphers if SERVER_SIZE_LIMIT_BUG is true. + # This won't be supported by Cisco ACE anyway. + "$SERVER_SIZE_LIMIT_BUG" || average_ciphers="${average_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" average_ciphers="${average_ciphers}, 00,FF" - # Here's the strongest discrepancy between sockets and OpenSSL - ossl_strong_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM' - # grep AEAD etc/cipher-mapping.txt | grep -v Au=None - strong_ciphers="00,9C, 00,9D, 00,9E, 00,9F, 00,A0, 00,A1, 00,A2, 00,A3, 00,A4, 00,A5, 00,A8, 00,A9, 00,AA, 00,AB, 00,AC, 00,AD, 13,01, 13,02, 13,03, 13,04, 13,05, 16,B7, 16,B8, 16,B9, 16,BA, C0,2B, C0,2C, C0,2D, C0,2E, C0,2F, C0,30, C0,31, C0,32, C0,50, C0,51, C0,52, C0,53, C0,54, C0,55, C0,56, C0,57, C0,58, C0,59, C0,5C, C0,5D, C0,5E, C0,5F, C0,60, C0,61, C0,62, C0,63, C0,6A, C0,6B, C0,6C, C0,6D, C0,6E, C0,6F, C0,7A, C0,7B, C0,7C, C0,7D, C0,7E, C0,7F, C0,80, C0,81, C0,82, C0,83, C0,86, C0,87, C0,88, C0,89, C0,8A, C0,8B, C0,8C, C0,8D, C0,8E, C0,8F, C0,90, C0,91, C0,92, C0,93, C0,9C, C0,9D, C0,9E, C0,9F, C0,A0, C0,A1, C0,A2, C0,A3, C0,A4, C0,A5, C0,A6, C0,A7, C0,A8, C0,A9, C0,AA, C0,AB, C0,AC, C0,AD, C0,AE, C0,AF, CC,13, CC,14, CC,15, CC,A8, CC,A9, CC,AA, CC,AB, CC,AC, CC,AD, CC,AE, 00,FF" + ossl_good_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM:!kEECDH:!kEDH:!kDHE:!kDHEPSK:!kECDHEPSK:!aNULL' + # grep AEAD etc/cipher-mapping.txt | egrep -v 'Au=None|TLS_ECDHE|TLS_DHE|TLS_PSK_DHE|TLSv1.3' + good_ciphers="00,9C, 00,9D, 00,A0, 00,A1, 00,A4, 00,A5, 00,A8, 00,A9, 00,AC, 00,AD, C0,2D, C0,2E, C0,31, C0,32, C0,50, C0,51, C0,54, C0,55, C0,58, C0,59, C0,5E, C0,5F, C0,62, C0,63, C0,6A, C0,6B, C0,6E, C0,6F, C0,7A, C0,7B, C0,7E, C0,7F, C0,82, C0,83, C0,88, C0,89, C0,8C, C0,8D, C0,8E, C0,8F, C0,92, C0,93, C0,9C, C0,9D, C0,A0, C0,A1, C0,A4, C0,A5, C0,A8, C0,A9, CC,AB, CC,AE, 00,FF" + + ossl_strong_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM:!kPSK:!kRSAPSK:!kRSA:!kDH:!kECDH:!aNULL' + # grep AEAD etc/cipher-mapping.txt | egrep 'TLS_ECDHE|TLS_DHE|TLS_PSK_DHE|TLSv1.3' + strong_ciphers="00,9E, 00,9F, 00,A2, 00,A3, 00,AA, 00,AB, 13,01, 13,02, 13,03, 13,04, 13,05, 16,B7, 16,B8, 16,B9, 16,BA, C0,2B, C0,2C, C0,2F, C0,30, C0,52, C0,53, C0,56, C0,57, C0,5C, C0,5D, C0,60, C0,61, C0,6C, C0,6D, C0,7C, C0,7D, C0,80, C0,81, C0,86, C0,87, C0,8A, C0,8B, C0,90, C0,91, C0,9E, C0,9F, C0,A2, C0,A3, C0,A6, C0,A7, C0,AA, C0,AB, C0,AC, C0,AD, C0,AE, C0,AF, CC,13, CC,14, CC,15, CC,A8, CC,A9, CC,AA, CC,AC, CC,AD, 00,FF" # argv[1]: non-TLSv1.3 cipher list to test in OpenSSL syntax # argv[2]: TLSv1.3 cipher list to test in OpenSSL syntax @@ -5806,19 +5810,21 @@ run_cipherlists() { # argv[9]: CVE # argv[10]: CWE - sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" ret=$? - sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication)" 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4] (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_average_ciphers" "" " Obsolete: SEED + 128+256 Bit CBC cipher " 4 "AVERAGE" "$average_ciphers" "" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_average_ciphers" "" " Obsolete: SEED + 128+256 Bit CBC cipher " 4 "AVERAGE" "$average_ciphers" "" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Strong encryption (AEAD ciphers) " 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_good_ciphers" "" " non-FS Strong encryption (AEAD ciphers) " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" + ret=$((ret + $?)) + sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secure Strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) outln From b6050e68de679743390ad9f386ed628c6cec3dd5 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 1 Apr 2020 13:34:29 -0400 Subject: [PATCH 018/211] Fix #1551 This commit fixes #1551 by changing get_cipher() to recognize RFC names that begin with SSL_*. It also modifies run_beast() so that it does not get stuck in an infinite loop if get_cipher() doesn't return a valid cipher name. --- testssl.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index a40a1ce..373fd5b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -911,7 +911,7 @@ get_cipher() { # and https://github.com/drwetter/testssl.sh/issues/1292 # Performance measurements showed no to barely measureable penalty (1s displayed in 9 tries). - if [[ "$server_hello" =~ Cipher\ *:\ ([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then + if [[ "$server_hello" =~ Cipher\ *:\ ([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+|SSL_[A-Za-z0-9_]+) ]]; then cipher="${BASH_REMATCH##* }" elif [[ "$server_hello" =~ (New|Reused)", "(SSLv[23]|TLSv1(\.[0-3])?(\/SSLv3)?)", Cipher is "([A-Z0-9]+-[A-Za-z0-9\-]+|TLS_[A-Za-z0-9_]+) ]]; then cipher="${BASH_REMATCH##* }" @@ -16822,6 +16822,7 @@ run_beast(){ for (( i=0; i < nr_ciphers; i++ )); do [[ "$cbc_cipher" == "${rfc_ciph[i]}" ]] && break done + [[ $i -eq $nr_ciphers ]] && break ciphers_found[i]=true if ( [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]] && [[ "${ciph[i]}" != - ]] ) || [[ "${rfc_ciph[i]}" == - ]]; then detected_cbc_ciphers+="${ciph[i]} " From 04e51db40281a1ba7e4311fea3ceb4c0f1bd8a46 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 4 Mar 2020 10:09:13 -0500 Subject: [PATCH 019/211] Fix #1514 This commit is an attempt to fix #1514. The commit is mostly based on a suggestion at https://unix.stackexchange.com/questions/57940/trap-int-term-exit-really-necessary. Even with that change, it seemed that if testssl.sh were in the middle of executing run_cipher_per_proto() when it received a signal, it would not stop until that function had completed. This seems to have something to do with subshells. Changing the while loop in run_cipher_per_proto() seems to have fixed that issue. So, I also made similar changes to the while loops in prettyprint_local(). --- testssl.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/testssl.sh b/testssl.sh index 697e5a3..fe299ef 100755 --- a/testssl.sh +++ b/testssl.sh @@ -113,7 +113,8 @@ fi ########### Traps! Make sure that temporary files are cleaned up after use in ANY case # -trap "cleanup" QUIT EXIT +trap "cleanup" EXIT +trap "sig_cleanup" INT QUIT TERM trap "child_error" USR1 @@ -3142,21 +3143,21 @@ prettyprint_local() { neat_header if [[ -z "$1" ]]; then - actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "-V" | while read -r hexcode dash ciph sslvers kx auth enc mac export ; do # -V doesn't work with openssl < 1.0 + while read -r hexcode dash ciph sslvers kx auth enc mac export ; do hexc="$(normalize_ciphercode $hexcode)" outln "$(neat_list "$hexc" "$ciph" "$kx" "$enc" "$export")" - done + done < <(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "-V") # -V doesn't work with openssl < 1.0 else #for arg in $(echo $@ | sed 's/,/ /g'); do for arg in ${*//,/ /}; do - actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "-V" | while read -r hexcode dash ciph sslvers kx auth enc mac export ; do # -V doesn't work with openssl < 1.0 + while read -r hexcode dash ciph sslvers kx auth enc mac export ; do hexc="$(normalize_ciphercode $hexcode)" # for numbers we don't do word matching: [[ $arg =~ $re ]] && \ line="$(neat_list "$hexc" "$ciph" "$kx" "$enc" "$export" | grep -ai "$arg")" || \ line="$(neat_list "$hexc" "$ciph" "$kx" "$enc" "$export" | grep -wai "$arg")" [[ -n "$line" ]] && outln "$line" - done + done < <(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "-V") # -V doesn't work with openssl < 1.0 done fi outln @@ -4274,9 +4275,9 @@ run_cipher_per_proto() { fi outln neat_header - echo -e " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n -tls1_3 04 TLS 1.3" | while read proto proto_hex proto_text; do + while read proto proto_hex proto_text; do ciphers_by_strength "$proto" "$proto_hex" "$proto_text" "$using_sockets" - done + done <<< "$(tm_out " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n -tls1_3 04 TLS 1.3")" return 0 #FIXME: no error condition } @@ -18674,6 +18675,13 @@ cleanup() { grep -q xtrace <<< "$SHELLOPTS" && ! "$DEBUG_ALLINONE" && exec 2>&42 42>&- } +# see https://unix.stackexchange.com/questions/57940/trap-int-term-exit-really-necessary +sig_cleanup() { + trap '' EXIT + cleanup + exit 0 +} + child_error() { cleanup exit $ERR_CHILD From 7eba0fbb41393abc475a900cfb56d24a851d8a69 Mon Sep 17 00:00:00 2001 From: Jaroslav Svoboda Date: Thu, 9 Apr 2020 16:18:33 +0200 Subject: [PATCH 020/211] FIxed links Links in comments with http:// changed to https://. Some non working links fixed. --- testssl.sh | 94 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/testssl.sh b/testssl.sh index 697e5a3..a55d64c 100755 --- a/testssl.sh +++ b/testssl.sh @@ -13,7 +13,7 @@ # Project lead and initiator: Dirk Wetter, copyleft: 2007-today, contributions so far see CREDITS.md # Main contributions from David Cooper # -# License: GPLv2, see http://www.fsf.org/licensing/licenses/info/GPLv2.html +# License: GPLv2, see https://spdx.org/licenses/GPL-2.0-only.html # and accompanying license "LICENSE.txt". Redistribution + modification under this # license permitted. # If you enclose this script or parts of it in your software, it has to @@ -142,7 +142,7 @@ HNAME="${HNAME%%.*}" declare CMDLINE CMDLINE_PARSED="" # This makes sure we don't let early fatal() write into files when files aren't created yet declare -r -a CMDLINE_ARRAY=("$@") # When performing mass testing, the child processes need to be sent the -declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and http://mywiki.wooledge.org/BashFAQ/050). +declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and https://mywiki.wooledge.org/BashFAQ/050). ########### Defining (and presetting) variables which can be changed @@ -514,7 +514,7 @@ outln() { printf -- "%b" "$1\n"; html_out "$(html_reserved "$1")\n"; } #TODO: Still no shell injection safe but if just run it from the cmd line: that's fine -# Color print functions, see also http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html +# Color print functions, see also https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html tm_liteblue() { [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && tm_out "\033[0;32m$1" || tm_out "\033[0;34m$1" ) || tm_out "$1"; tm_off; } # not yet used pr_liteblue() { tm_liteblue "$1"; [[ "$COLOR" -ge 2 ]] && ( "$COLORBLIND" && html_out "$(html_reserved "$1")" || html_out "$(html_reserved "$1")" ) || html_out "$(html_reserved "$1")"; } tmln_liteblue() { tm_liteblue "$1"; tmln_out; } @@ -629,7 +629,7 @@ pr_reverse() { tm_reverse "$1"; [[ "$COLOR" -ne 0 ]] && html_out "$(html_reserved "$1")" || html_out "$(html_reserved "$1")"; } #pr_headline() { pr_blue "$1"; } -#http://misc.flogisoft.com/bash/tip_colors_and_formatting +#https://misc.flogisoft.com/bash/tip_colors_and_formatting #pr_headline() { [[ "$COLOR" -ge 2 ]] && out "\033[1;30m\033[47m$1" || out "$1"; tm_off; } tm_headline() { [[ "$COLOR" -ne 0 ]] && tm_out "\033[1m\033[4m$1" || tm_out "$1"; tm_off; } @@ -658,7 +658,7 @@ pr_url() { tm_out "$1"; html_out "$1"; } ### color switcher (see e.g. https://linuxtidbits.wordpress.com/2008/08/11/output-color-on-bash-scripts/ -### http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html +### https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html ### no output support for HTML! set_color_functions() { local ncurses_tput=true @@ -816,7 +816,7 @@ is_number() { } strip_quote() { - # remove color codes (see http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed) + # remove color codes (see https://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed) # \', leading and all trailing spaces sed -e "s,$(echo -e "\033")\[[0-9;]*[a-zA-Z],,g" \ -e "s/\"/\\'/g" \ @@ -1334,7 +1334,7 @@ html_header() { html_out "\n" html_out "\n" html_out "\n" - html_out "\n" + html_out "\n" html_out "\n" html_out "\n" html_out "testssl.sh\n" @@ -2582,7 +2582,7 @@ run_hpkp() { $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS $PROXY -showcerts -connect $NODEIP:$PORT $SNI") $TMPFILE 2>$ERRFILE # Place the server's certificate in $HOSTCERT and any intermediate # certificates that were provided in $TEMPDIR/intermediatecerts.pem - # http://backreference.org/2010/05/09/ocsp-verification-with-openssl/ + # https://backreference.org/2010/05/09/ocsp-verification-with-openssl/ awk -v n=-1 "/Certificate chain/ {start=1} /-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} } inc { print > (\"$TEMPDIR/level\" n \".crt\") } @@ -2728,7 +2728,7 @@ emphasize_stuff_in_headers(){ local html_yellow="" local html_off="<\\/span>" -# see http://www.grymoire.com/Unix/Sed.html#uh-3 +# see https://www.grymoire.com/Unix/Sed.html#uh-3 # outln "$1" | sed "s/[0-9]*/$brown&${off}/g" tmln_out "$1" | sed -e "s/\([0-9]\)/${brown}\1${off}/g" \ -e "s/Unix/${yellow}Unix${off}/g" \ @@ -6744,7 +6744,7 @@ cipher_pref_check() { ciphers_found="$order" fi if "$WIDE" || [[ -z "$order" ]]; then - tested_cipher=""; order=""; nr_ciphers_found=0 + tested_cipher=""; order=""; nr_ciphers_found=0 while true; do if [[ $p != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then @@ -7311,7 +7311,7 @@ extract_certificates() { # Place the server's certificate in $HOSTCERT and any intermediate # certificates that were provided in $TEMPDIR/intermediatecerts.pem savedir="$PWD"; cd $TEMPDIR - # http://backreference.org/2010/05/09/ocsp-verification-with-openssl/ + # https://backreference.org/2010/05/09/ocsp-verification-with-openssl/ if [[ "$version" == ssl2 ]]; then awk -v n=-1 '/Server certificate/ {start=1} /-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} } @@ -8352,10 +8352,10 @@ certificate_info() { let ret++ ;; esac out "$short_keyAlgo " - # https://tools.ietf.org/html/rfc4492, http://www.keylength.com/en/compare/ - # http://infoscience.epfl.ch/record/164526/files/NPDF-22.pdf - # see http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf - # Table 2 @ chapter 5.6.1 (~ p64) + # https://tools.ietf.org/html/rfc4492, https://www.keylength.com/en/compare/ + # https://doi.org/10.1007/s00145-001-0009-4 + # see http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 + # Table 2 @ chapter 5.6.1 (~ p66) if [[ $cert_key_algo =~ ecdsa ]] || [[ $cert_key_algo =~ ecPublicKey ]]; then if [[ "$cert_keysize" -le 110 ]]; then # a guess pr_svrty_critical "$cert_keysize" @@ -8723,7 +8723,7 @@ certificate_info() { determine_trust "$jsonID" "$json_postfix" || ((ret++)) fi - # http://events.ccc.de/congress/2010/Fahrplan/attachments/1777_is-the-SSLiverse-a-safe-place.pdf, see page 40pp + # https://www.eff.org/files/ccc2010.pdf, see page 40pp out "$indent"; pr_bold " EV cert"; out " (experimental) " jsonID="cert_certificatePolicies_EV" # only the first one, seldom we have two @@ -8745,8 +8745,8 @@ certificate_info() { debugme echo "($(newline_to_spaces "$policy_oid"))" outln #TODO: check browser OIDs: -# https://mxr.mozilla.org/mozilla-central/source/security/certverifier/ExtendedValidation.cpp -# http://src.chromium.org/chrome/trunk/src/net/cert/ev_root_ca_metadata.cc +# https://dxr.mozilla.org/mozilla-central/source/security/certverifier/ExtendedValidation.cpp +# https://chromium.googlesource.com/chromium/chromium/+/master/net/base/ev_root_ca_metadata.cc # https://certs.opera.com/03/ev-oids.xml # see #967 @@ -9990,7 +9990,7 @@ run_pfs() { # good source for configuration and bugs: https://wiki.mozilla.org/Security/Server_Side_TLS -# good start to read: http://en.wikipedia.org/wiki/Transport_Layer_Security#Attacks_against_TLS.2FSSL +# good start to read: https://en.wikipedia.org/wiki/Transport_Layer_Security#Attacks_against_TLS.2FSSL npn_pre(){ @@ -10054,7 +10054,7 @@ run_npn() { ((ret++)) fi fi - # btw: nmap can do that too http://nmap.org/nsedoc/scripts/tls-nextprotoneg.html + # btw: nmap can do that too https://nmap.org/nsedoc/scripts/tls-nextprotoneg.html # nmap --script=tls-nextprotoneg #NODE -p $PORT is your friend if your openssl doesn't want to test this tmpfile_handle ${FUNCNAME[0]}.txt return $ret @@ -10445,7 +10445,7 @@ fd_socket() { starttls_xmpp_dialog # IM observatory: https://xmpp.net , XMPP server directory: https://xmpp.net/directory.php ;; - postgres) # Postgres SQL, see http://www.postgresql.org/docs/devel/static/protocol-message-formats.html + postgres) # Postgres SQL, see https://www.postgresql.org/docs/devel/protocol-message-formats.html starttls_postgres_dialog ;; mysql) # MySQL, see https://dev.mysql.com/doc/internals/en/x-protocol-lifecycle-lifecycle.html#x-protocol-lifecycle-tls-extension @@ -10484,7 +10484,7 @@ code2network() { NW_STR=$(sed -e 's/,/\\\x/g' <<< "$1" | sed -e 's/# .*$//g' -e 's/ //g' -e '/^$/d' | tr -d '\n' | tr -d '\t') } -# sockets inspired by http://blog.chris007.de/?p=238 +# sockets inspired by https://blog.chris007.de/using-bash-for-network-socket-operation/ # ARG1: hexbytes separated by commas, with a leading comma # ARG2: seconds to sleep socksend_clienthello() { @@ -11226,7 +11226,7 @@ derive-handshake-traffic-keys() { label="732068732074726166666963" else # "632068732074726166666963" = "c hs traffic" - label="632068732074726166666963" + label="632068732074726166666963" fi handshake_traffic_secret="$(derive-secret "$hash_fn" "$handshake_secret" "$label" "$transcript")" [[ $? -ne 0 ]] && return 7 @@ -11317,7 +11317,7 @@ derive-application-traffic-keys() { label="732061702074726166666963" else # "632061702074726166666963" = "c hs traffic" - label="632061702074726166666963" + label="632061702074726166666963" fi application_traffic_secret_0="$(derive-secret "$hash_fn" "$master_secret" "$label" "$transcript")" [[ $? -ne 0 ]] && return 7 @@ -11544,7 +11544,7 @@ chacha20() { # Used to decode value encoded as 32-bit little-endian integer u8to32() { local p="$1" - + tm_out "0x${p:6:2}${p:4:2}${p:2:2}${p:0:2}" return 0 } @@ -11627,12 +11627,12 @@ poly1305_mac() { s2=$((r2*5)) s3=$((r3*5)) s4=$((r4*5)) - + pad0=$(u8to32 "${mac_key:32:8}") pad1=$(u8to32 "${mac_key:40:8}") pad2=$(u8to32 "${mac_key:48:8}") pad3=$(u8to32 "${mac_key:56:8}") - + # poly1305_update for (( 1 ; bytes > 0; bytes=bytes-blocksize )); do if [[ $bytes -ge 32 ]]; then @@ -12625,7 +12625,7 @@ parse_tls_serverhello() { # Client messages, including handshake messages, are carried by the record layer. # First, extract the handshake and alert messages. - # see http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#TLS_record + # see https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record # byte 0: content type: 0x14=CCS, 0x15=TLS alert x16=Handshake, 0x17 Application, 0x18=HB # byte 1+2: TLS version word, major is 03, minor 00=SSL3, 01=TLS1 02=TLS1.1 03=TLS 1.2 # byte 3+4: fragment length @@ -13594,7 +13594,7 @@ parse_tls_serverhello() { if [[ $curve_type -eq 3 ]]; then # named_curve - the curve is identified by a 2-byte number named_curve=$(hex2dec "${tls_serverkeyexchange_ascii:2:4}") - # http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 + # https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 case $named_curve in 1) dh_bits=163 ; named_curve_str="K-163" ;; 2) dh_bits=162 ; named_curve_str="sect163r1" ;; @@ -14301,7 +14301,7 @@ prepare_tls_clienthello() { [[ 0x$tls_legacy_version -ge 0x04 ]] && tls_legacy_version="03" if "$offer_compression"; then - # See http://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml#comp-meth-ids-2 + # See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml#comp-meth-ids-2 compression_methods="03,01,40,00" # Offer NULL, DEFLATE, and LZS compression else compression_methods="01,00" # Only offer NULL compression (0x00) @@ -14726,7 +14726,7 @@ tls_sockets() { finished_msg="$(sym-encrypt "$cipher" "$key" "$(get-nonce "$iv" 0)" "${finished_msg}16" "")" fi finished_msg="$aad$finished_msg" - + len=${#finished_msg} for (( i=0; i < len; i+=2 )); do data+=", ${finished_msg:i:2}" @@ -14734,7 +14734,7 @@ tls_sockets() { debugme echo -e "\nsending finished..." socksend_clienthello "${data}" sleep $USLEEP_SND - + # Compute application traffic keys and IVs. master_secret="$(derive-master-secret "$cipher" "$handshake_secret")" master_traffic_keys="$(derive-application-traffic-keys "$cipher" "$master_secret" "$msg_transcript" server)" @@ -14832,7 +14832,7 @@ receive_app_data() { read -r tls_version cipher server_key server_iv server_seq client_key client_iv client_seq <<< "$APP_TRAF_KEY_INFO" [[ "${tls_version:0:2}" == 7F ]] && [[ 0x${tls_version:2:2} -lt 25 ]] && include_headers=false - + sleep $USLEEP_REC while true; do len=${#ciphertext} @@ -14882,7 +14882,7 @@ receive_app_data() { ####### Vulnerabilities follow ####### # General overview which browser "supports" which vulnerability: -# http://en.wikipedia.org/wiki/Transport_Layer_Security-SSL#Web_browsers +# https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers # mainly adapted from https://gist.github.com/takeshixx/10107280 # @@ -15190,7 +15190,7 @@ sub_session_ticket_tls() { } -# see https://blog.filippo.io/finding-ticketbleed/ | http://ticketbleed.com/ +# see https://blog.filippo.io/finding-ticketbleed/ | https://filippo.io/ticketbleed/ run_ticketbleed() { local session_tckt_tls="" local -i len_ch=300 # fixed len of prepared clienthello below @@ -15512,7 +15512,7 @@ run_renego() { jsonID="secure_client_renego" cve="CVE-2011-1473" # see: https://blog.qualys.com/ssllabs/2011/10/31/tls-renegotiation-and-denial-of-service-attacks - # http://blog.ivanristic.com/2009/12/testing-for-ssl-renegotiation.html -- head/get doesn't seem to be needed though + # https://blog.ivanristic.com/2009/12/testing-for-ssl-renegotiation.html -- head/get doesn't seem to be needed though # https://archive.fo/20130415224936/http://www.thc.org/thc-ssl-dos/ # https://vincent.bernat.ch/en/blog/2011-ssl-dos-mitigation case "$OSSL_VER" in @@ -16930,7 +16930,8 @@ run_beast(){ } -# http://www.isg.rhul.ac.uk/tls/Lucky13.html +# https://web.archive.org/web/20200324101422/http://www.isg.rhul.ac.uk/tls/Lucky13.html +# Paper: https://doi.org/10.1109/SP.2013.42 # in a nutshell: don't offer CBC suites (again). MAC as a fix for padding oracles is not enough. Best: TLS v1.2+ AES GCM run_lucky13() { local spaces=" " @@ -17010,7 +17011,7 @@ run_lucky13() { # https://tools.ietf.org/html/rfc7465 REQUIRES that TLS clients and servers NEVER negotiate the use of RC4 cipher suites! # https://en.wikipedia.org/wiki/Transport_Layer_Security#RC4_attacks -# http://blog.cryptographyengineering.com/2013/03/attack-of-week-rc4-is-kind-of-broken-in.html +# https://blog.cryptographyengineering.com/2013/03/attack-of-week-rc4-is-kind-of-broken-in.html # run_rc4() { local -i rc4_offered=0 @@ -17265,8 +17266,9 @@ run_rc4() { run_youknowwho() { local cve="CVE-2013-2566" local cwe="CWE-310" - # NOT FIXME as there's no code: http://www.isg.rhul.ac.uk/tls/ - # http://blog.cryptographyengineering.com/2013/03/attack-of-week-rc4-is-kind-of-broken-in.html + # NOT FIXME as there's no code: https://web.archive.org/web/20191008002003/http://www.isg.rhul.ac.uk/tls/index.html + # Paper and presentation: https://www.usenix.org/conference/usenixsecurity13/technical-sessions/paper/alFardan + # https://blog.cryptographyengineering.com/2013/03/12/attack-of-week-rc4-is-kind-of-broken-in/ return 0 # in a nutshell: don't use RC4, really not! } @@ -18026,7 +18028,8 @@ find_openssl_binary() { fatal "cannot exec or find any openssl binary" $ERR_OSSLBIN fi - # http://www.openssl.org/news/openssl-notes.html + # https://www.openssl.org/news/changelog.html + # https://web.archive.org/web/20150815130800/http://openssl.org/news/openssl-notes.html OSSL_NAME=$($OPENSSL version 2>/dev/null | awk '{ print $1 }') OSSL_VER=$($OPENSSL version 2>/dev/null | awk -F' ' '{ print $2 }') OSSL_VER_MAJOR="${OSSL_VER%%\.*}" @@ -18597,7 +18600,7 @@ mybanner() { bb1=$(cat < Date: Mon, 13 Apr 2020 22:59:13 +0200 Subject: [PATCH 021/211] Tuning multiflexi's fixes --- testssl.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index a55d64c..08821b1 100755 --- a/testssl.sh +++ b/testssl.sh @@ -13,7 +13,7 @@ # Project lead and initiator: Dirk Wetter, copyleft: 2007-today, contributions so far see CREDITS.md # Main contributions from David Cooper # -# License: GPLv2, see https://spdx.org/licenses/GPL-2.0-only.html +# License: GPLv2, see https://opensource.org/licenses/gpl-2.0.php # and accompanying license "LICENSE.txt". Redistribution + modification under this # license permitted. # If you enclose this script or parts of it in your software, it has to @@ -1334,7 +1334,7 @@ html_header() { html_out "\n" html_out "\n" html_out "\n" - html_out "\n" + html_out "\n" html_out "\n" html_out "\n" html_out "testssl.sh\n" @@ -8354,7 +8354,7 @@ certificate_info() { out "$short_keyAlgo " # https://tools.ietf.org/html/rfc4492, https://www.keylength.com/en/compare/ # https://doi.org/10.1007/s00145-001-0009-4 - # see http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 + # see https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-4/final # Table 2 @ chapter 5.6.1 (~ p66) if [[ $cert_key_algo =~ ecdsa ]] || [[ $cert_key_algo =~ ecPublicKey ]]; then if [[ "$cert_keysize" -le 110 ]]; then # a guess @@ -8723,7 +8723,7 @@ certificate_info() { determine_trust "$jsonID" "$json_postfix" || ((ret++)) fi - # https://www.eff.org/files/ccc2010.pdf, see page 40pp + # https://fahrplan.events.ccc.de/congress/2010/Fahrplan/attachments/1777_is-the-SSLiverse-a-safe-place.pdf, see p40+ out "$indent"; pr_bold " EV cert"; out " (experimental) " jsonID="cert_certificatePolicies_EV" # only the first one, seldom we have two @@ -18617,7 +18617,7 @@ EOF ########################################################### EOF ) - pr_bold "$bb1" + pr_bold "$bb1 " pr_boldurl "$SWURL"; outln if [[ -n "$idtag" ]]; then #FIXME: if we run it not off the git dir we miss the version tag. @@ -18626,7 +18626,7 @@ EOF pr_grey "$idtag" prln_bold ")" fi - pr_bold "$bb2" + pr_bold "$bb2 " pr_boldurl "https://testssl.sh/bugs/"; outln pr_bold "$bb3" outln "\n" From 67cfe013b1b116e6c913d22ba249775521831ad1 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 14 Apr 2020 13:35:26 +0200 Subject: [PATCH 022/211] Polishing * remove CVS variables * add 2x https links instead of http in code doc --- testssl.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/testssl.sh b/testssl.sh index 08821b1..74d1c9e 100755 --- a/testssl.sh +++ b/testssl.sh @@ -94,7 +94,7 @@ declare -r ALLOK=0 # All is fine ########### Debugging helpers + profiling # declare -r PS4='|${LINENO}> \011${FUNCNAME[0]:+${FUNCNAME[0]}(): }' -DEBUGTIME=${DEBUGTIME:-false} # stackoverflow.com/questions/5014823/how-to-profile-a-bash-shell-script-slow-startup#20855353, profiling bash +DEBUGTIME=${DEBUGTIME:-false} # https://stackoverflow.com/questions/5014823/how-to-profile-a-bash-shell-script-slow-startup#20855353 DEBUG_ALLINONE=${DEBUG_ALLINONE:-false} # true: do debugging in one screen (old behavior for testssl.sh and bash3's default # false: needed for performance analysis or useful for just having an extra file DEBUG_ALLINONE=${SETX:-false} # SETX as a shortcut for old style debugging, overriding DEBUG_ALLINONE @@ -124,14 +124,10 @@ declare -r SWCONTACT="dirk aet testssl dot sh" grep -E -q "dev|rc|beta" <<< "$VERSION" && \ SWURL="https://testssl.sh/dev/" || SWURL="https://testssl.sh/" -declare -r CVS_REL="$(tail -5 "$0" | awk '/dirkw Exp/ { print $4" "$5" "$6}')" -declare -r CVS_REL_SHORT="$(tail -5 "$0" | awk '/dirkw Exp/ { print $4 }')" if git log &>/dev/null; then declare -r GIT_REL="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $1" "$2" "$3 }')" declare -r GIT_REL_SHORT="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $1 }')" declare -r REL_DATE="$(git log --format='%h %ci' -1 2>/dev/null | awk '{ print $2 }')" -else - declare -r REL_DATE="$(tail -5 "$0" | awk '/dirkw Exp/ { print $5 }')" fi declare -r PROG_NAME="$(basename "$0")" declare -r RUN_DIR="$(dirname "$0")" @@ -1119,7 +1115,7 @@ fileout_pretty_json_banner() { echo -e " \"Invocation\" : \"$PROG_NAME $CMDLINE\", \"at\" : \"$HNAME:$OPENSSL_LOCATION\", - \"version\" : \"$VERSION ${GIT_REL_SHORT:-$CVS_REL_SHORT} from $REL_DATE\", + \"version\" : \"$VERSION $GIT_REL_SHORT\", \"openssl\" : \"$OSSL_NAME $OSSL_VER from $OSSL_BUILD_DATE\", \"startTime\" : \"$START_TIME\", \"scanResult\" : [" @@ -6117,7 +6113,7 @@ read_dhbits_from_file() { else pr_dh_quality "$bits" "$bits $add" fi - # https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography, http://www.keylength.com/en/compare/ + # https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography, https://www.keylength.com/en/compare/ elif [[ $what_dh == ECDH ]]; then add="bit ECDH" [[ -n "$curve" ]] && add+=" ($curve)" @@ -13785,7 +13781,8 @@ sslv2_sockets() { FF,80,00, # 11 FF,80,10, # 12 00,00,00" # 13 - # FIXME: http://max.euston.net/d/tip_sslciphers.html <-- also SSLv3 ciphers + # FIXME: also SSLv3 ciphers, see + # https://web.archive.org/web/20170310142840/http://max.euston.net/d/tip_sslciphers.html fi code2network "$cipher_suites" # convert CIPHER_SUITES @@ -17273,9 +17270,9 @@ run_youknowwho() { # in a nutshell: don't use RC4, really not! } +run_tls_truncation() { # https://www.usenix.org/conference/woot13/workshop-program/presentation/smyth # https://secure-resumption.com/tlsauth.pdf -run_tls_truncation() { #FIXME: difficult to test, is there any test available: pls let me know : } From 8c466bf2ee4c7b454f3381ea0430c640b926c9b7 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 14 Apr 2020 15:53:05 +0200 Subject: [PATCH 023/211] Rename PFS/perfect forward secrecy to FS/forward secrecy In all instances: * command line (will break things) * JSON IDs (will break things) * in the documentation * in the travis checks where used * everywhere in the code: variables, functions, comments --- doc/testssl.1 | 8 +- doc/testssl.1.html | 8 +- doc/testssl.1.md | 8 +- t/20_baseline_ipv4_http.t | 2 +- t/21_baseline_ipv6_http.t.DISABLED | 2 +- t/25_baseline_starttls.t | 2 +- testssl.sh | 124 ++++++++++++++--------------- 7 files changed, 77 insertions(+), 77 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 822fbbd..9c0f684 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -46,7 +46,7 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 2) standard cipher categories to give you upfront an idea for the ciphers supported . .P -3) checks (perfect) forward secrecy: ciphers and elliptical curves +3) checks forward secrecy: ciphers and elliptical curves . .P 4) server preferences (server order) @@ -214,7 +214,7 @@ Any single check switch supplied as an argument prevents testssl\.sh from doing .IP "" 0 . .P -\fB\-f, \-\-pfs, \-\-fs,\-\-nsa\fR Checks robust (perfect) forward secrecy key exchange\. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won\'t be considered here\. There shouldn\'t be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks\. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1\.2 and TLS 1\.3)\. +\fB\-f, \-\-fs, \-\-nsa, \-\-forward\-secrecy\fR Checks robust forward secrecy key exchange\. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won\'t be considered here\. There shouldn\'t be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks\. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1\.2 and TLS 1\.3)\. . .P \fB\-p, \-\-protocols\fR checks TLS/SSL protocols SSLv2, SSLv3, TLS 1\.0 through TLS 1\.3 and for HTTP: SPDY (NPN) and ALPN, a\.k\.a\. HTTP/2\. For TLS 1\.3 several drafts (from 18 on) and final are supported and being tested for\. @@ -389,7 +389,7 @@ Security headers (X\-Frame\-Options, X\-XSS\-Protection, Expect\-CT,\.\.\. , CSP \fB\-q, \-\-quiet\fR Normally testssl\.sh displays a banner on stdout with several version information, usage rights and a warning\. This option suppresses it\. Please note that by choosing this option you acknowledge usage terms and the warning normally appearing in the banner\. . .P -\fB\-\-wide\fR Except the "each cipher output" all tests displays the single cipher name (scheme see below)\. This option enables testssl\.sh to display also for the following sections the same output as for testing each ciphers: BEAST, PFS, RC4\. The client simulation has also a wide mode\. The difference here is restricted to a column aligned output and a proper headline\. The environment variable \fBWIDE\fR can be used instead\. +\fB\-\-wide\fR Except the "each cipher output" all tests displays the single cipher name (scheme see below)\. This option enables testssl\.sh to display also for the following sections the same output as for testing each ciphers: BEAST, FS, RC4\. The client simulation has also a wide mode\. The difference here is restricted to a column aligned output and a proper headline\. The environment variable \fBWIDE\fR can be used instead\. . .P \fB\-\-mapping \fR @@ -635,7 +635,7 @@ MAX_HEADER_FAIL: A number which tells testssl\.sh how often a HTTP GET request o .fi . .P -does a default run on https://testssl\.sh (protocols, standard cipher lists, PFS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation\. +does a default run on https://testssl\.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation\. . .IP "" 4 . diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 56c24d8..e7f3c34 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -123,7 +123,7 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

2) standard cipher categories to give you upfront an idea for the ciphers supported

-

3) checks (perfect) forward secrecy: ciphers and elliptical curves

+

3) checks forward secrecy: ciphers and elliptical curves

4) server preferences (server order)

@@ -244,7 +244,7 @@ ADDITIONAL_CA_FILES is the environment variable for this.

-

-f, --pfs, --fs,--nsa Checks robust (perfect) forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3).

+

-f, --fs, --nsa, --forward-secrecy Checks robust forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3).

-p, --protocols checks TLS/SSL protocols SSLv2, SSLv3, TLS 1.0 through TLS 1.3 and for HTTP: SPDY (NPN) and ALPN, a.k.a. HTTP/2. For TLS 1.3 several drafts (from 18 on) and final are supported and being tested for.

@@ -345,7 +345,7 @@ Also for multiple server certificates are being checked for as well as for the c

-q, --quiet Normally testssl.sh displays a banner on stdout with several version information, usage rights and a warning. This option suppresses it. Please note that by choosing this option you acknowledge usage terms and the warning normally appearing in the banner.

-

--wide Except the "each cipher output" all tests displays the single cipher name (scheme see below). This option enables testssl.sh to display also for the following sections the same output as for testing each ciphers: BEAST, PFS, RC4. The client simulation has also a wide mode. The difference here is restricted to a column aligned output and a proper headline. The environment variable WIDE can be used instead.

+

--wide Except the "each cipher output" all tests displays the single cipher name (scheme see below). This option enables testssl.sh to display also for the following sections the same output as for testing each ciphers: BEAST, FS, RC4. The client simulation has also a wide mode. The difference here is restricted to a column aligned output and a proper headline. The environment variable WIDE can be used instead.

--mapping <openssl|iana|no-openssl|no-iana>

@@ -480,7 +480,7 @@ Also for multiple server certificates are being checked for as well as for the c
  testssl.sh testssl.sh
 
-

does a default run on https://testssl.sh (protocols, standard cipher lists, PFS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation.

+

does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation.

  testssl.sh testssl.net:443
 
diff --git a/doc/testssl.1.md b/doc/testssl.1.md index d2756b6..f347c20 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -42,7 +42,7 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 2) standard cipher categories to give you upfront an idea for the ciphers supported -3) checks (perfect) forward secrecy: ciphers and elliptical curves +3) checks forward secrecy: ciphers and elliptical curves 4) server preferences (server order) @@ -164,7 +164,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a * `Average grade Ciphers`: 'HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL' * `Strong grade Ciphers` (AEAD): 'AESGCM:CHACHA20:AESGCM:CamelliaGCM:AESCCM8:AESCCM' -`-f, --pfs, --fs,--nsa ` Checks robust (perfect) forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3). +`-f, --fs, --nsa, --forward-secrecy` Checks robust forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3). `-p, --protocols` checks TLS/SSL protocols SSLv2, SSLv3, TLS 1.0 through TLS 1.3 and for HTTP: SPDY (NPN) and ALPN, a.k.a. HTTP/2. For TLS 1.3 several drafts (from 18 on) and final are supported and being tested for. @@ -258,7 +258,7 @@ Also for multiple server certificates are being checked for as well as for the c `-q, --quiet` Normally testssl.sh displays a banner on stdout with several version information, usage rights and a warning. This option suppresses it. Please note that by choosing this option you acknowledge usage terms and the warning normally appearing in the banner. -`--wide` Except the "each cipher output" all tests displays the single cipher name (scheme see below). This option enables testssl.sh to display also for the following sections the same output as for testing each ciphers: BEAST, PFS, RC4. The client simulation has also a wide mode. The difference here is restricted to a column aligned output and a proper headline. The environment variable `WIDE` can be used instead. +`--wide` Except the "each cipher output" all tests displays the single cipher name (scheme see below). This option enables testssl.sh to display also for the following sections the same output as for testing each ciphers: BEAST, FS, RC4. The client simulation has also a wide mode. The difference here is restricted to a column aligned output and a proper headline. The environment variable `WIDE` can be used instead. `--mapping ` @@ -389,7 +389,7 @@ Except the environment variables mentioned above which can replace command line testssl.sh testssl.sh -does a default run on https://testssl.sh (protocols, standard cipher lists, PFS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation. +does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation. testssl.sh testssl.net:443 diff --git a/t/20_baseline_ipv4_http.t b/t/20_baseline_ipv4_http.t index b6412b6..575a262 100755 --- a/t/20_baseline_ipv4_http.t +++ b/t/20_baseline_ipv4_http.t @@ -15,7 +15,7 @@ use JSON; my $tests = 0; my $prg="./testssl.sh"; -my $check2run="-p -s -P --pfs -S -h -U -q --ip=one --color 0"; +my $check2run="-p -s -P --fs -S -h -U -q --ip=one --color 0"; my $uri="google.com"; my $socket_out=""; my $openssl_out=""; diff --git a/t/21_baseline_ipv6_http.t.DISABLED b/t/21_baseline_ipv6_http.t.DISABLED index f8ff53b..2043f50 100755 --- a/t/21_baseline_ipv6_http.t.DISABLED +++ b/t/21_baseline_ipv6_http.t.DISABLED @@ -15,7 +15,7 @@ use Data::Dumper; my $tests = 0; my $prg="./testssl.sh"; -my $check2run ="-p -s -P --pfs -S -h -U -q --ip=one --color 0"; +my $check2run ="-p -s -P --fs -S -h -U -q --ip=one --color 0"; my $uri=""; my $socket_out=""; my $openssl_out=""; diff --git a/t/25_baseline_starttls.t b/t/25_baseline_starttls.t index 3513eb2..0179e4a 100755 --- a/t/25_baseline_starttls.t +++ b/t/25_baseline_starttls.t @@ -17,7 +17,7 @@ use Data::Dumper; my $tests = 0; my $prg="./testssl.sh"; -my $check2run_smtp="--protocols --standard --pfs --server-preference --headers --vulnerable --each-cipher -q --ip=one --color 0"; +my $check2run_smtp="--protocols --standard --fs --server-preference --headers --vulnerable --each-cipher -q --ip=one --color 0"; my $check2run="-q --ip=one --color 0"; my $uri=""; my $socket_out=""; diff --git a/testssl.sh b/testssl.sh index 926c863..9470482 100755 --- a/testssl.sh +++ b/testssl.sh @@ -215,7 +215,7 @@ DAYS2WARN2=${DAYS2WARN2:-30} # days to warn before cert expires, thre VULN_THRESHLD=${VULN_THRESHLD:-1} # if vulnerabilities to check >$VULN_THRESHLD we DON'T show a separate header line in the output each vuln. check UNBRACKTD_IPV6=${UNBRACKTD_IPV6:-false} # some versions of OpenSSL (like Gentoo) don't support [bracketed] IPv6 addresses NO_ENGINE=${NO_ENGINE:-false} # if there are problems finding the (external) openssl engine set this to true -declare -r CLIENT_MIN_PFS=5 # number of ciphers needed to run a test for PFS +declare -r CLIENT_MIN_FS=5 # number of ciphers needed to run a test for FS CAPATH="${CAPATH:-/etc/ssl/certs/}" # Does nothing yet (FC has only a CA bundle per default, ==> openssl version -d) GOOD_CA_BUNDLE="" # A bundle of CA certificates that can be used to validate the server's certificate CERTIFICATE_LIST_ORDERING_PROBLEM=false # Set to true if server sends a certificate list that contains a certificate @@ -1019,7 +1019,7 @@ fileout_json_section() { 2) echo -e ",\n \"protocols\" : [" ;; 3) echo -e ",\n \"grease\" : [" ;; 4) echo -e ",\n \"ciphers\" : [" ;; - 5) echo -e ",\n \"pfs\" : [" ;; + 5) echo -e ",\n \"fs\" : [" ;; 6) echo -e ",\n \"serverPreferences\" : [" ;; 7) echo -e ",\n \"serverDefaults\" : [" ;; 8) echo -e ",\n \"headerResponse\" : [" ;; @@ -8757,7 +8757,7 @@ certificate_info() { etsi_etls_visibility_info "$jsonID" "$spaces" "$HOSTCERT" "$cert_txt" # *Currently* this is even listed as a vulnerability (CWE-310, CVE-2019-919), see # https://nvd.nist.gov/vuln/detail/CVE-2019-9191, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9191 - # For now we leave this here. We may want to change that later or add infos to other sections (PFS & vulnerability) + # For now we leave this here. We may want to change that later or add infos to other sections (FS & vulnerability) out "$indent"; pr_bold " Certificate Validity (UTC) " # FreeBSD + OSX can't swallow the leading blank: @@ -9341,7 +9341,7 @@ run_server_defaults() { unit=$(grep -a lifetime <<< "$sessticket_lifetime_hint" | sed -e 's/^.*'"$lifetime"'//' -e 's/[ ()]//g') out "$lifetime $unit" if [[ $((3600 * 24)) -lt $lifetime ]]; then - prln_svrty_low " but: PFS requires session ticket keys to be rotated < daily !" + prln_svrty_low " but: FS requires session ticket keys to be rotated < daily !" fileout "$jsonID" "LOW" "valid for $lifetime $unit (>daily)" else outln ", session tickets keys seems to be rotated < daily" @@ -9498,15 +9498,15 @@ get_san_dns_from_cert() { } -run_pfs() { +run_fs() { local -i sclient_success - local pfs_offered=false ecdhe_offered=false ffdhe_offered=false - local pfs_tls13_offered=false - local protos_to_try proto hexc dash pfs_cipher sslvers auth mac export curve dhlen + local fs_offered=false ecdhe_offered=false ffdhe_offered=false + local fs_tls13_offered=false + local protos_to_try proto hexc dash fs_cipher sslvers auth mac export curve dhlen local -a hexcode normalized_hexcode ciph rfc_ciph kx enc ciphers_found sigalg ossl_supported # generated from 'kEECDH:kEDH:!aNULL:!eNULL:!DES:!3DES:!RC4' with openssl 1.0.2i and openssl 1.1.0 - local pfs_cipher_list="DHE-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-DSS-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA:DHE-DSS-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA:DHE-DSS-SEED-SHA:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-SEED-SHA:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305" - local pfs_hex_cipher_list="" ciphers_to_test tls13_ciphers_to_test + local fs_cipher_list="DHE-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-DSS-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA:DHE-DSS-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA:DHE-DSS-SEED-SHA:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA:DHE-RSA-CAMELLIA256-SHA256:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-CHACHA20-POLY1305-OLD:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-SEED-SHA:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA256-SHA384:ECDHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305" + local fs_hex_cipher_list="" ciphers_to_test tls13_ciphers_to_test local ecdhe_cipher_list="" tls13_cipher_list="" ecdhe_cipher_list_hex="" ffdhe_cipher_list_hex="" local curves_hex=("00,01" "00,02" "00,03" "00,04" "00,05" "00,06" "00,07" "00,08" "00,09" "00,0a" "00,0b" "00,0c" "00,0d" "00,0e" "00,0f" "00,10" "00,11" "00,12" "00,13" "00,14" "00,15" "00,16" "00,17" "00,18" "00,19" "00,1a" "00,1b" "00,1c" "00,1d" "00,1e") local -a curves_ossl=("sect163k1" "sect163r1" "sect163r2" "sect193r1" "sect193r2" "sect233k1" "sect233r1" "sect239k1" "sect283k1" "sect283r1" "sect409k1" "sect409r1" "sect571k1" "sect571r1" "secp160k1" "secp160r1" "secp160r2" "secp192k1" "prime192v1" "secp224k1" "secp224r1" "secp256k1" "prime256v1" "secp384r1" "secp521r1" "brainpoolP256r1" "brainpoolP384r1" "brainpoolP512r1" "X25519" "X448") @@ -9519,20 +9519,20 @@ run_pfs() { local -a ffdhe_groups_output=("ffdhe2048" "ffdhe3072" "ffdhe4096" "ffdhe6144" "ffdhe8192") local -a supported_curve local -i nr_supported_ciphers=0 nr_curves=0 nr_ossl_curves=0 i j low high - local pfs_ciphers curves_offered="" curves_to_test temp + local fs_ciphers curves_offered="" curves_to_test temp local len1 len2 curve_found local key_bitstring quality_str local -i len_dh_p quality local has_dh_bits="$HAS_DH_BITS" local using_sockets=true - local jsonID="PFS" + local jsonID="FS" "$SSL_NATIVE" && using_sockets=false "$FAST" && using_sockets=false [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false outln - pr_headline " Testing robust (perfect) forward secrecy"; prln_underline ", (P)FS -- omitting Null Authentication/Encryption, 3DES, RC4 " + pr_headline " Testing robust forward secrecy (FS)"; prln_underline " -- omitting Null Authentication/Encryption, 3DES, RC4 " if ! "$using_sockets"; then [[ $TLS_NR_CIPHERS == 0 ]] && ! "$SSL_NATIVE" && ! "$FAST" && pr_warning " Cipher mapping not available, doing a fallback to openssl" if ! "$HAS_DH_BITS" && "$WIDE"; then @@ -9544,12 +9544,12 @@ run_pfs() { if "$using_sockets" || [[ $OSSL_VER_MAJOR -lt 1 ]]; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do - pfs_cipher="${TLS_CIPHER_RFC_NAME[i]}" + fs_cipher="${TLS_CIPHER_RFC_NAME[i]}" hexc="${TLS_CIPHER_HEXCODE[i]}" - if ( [[ "$pfs_cipher" == "TLS_DHE_"* ]] || [[ "$pfs_cipher" == "TLS_ECDHE_"* ]] || [[ "${hexc:2:2}" == "13" ]] ) && \ - [[ ! "$pfs_cipher" =~ NULL ]] && [[ ! "$pfs_cipher" =~ DES ]] && [[ ! "$pfs_cipher" =~ RC4 ]] && \ - [[ ! "$pfs_cipher" =~ PSK ]] && ( "$using_sockets" || "${TLS_CIPHER_OSSL_SUPPORTED[i]}" ); then - pfs_hex_cipher_list+=", ${hexc:2:2},${hexc:7:2}" + if ( [[ "$fs_cipher" == "TLS_DHE_"* ]] || [[ "$fs_cipher" == "TLS_ECDHE_"* ]] || [[ "${hexc:2:2}" == "13" ]] ) && \ + [[ ! "$fs_cipher" =~ NULL ]] && [[ ! "$fs_cipher" =~ DES ]] && [[ ! "$fs_cipher" =~ RC4 ]] && \ + [[ ! "$fs_cipher" =~ PSK ]] && ( "$using_sockets" || "${TLS_CIPHER_OSSL_SUPPORTED[i]}" ); then + fs_hex_cipher_list+=", ${hexc:2:2},${hexc:7:2}" ciph[nr_supported_ciphers]="${TLS_CIPHER_OSSL_NAME[i]}" rfc_ciph[nr_supported_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" kx[nr_supported_ciphers]="${TLS_CIPHER_KX[i]}" @@ -9578,26 +9578,26 @@ run_pfs() { sigalg[nr_supported_ciphers]="" ossl_supported[nr_supported_ciphers]=true nr_supported_ciphers+=1 - done < <(actually_supported_osslciphers "$pfs_cipher_list" "ALL" "-V") + done < <(actually_supported_osslciphers "$fs_cipher_list" "ALL" "-V") fi if [[ $(has_server_protocol "tls1_3") -eq 0 ]]; then - # All TLSv1.3 cipher suites offer robust PFS. + # All TLSv1.3 cipher suites offer robust FS. sclient_success=0 elif "$using_sockets"; then - tls_sockets "04" "${pfs_hex_cipher_list:2}, 00,ff" + tls_sockets "04" "${fs_hex_cipher_list:2}, 00,ff" sclient_success=$? [[ $sclient_success -eq 2 ]] && sclient_success=0 else debugme echo $nr_supported_ciphers - debugme echo $(actually_supported_osslciphers $pfs_cipher_list "ALL") - if [[ "$nr_supported_ciphers" -le "$CLIENT_MIN_PFS" ]]; then + debugme echo $(actually_supported_osslciphers $fs_cipher_list "ALL") + if [[ "$nr_supported_ciphers" -le "$CLIENT_MIN_FS" ]]; then outln - prln_local_problem "You only have $nr_supported_ciphers PFS ciphers on the client side " - fileout "$jsonID" "WARN" "tests skipped as you only have $nr_supported_ciphers PFS ciphers on the client site. ($CLIENT_MIN_PFS are required)" + prln_local_problem "You only have $nr_supported_ciphers FS ciphers on the client side " + fileout "$jsonID" "WARN" "tests skipped as you only have $nr_supported_ciphers FS ciphers on the client site. ($CLIENT_MIN_FS are required)" return 1 fi - $OPENSSL s_client $(s_client_options "-cipher $pfs_cipher_list -ciphersuites "ALL" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE $TMPFILE ("$PROG_NAME URI" does everything except -E and -g): -e, --each-cipher checks each local cipher remotely -E, --cipher-per-proto checks those per protocol -s, --std, --standard tests certain lists of cipher suites by strength - -f, --pfs, --fs, --nsa checks (perfect) forward secrecy settings + -f, --fs, --nsa checks forward secrecy settings -p, --protocols checks TLS/SSL protocols (including SPDY/HTTP2) -g, --grease tests several server implementation bugs like GREASE and size limitations -S, --server-defaults displays the server's default picks and certificate info @@ -18377,7 +18377,7 @@ tuning / connect options (most also can be preset via environment variables): output options (can also be preset via environment variables): --quiet don't output the banner. By doing this you acknowledge usage terms normally appearing in the banner - --wide wide output for tests like RC4, BEAST. PFS also with hexcode, kx, strength, RFC name + --wide wide output for tests like RC4, BEAST. FS also with hexcode, kx, strength, RFC name --show-each for wide outputs: display all ciphers tested -- not only succeeded ones --mapping use the IANA/(RFC) cipher suite name as the primary name cipher suite name form @@ -18528,7 +18528,7 @@ DEBUG: $DEBUG HSTS_MIN: $HSTS_MIN HPKP_MIN: $HPKP_MIN -CLIENT_MIN_PFS: $CLIENT_MIN_PFS +CLIENT_MIN_FS: $CLIENT_MIN_FS DAYS2WARN1: $DAYS2WARN1 DAYS2WARN2: $DAYS2WARN2 @@ -20387,7 +20387,7 @@ initialize_globals() { do_pretty_json=false do_csv=false do_html=false - do_pfs=false + do_fs=false do_protocols=false do_rc4=false do_grease=false @@ -20424,7 +20424,7 @@ set_scanning_defaults() { do_ssl_poodle=true do_sweet32=true do_header=true - do_pfs=true + do_fs=true do_rc4=true do_protocols=true do_renego=true @@ -20446,7 +20446,7 @@ count_do_variables() { local 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 \ - do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_pfs do_protocols do_rc4 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_grease 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 [[ "${!gbl}" == true ]] && let true_nr++ @@ -20459,7 +20459,7 @@ debug_globals() { 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 \ - do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_pfs do_protocols do_rc4 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_grease 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 printf "%-22s = %s\n" $gbl "${!gbl}" @@ -20719,8 +20719,8 @@ parse_cmd_line() { do_rc4=true let "VULN_COUNT++" ;; - -f|--pfs|--fs|--nsa) - do_pfs=true + -f|--fs|--nsa|--forward-secrecy) + do_fs=true ;; -g|--grease) do_grease=true @@ -21159,7 +21159,7 @@ lets_roll() { "$do_cipherlists" && { run_cipherlists; ret=$(($? + ret)); stopwatch run_cipherlists; } fileout_section_header $section_number true && ((section_number++)) - "$do_pfs" && { run_pfs; ret=$(($? + ret)); stopwatch run_pfs; } + "$do_fs" && { run_fs; ret=$(($? + ret)); stopwatch run_fs; } fileout_section_header $section_number true && ((section_number++)) "$do_server_preference" && { run_server_preference; ret=$(($? + ret)); stopwatch run_server_preference; } From e4cef5438dfa2ed242b4faceccc6002fab87a492 Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Wed, 15 Apr 2020 15:06:08 +0200 Subject: [PATCH 024/211] Added grading based on ssllabs --- doc/testssl.1.md | 53 +++++- testssl.sh | 446 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 479 insertions(+), 20 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index f347c20..696d9f3 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -56,6 +56,8 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 9) client simulation +10) Result of script in form of a grade + ## OPTIONS AND PARAMETERS @@ -142,8 +144,7 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. -`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this. +`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively ADDITIONAL_CA_FILES is the environment variable for this. ### SINGLE CHECK OPTIONS @@ -286,6 +287,8 @@ Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and 5. display bytes received via sockets 6. whole 9 yards +`--disable-grading` disables grading explicitly. +Grading automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). `DISABLE_GRADING` is the according environment variable which you can use. ### FILE OUTPUT OPTIONS @@ -383,13 +386,56 @@ Except the environment variables mentioned above which can replace command line * MAX_OSSL_FAIL: A number which tells testssl.sh how often an OpenSSL s_client connect may fail before the program gives up and terminates. The default is 2. You can increase it to a higher value if you frequently see a message like *Fatal error: repeated TCP connect problems, giving up*. * MAX_HEADER_FAIL: A number which tells testssl.sh how often a HTTP GET request over OpenSSL may return an empty file before the program gives up and terminates. The default is 3. Also here you can incerase the threshold when you spot messages like *Fatal error: repeated HTTP header connect problems, doesn't make sense to continue*. +### GRADING +This script has a near-complete implementation of SSLLabs's '[SSL Server Rating Guide](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide)'. +This is *not* a reimplementation of the [SSLLab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but a implementation of the above grading specification, slight discrepancies might occur! + +Disclaimer: Having a good grade does **NOT** necessary equal to having good security! Never rely solely on a good grade! + +As of writing, these checks are missing: +* Authenticated encryption (AEAD) - should be graded **B** if not supported +* GOLDENDOODLE - should be graded **F** if vulnerable +* Insecure renegotiation - should be graded **F** if vulnerable +* Padding oracle in AES-NI CBC MAC check (CVE-2016-2107) - should be graded **F** if vulnerable +* Sleeping POODLE - should be graded **F** if vulnerable +* Zero Length Padding Oracle (CVE-2019-1559) - should be graded **F** if vulnerable +* Zombie POODLE - should be graded **F** if vulnerable +* All remaining old Symantec PKI certificates are distrusted - should be graded **T** +* Symantec certificates issued before June 2016 are distrusted - should be graded **T** +* ! A reading of DH params - should give correct points in `set_key_str_score()` +* Anonymous key exchange - should give **0** points in `set_key_str_score()` +* Exportable key exchange - should give **40** points in `set_key_str_score()` +* Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()` + +#### Implementing new grades caps or -warnings +To implement at new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: +```bash +set_grade_cap "D" "Vulnerable to documentation" +``` +To implement a new grade warning, simply call the `set_grade_warning()` function, with a message: +```bash +set_grade_warning "Documentation is always right" +``` +#### Implementing a new check which contains grade caps +When implementing a new check (be it vulnerability or not) that sets grade caps, the `set_grading_state()` has to be updated (i.e. the `$do_mycheck` variable-name has to be added to the loop, and `$nr_enabled` if-statement has to be incremented) + +The `set_grading_state()` automatically disables grading, if all the required checks are *not* enabled. +This is to prevent giving out a misleading or wrong grade. + +#### Implementing a new revision +When a new revision of the grading specification comes around, the following has to be done: +* New grade caps has to be either: + 1. Added to the script wherever relevant, or + 2. Added to the above list of missing checks (if *i.* is not possible) +* New grade warnings has to be added wherever relevant +* The revision output in `run_grading()` function has to updated ## EXAMPLES testssl.sh testssl.sh -does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation. +does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation, and grading. testssl.sh testssl.net:443 @@ -508,4 +554,3 @@ Probably. Current known ones and interface for filing new ones: https://testssl. ## SEE ALSO `ciphers`(1), `openssl`(1), `s_client`(1), `x509`(1), `verify`(1), `ocsp`(1), `crl`(1), `bash`(1) and the websites https://testssl.sh/ and https://github.com/drwetter/testssl.sh/ . - diff --git a/testssl.sh b/testssl.sh index d5d7ee7..c540e33 100755 --- a/testssl.sh +++ b/testssl.sh @@ -227,6 +227,7 @@ fi DISPLAY_CIPHERNAMES="openssl" # display OpenSSL ciphername (but both OpenSSL and RFC ciphernames in wide mode) declare -r UA_STD="TLS tester from $SWURL" declare -r UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" +DISABLE_GRADING=${DISABLE_GRADING:-false} # Whether to disable grading, or not ########### Initialization part, further global vars just being declared here # @@ -372,6 +373,13 @@ SERVER_COUNTER=0 # Counter for multiple servers TLS_LOW_BYTE="" # For "secret" development stuff, see -q below HEX_CIPHER="" # " +GRADE_CAP="" # Keeps track of the current grading cap +GRADE_CAP_REASONS=() # Keeps track of all the reasons why grades are capped +GRADE_WARNINGS=() # Keeps track of all the grade warnings +KEY_EXCH_SCORE=0 # Keeps track of the score for category 2 "Key Exchange Strength" +CIPH_STR_BEST=0 # Keeps track of the best bit size for category 3 "Cipher Strength" +CIPH_STR_WORST=100000 # Keeps track of the worst bit size for category 3 "Cipher Strength" + # Intentionally set very high, so it can be set to 0, if necessary ########### Global variables for parallel mass testing # @@ -982,6 +990,105 @@ f5_port_decode() { echo $((16#${tmp:2:2}${tmp:0:2})) # reverse order and convert it from hex to dec } +# Sets the grade cap to ARG1 +# arg1: A grade to set ("A", "B", "C", "D", "E", "F", "M", or "T") +# arg2: A reason why (e.g. "Vulnerable to CRIME") +set_grade_cap() { + # Do nothing if disabled + "$DISABLE_GRADING" && return 0 + + GRADE_CAP_REASONS+=("Grade capped to $1. $2") + + # Always set special attributes. These are hard caps, due to name mismatch or cert being invalid + if [[ "$1" == "T" || "$1" == "M" ]]; then + GRADE_CAP=$1 + # Only keep track of the lowest grade cap, since a higher grade cap wont do anything (F = lowest, A = highest) + elif [[ ! "$GRADE_CAP" > "$1" ]]; then + GRADE_CAP=$1 + fi + + return 0 +} + +# Sets a grade warning, as specified by the grade specification +# arg1: A warning message +set_grade_warning() { + # Do nothing if disabled + "$DISABLE_GRADING" && return 0 + + GRADE_WARNINGS+=("$1") + + return 0 +} + +# Sets the score for Category 2 (Key Exchange Strength) +# arg1: Short key algorithm ("EC", "DH", "RSA", ...) # Can die, when we get DH_PARAMs +# arg2: key size (number of bits) +set_key_str_score() { + local type=$1 + local size=$2 + + # Do nothing if disabled + "$DISABLE_GRADING" && return 0 + + # TODO: We need to get the size of DH params (follows the same table as the "else" clause) + # For now, verifying the key size will do... + if [[ $type == "EC" || $type == "DH" ]]; then + if [[ $size -lt 110 ]]; then + let KEY_EXCH_SCORE=20 + set_grade_cap "F" "Using a insecure key" + elif [[ $size -lt 123 ]]; then + let KEY_EXCH_SCORE=40 + set_grade_cap "F" "Using a insecure key" + elif [[ $size -lt 163 ]]; then + let KEY_EXCH_SCORE=80 + set_grade_cap "B" "Using a weak key" + elif [[ $size -lt 225 ]]; then + let KEY_EXCH_SCORE=90 + elif [[ $size -ge 225 ]]; then + let KEY_EXCH_SCORE=100 + else + let KEY_EXCH_SCORE=0 + set_grade_cap "F" "Using a insecure key" + fi + else + if [[ $size -lt 512 ]]; then + let KEY_EXCH_SCORE=20 + set_grade_cap "F" "Using a insecure key" + elif [[ $size -lt 1024 ]]; then + let KEY_EXCH_SCORE=40 + set_grade_cap "F" "Using a insecure key" + elif [[ $size -lt 2048 ]]; then + let KEY_EXCH_SCORE=80 + set_grade_cap "B" "Using a weak key" + elif [[ $size -lt 4096 ]]; then + let KEY_EXCH_SCORE=90 + elif [[ $size -ge 4096 ]]; then + let KEY_EXCH_SCORE=100 + else + let KEY_EXCH_SCORE=0 + set_grade_cap "F" "Using a insecure key" + fi + fi + + return 0 +} + +# Sets the best and worst bit size key, used to grade Category 3 (Cipher Strength) +# This function itself doesn't actually set a score; its just in the name to keep it logical (score == grading function) +# arg1: a bit size +set_ciph_str_score() { + local size=$1 + + # Do nothing if disabled + "$DISABLE_GRADING" && return 0 + + [[ $size -gt $CIPH_STR_BEST ]] && let CIPH_STR_BEST=$size + [[ $size -lt $CIPH_STR_WORST ]] && let CIPH_STR_WORST=$size + + return 0 +} + ###### START ServerHello/OpenSSL/F5 function definitions ###### ###### END helper function definitions ###### @@ -1756,12 +1863,14 @@ check_revocation_crl() { out ", " pr_svrty_critical "revoked" fileout "$jsonID" "CRITICAL" "revoked" + set_grade_cap "T" "Certificate revoked" else retcode="$(verify_retcode_helper "$retcode")" out " $retcode" retcode="${retcode#(}" retcode="${retcode%)}" fileout "$jsonID" "WARN" "$retcode" + set_grade_cap "T" "Issues with certificate $retcode" if [[ $DEBUG -ge 2 ]]; then outln cat "${tmpfile%%.crl}.err" @@ -1823,6 +1932,7 @@ check_revocation_ocsp() { out ", " pr_svrty_critical "revoked" fileout "$jsonID" "CRITICAL" "revoked" + set_grade_cap "T" "Certificate revoked" else out ", " pr_warning "error querying OCSP responder" @@ -2441,15 +2551,18 @@ run_hsts() { if [[ $hsts_age_days -eq -1 ]]; then pr_svrty_medium "misconfiguration: HSTS max-age (recommended > $HSTS_MIN seconds = $((HSTS_MIN/86400)) days ) is required but missing" fileout "${jsonID}_time" "MEDIUM" "misconfiguration, parameter max-age (recommended > $HSTS_MIN seconds = $((HSTS_MIN/86400)) days) missing" + set_grade_cap "A" "HSTS max-age is misconfigured" elif [[ $hsts_age_sec -eq 0 ]]; then pr_svrty_low "HSTS max-age is set to 0. HSTS is disabled" fileout "${jsonID}_time" "LOW" "0. HSTS is disabled" + set_grade_cap "A" "HSTS is disabled" elif [[ $hsts_age_sec -gt $HSTS_MIN ]]; then pr_svrty_good "$hsts_age_days days" ; out "=$hsts_age_sec s" fileout "${jsonID}_time" "OK" "$hsts_age_days days (=$hsts_age_sec seconds) > $HSTS_MIN seconds" else pr_svrty_medium "$hsts_age_sec s = $hsts_age_days days is too short ( > $HSTS_MIN seconds recommended)" fileout "${jsonID}_time" "MEDIUM" "max-age too short. $hsts_age_days days (=$hsts_age_sec seconds) <= $HSTS_MIN seconds" + set_grade_cap "A" "HSTS max-age is too short" fi if includeSubDomains "$TMPFILE"; then fileout "${jsonID}_subdomains" "OK" "includes subdomains" @@ -2467,6 +2580,7 @@ run_hsts() { else pr_svrty_low "not offered" fileout "$jsonID" "LOW" "not offered" + set_grade_cap "A" "HSTS is not offered" fi outln @@ -2502,6 +2616,7 @@ run_hpkp() { first_hpkp_header="$(grep -ai '^Public-Key-Pins:' $TMPFILE | head -1)" # we only evaluate the keys here, unless they a not present out "$spaces " + set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)" elif [[ $(grep -aci '^Public-Key-Pins-Report-Only:' $TMPFILE) -gt 1 ]]; then outln "Multiple HPKP headers (Report-Only), taking first line" fileout "HPKP_notice" "INFO" "multiple Public-Key-Pins-Report-Only in header" @@ -2528,6 +2643,7 @@ run_hpkp() { if [[ $hpkp_nr_keys -eq 1 ]]; then pr_svrty_high "Only one key pinned (NOT ok), means the site may become unavailable in the future, " fileout "HPKP_SPKIs" "HIGH" "Only one key pinned" + set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)" else pr_svrty_good "$hpkp_nr_keys" out " keys, " @@ -2548,6 +2664,7 @@ run_hpkp() { out "$hpkp_age_sec s = " pr_svrty_medium "$hpkp_age_days days (< $HPKP_MIN s = $((HPKP_MIN / 86400)) days is not good enough)" fileout "HPKP_age" "MEDIUM" "age is set to $hpkp_age_days days ($hpkp_age_sec sec) < $HPKP_MIN s = $((HPKP_MIN / 86400)) days is not good enough." + set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)" fi if includeSubDomains "$TMPFILE"; then @@ -2705,11 +2822,13 @@ run_hpkp() { "$has_backup_spki" && out "$spaces" # we had a few lines with backup SPKIs already prln_svrty_high " No matching key for SPKI found " fileout "HPKP_SPKImatch" "HIGH" "None of the SPKI match your host certificate, intermediate CA or known root CAs. Bricked site?" + set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)" fi if ! "$has_backup_spki"; then prln_svrty_high " No backup keys found. Loss/compromise of the currently pinned key(s) will lead to bricked site. " fileout "HPKP_backup" "HIGH" "No backup keys found. Loss/compromise of the currently pinned key(s) will lead to bricked site." + set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)" fi else outln "--" @@ -3326,6 +3445,9 @@ neat_list(){ enc="${enc//POLY1305/}" # remove POLY1305 enc="${enc//\//}" # remove "/" + # For grading, set bits size + set_ciph_str_score $strength + [[ "$export" =~ export ]] && strength="$strength,exp" [[ "$DISPLAY_CIPHERNAMES" != openssl-only ]] && tls_cipher="$(show_rfc_style "$hexcode")" @@ -4986,6 +5108,7 @@ run_protocols() { if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) add_tls_offered ssl2 yes + set_grade_cap "F" "SSLv2 is offered" if [[ 0 -eq "$nr_ciphers_detected" ]]; then prln_svrty_high "supported but couldn't detect a cipher and vulnerable to CVE-2015-3197 "; fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310" @@ -5007,6 +5130,7 @@ run_protocols() { 0) prln_svrty_critical "offered (NOT ok)" fileout "$jsonID" "CRITICAL" "offered" add_tls_offered ssl2 yes + set_grade_cap "F" "SSLv2 is offered" ;; 1) prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" @@ -5015,6 +5139,7 @@ run_protocols() { 5) prln_svrty_high "CVE-2015-3197: $supported_no_ciph2"; fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310" add_tls_offered ssl2 yes + set_grade_cap "F" "SSLv2 is offered" ;; 7) prln_local_problem "$OPENSSL doesn't support \"s_client -ssl2\"" fileout "$jsonID" "INFO" "not tested due to lack of local support" @@ -5042,6 +5167,7 @@ run_protocols() { latest_supported_string="SSLv3" fi add_tls_offered ssl3 yes + set_grade_cap "B" "SSLv3 is offered" ;; 1) prln_svrty_best "not offered (OK)" fileout "$jsonID" "OK" "not offered" @@ -5077,6 +5203,7 @@ run_protocols() { 5) pr_svrty_high "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "HIGH" "$supported_no_ciph1" add_tls_offered ssl3 yes + set_grade_cap "B" "SSLv3 is offered" ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5108,6 +5235,7 @@ run_protocols() { latest_supported="0301" latest_supported_string="TLSv1.0" add_tls_offered tls1 yes + set_grade_cap "B" "TLS1.0 offered" ;; # nothing wrong with it -- per se 1) out "not offered" add_tls_offered tls1 no @@ -5154,6 +5282,7 @@ run_protocols() { 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" add_tls_offered tls1 yes + set_grade_cap "B" "TLS1.0 offered" ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5186,6 +5315,7 @@ run_protocols() { latest_supported="0302" latest_supported_string="TLSv1.1" add_tls_offered tls1_1 yes + set_grade_cap "B" "TLS1.1 offered" ;; # nothing wrong with it 1) out "not offered" add_tls_offered tls1_1 no @@ -5235,6 +5365,7 @@ run_protocols() { 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" add_tls_offered tls1_1 yes + set_grade_cap "B" "TLS1.1 offered" ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5299,6 +5430,7 @@ run_protocols() { add_tls_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! 1) add_tls_offered tls1_2 no + set_grade_cap "C" "TLS1.2 is not offered" if "$offers_tls13"; then out "not offered" else @@ -5317,6 +5449,7 @@ run_protocols() { fi ;; 2) add_tls_offered tls1_2 no + set_grade_cap "C" "TLS1.2 is not offered" pr_svrty_medium "not offered and downgraded to a weaker protocol" if [[ "$tls12_detected_version" == 0300 ]]; then detected_version_string="SSLv3" @@ -5345,12 +5478,15 @@ run_protocols() { 3) out "not offered, " fileout "$jsonID" "INFO" "not offered" add_tls_offered tls1_2 no + set_grade_cap "C" "TLS1.2 is not offered" pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" + set_grade_cap "C" "TLS1.2 is not offered" ;; 4) out "likely "; pr_svrty_medium "not offered, " fileout "$jsonID" "MEDIUM" "not offered" add_tls_offered tls1_2 no + set_grade_cap "C" "TLS1.2 is not offered" pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; @@ -5717,6 +5853,8 @@ sub_cipherlists() { ((ret++)) ;; esac + + [[ $sclient_success -eq 0 && "$1" =~ (^|:)EXPORT(:|$) ]] && set_grade_cap "F" "Export suite offered" fi tmpfile_handle ${FUNCNAME[0]}.${5}.txt [[ $DEBUG -ge 1 ]] && tm_out " -- $1" @@ -6991,17 +7129,17 @@ verify_retcode_helper() { case $retcode in # codes from ./doc/apps/verify.pod | verify(1ssl) - 44) tm_out "(different CRL scope)" ;; # X509_V_ERR_DIFFERENT_CRL_SCOPE - 26) tm_out "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE - 24) tm_out "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA - 23) tm_out "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED - 21) tm_out "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE - 20) tm_out "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY - 19) tm_out "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN - 18) tm_out "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT - 10) tm_out "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED - 9) tm_out "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID - 2) tm_out "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT + 44) tm_out "(different CRL scope)" ;; # X509_V_ERR_DIFFERENT_CRL_SCOPE + 26) tm_out "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE + 24) tm_out "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA + 23) tm_out "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED + 21) tm_out "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE + 20) tm_out "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY + 19) tm_out "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + 18) tm_out "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + 10) tm_out "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED + 9) tm_out "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID + 2) tm_out "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT *) ret=1 ; tm_out " (unknown, pls report) $1" ;; esac return $ret @@ -7097,6 +7235,7 @@ determine_trust() { out "$code" fi fileout "${jsonID}${json_postfix}" "CRITICAL" "failed $code. $addtl_warning" + set_grade_cap "T" "Issues with certificate $code" else # is one ok and the others not ==> display the culprit store if "$some_ok"; then @@ -7115,6 +7254,7 @@ determine_trust() { out "$code" fi notok_was="${certificate_file[i]} $code $notok_was" + set_grade_cap "T" "Issues with certificate $code" fi done #pr_svrty_high "$notok_was " @@ -8232,6 +8372,7 @@ certificate_info() { fi outln fileout "${jsonID}${json_postfix}" "MEDIUM" "SHA1 with RSA" + set_grade_cap "T" "Uses SHA1 algorithm" ;; sha224WithRSAEncryption) outln "SHA224 with RSA" @@ -8252,6 +8393,7 @@ certificate_info() { ecdsa-with-SHA1) prln_svrty_medium "ECDSA with SHA1" fileout "${jsonID}${json_postfix}" "MEDIUM" "ECDSA with SHA1" + set_grade_cap "T" "Uses SHA1 algorithm" ;; ecdsa-with-SHA224) outln "ECDSA with SHA224" @@ -8272,6 +8414,7 @@ certificate_info() { dsaWithSHA1) prln_svrty_medium "DSA with SHA1" fileout "${jsonID}${json_postfix}" "MEDIUM" "DSA with SHA1" + set_grade_cap "T" "Uses SHA1 algorithm" ;; dsa_with_SHA224) outln "DSA with SHA224" @@ -8287,6 +8430,7 @@ certificate_info() { sha1) prln_svrty_medium "RSASSA-PSS with SHA1" fileout "${jsonID}${json_postfix}" "MEDIUM" "RSASSA-PSS with SHA1" + set_grade_cap "T" "Uses SHA1 algorithm" ;; sha224) outln "RSASSA-PSS with SHA224" @@ -8313,6 +8457,7 @@ certificate_info() { md2*) prln_svrty_critical "MD2" fileout "${jsonID}${json_postfix}" "CRITICAL" "MD2" + set_grade_cap "F" "Supports a insecure signature (MD2)" ;; md4*) prln_svrty_critical "MD4" @@ -8321,6 +8466,7 @@ certificate_info() { md5*) prln_svrty_critical "MD5" fileout "${jsonID}${json_postfix}" "CRITICAL" "MD5" + set_grade_cap "F" "Supports a insecure signature (MD5)" ;; *) out "$cert_sig_algo (" @@ -8375,6 +8521,8 @@ certificate_info() { ((ret++)) fi outln " bits" + + set_key_str_score "$short_keyAlgo" "$cert_keysize" # TODO: should be $dh_param_size elif [[ $cert_key_algo =~ RSA ]] || [[ $cert_key_algo =~ rsa ]] || [[ $cert_key_algo =~ dsa ]] || \ [[ $cert_key_algo =~ dhKeyAgreement ]] || [[ $cert_key_algo == X9.42\ DH ]]; then if [[ "$cert_keysize" -le 512 ]]; then @@ -8401,6 +8549,8 @@ certificate_info() { fileout "${jsonID}${json_postfix}" "WARN" "$cert_keysize bits (Odd)" ((ret++)) fi + + set_key_str_score "$short_keyAlgo" "$cert_keysize" else out "$cert_key_algo + $cert_keysize bits (" pr_warning "FIXME: can't tell whether this is good or not" @@ -8567,6 +8717,7 @@ certificate_info() { if [[ "$issuer_O" == "issuer=" ]] || [[ "$issuer_O" == "issuer= " ]] || [[ "$issuer_CN" == "$cn" ]]; then prln_svrty_critical "self-signed (NOT ok)" fileout "${jsonID}${json_postfix}" "CRITICAL" "selfsigned" + set_grade_cap "T" "Self-signed certificate" else issuerfinding="$issuer_CN" pr_italic "$issuer_CN" @@ -8610,7 +8761,9 @@ certificate_info() { has_dns_sans=$HAS_DNS_SANS case $trust_sni in - 0) trustfinding="certificate does not match supplied URI" ;; + 0) trustfinding="certificate does not match supplied URI" + set_grade_cap "M" "Domain name mismatch" + ;; 1) trustfinding="Ok via SAN" ;; 2) trustfinding="Ok via SAN wildcard" ;; 4) if "$has_dns_sans"; then @@ -8715,6 +8868,7 @@ certificate_info() { # Shortcut for this special case here. pr_italic "WoSign/StartCom"; out " are " ; prln_svrty_critical "not trusted anymore (NOT ok)" fileout "${jsonID}${json_postfix}" "CRITICAL" "Issuer not trusted anymore (WoSign/StartCom)" + set_grade_cap "T" "Untrusted certificate chain" else # Also handles fileout, keep error if happened determine_trust "$jsonID" "$json_postfix" || ((ret++)) @@ -8807,6 +8961,7 @@ certificate_info() { pr_svrty_critical "expired" expfinding="expired" expok="CRITICAL" + set_grade_cap "T" "Certificate expired" else secs2warn=$((24 * 60 * 60 * days2warn2)) # low threshold first expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE) @@ -9603,6 +9758,7 @@ run_fs() { outln prln_svrty_medium " No ciphers supporting Forward Secrecy offered" fileout "$jsonID" "MEDIUM" "No ciphers supporting (P)FS offered" + set_grade_cap "B" "Forward Security (PFS) is not supported" else outln fs_offered=true @@ -14973,6 +15129,7 @@ run_heartbleed(){ else pr_svrty_critical "VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE $cve" "$cwe" "$hint" + set_grade_cap "F" "Vulnerable to Heartbleed" fi else pr_svrty_best "not vulnerable (OK)" @@ -15131,6 +15288,7 @@ run_ccs_injection(){ # decryption failed received pr_svrty_critical "VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" + set_grade_cap "F" "Vulnerable to CCS injection" elif [[ "$byte6" == "0A" ]] || [[ "$byte6" == "28" ]]; then # Unexpected message / Handshake failure received pr_warning "likely " @@ -15428,6 +15586,7 @@ run_ticketbleed() { if [[ ${memory[1]} != ${memory[2]} ]] && [[ ${memory[2]} != ${memory[3]} ]]; then pr_svrty_critical "VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" + set_grade_cap "F" "Vulnerable to Ticketbleed" else pr_svrty_best "not vulnerable (OK)" out ", session IDs were returned but potential memory fragments do not differ" @@ -15485,6 +15644,7 @@ run_renego() { case $sec_renego in 0) prln_svrty_critical "Not supported / VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint" + set_grade_warning "Secure renegotiation is not supported" ;; 1) prln_svrty_best "supported (OK)" fileout "$jsonID" "OK" "supported" "$cve" "$cwe" @@ -15668,6 +15828,7 @@ run_crime() { # not clear whether a protocol != HTTP offers the ability to repeatedly modify the input # which is done e.g. via javascript in the context of HTTP fi + set_grade_cap "C" "Vulnerable to CRIME" fi outln @@ -15800,6 +15961,7 @@ run_sweet32() { local -i nr_sweet32_ciphers=0 nr_supported_ciphers=0 nr_ssl2_sweet32_ciphers=0 nr_ssl2_supported_ciphers=0 local ssl2_sweet=false local using_sockets=true + local tls1_1_vulnable=false [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for SWEET32 (Birthday Attacks on 64-bit Block Ciphers) " && outln pr_bold " SWEET32"; out " (${cve// /, }) " @@ -15860,6 +16022,7 @@ run_sweet32() { sclient_connect_successful $? $TMPFILE sclient_success=$? [[ $DEBUG -ge 2 ]] && grep -Eq "error|failure" $ERRFILE | grep -Eav "unable to get local|verify error" + [[ $proto == -tls1_1 && $sclient_success -eq 0 ]] && tls1_1_vulnable=true [[ $sclient_success -eq 0 ]] && break done if "$HAS_SSL2"; then @@ -15879,9 +16042,11 @@ run_sweet32() { if [[ $sclient_success -eq 0 ]] && "$ssl2_sweet" ; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers for SSLv2 and above" fileout "SWEET32" "LOW" "uses 64 bit block ciphers for SSLv2 and above" "$cve" "$cwe" "$hint" + "$tls1_1_vulnable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS1.1+ (vulnerable to SWEET32)" elif [[ $sclient_success -eq 0 ]]; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers" fileout "SWEET32" "LOW" "uses 64 bit block ciphers" "$cve" "$cwe" "$hint" + "$tls1_1_vulnable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS1.1+ (vulnerable to SWEET32)" elif "$ssl2_sweet"; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers wth SSLv2 only" fileout "SWEET32" "LOW" "uses 64 bit block ciphers with SSLv2 only" "$cve" "$cwe" "$hint" @@ -15963,6 +16128,7 @@ run_ssl_poodle() { POODLE=0 pr_svrty_high "VULNERABLE (NOT ok)"; out ", uses SSLv3+CBC (check TLS_FALLBACK_SCSV mitigation below)" fileout "$jsonID" "HIGH" "VULNERABLE, uses SSLv3+CBC" "$cve" "$cwe" "$hint" + set_grade_cap "C" "Vulnerable to POODLE" else POODLE=1 pr_svrty_best "not vulnerable (OK)"; @@ -15993,6 +16159,8 @@ run_tls_poodle() { #FIXME prln_warning "#FIXME" fileout "$jsonID" "WARN" "Not yet implemented #FIXME" "$cve" "$cwe" + # set_grade_cap "F" "Vulnerable to POODLE TLS" + return 0 } @@ -16021,6 +16189,7 @@ run_tls_fallback_scsv() { if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then prln_svrty_critical "No fallback possible, SSLv2 is the only protocol" fileout "$jsonID" "CRITICAL" "SSLv2 is the only protocol" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 fi for p in tls1_2 tls1_1 tls1 ssl3; do @@ -16049,6 +16218,7 @@ run_tls_fallback_scsv() { "ssl3") prln_svrty_high "No fallback possible, SSLv3 is the only protocol" fileout "$jsonID" "HIGH" "only SSLv3 supported" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 ;; *) if [[ $(has_server_protocol tls1_3) -eq 0 ]]; then @@ -16056,6 +16226,7 @@ run_tls_fallback_scsv() { # then assume it does not support SSLv3, even if SSLv3 cannot be tested. pr_svrty_good "No fallback possible (OK)"; outln ", TLS 1.3 is the only protocol" fileout "$jsonID" "OK" "only TLS 1.3 supported" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" elif [[ $(has_server_protocol tls1_3) -eq 1 ]] && \ ( [[ $(has_server_protocol ssl3) -eq 1 ]] || "$HAS_SSL3" ); then # TLS 1.3, TLS 1.2, TLS 1.1, TLS 1, and SSLv3 are all not supported. @@ -16069,6 +16240,7 @@ run_tls_fallback_scsv() { # it is very likely that SSLv3 is the only supported protocol. pr_svrty_high "NOT ok, no fallback possible"; outln ", TLS 1.3, 1.2, 1.1 and 1.0 not supported" fileout "$jsonID" "HIGH" "TLS 1.3, 1.2, 1.1, 1.0 not supported" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" else # TLS 1.2, TLS 1.1, and TLS 1 are not supported, but can't tell whether TLS 1.3 is supported. # This could be a TLS 1.3 only server, an SSLv3 only server (if SSLv3 support cannot be tested), @@ -16076,6 +16248,7 @@ run_tls_fallback_scsv() { # since this could either be good or bad. outln "No fallback possible, TLS 1.2, TLS 1.1, and TLS 1 not supported" fileout "$jsonID" "INFO" "TLS 1.2, TLS 1.1, and TLS 1 not supported" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" fi return 0 esac @@ -16120,6 +16293,7 @@ run_tls_fallback_scsv() { ;; esac fileout "$jsonID" "OK" "no protocol below $high_proto_str offered" + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 fi case "$low_proto" in @@ -16140,15 +16314,18 @@ run_tls_fallback_scsv() { if [[ -z "$POODLE" ]]; then pr_warning "Rerun including POODLE SSL check. " pr_svrty_medium "Downgrade attack prevention NOT supported" - fileout "$jsonID" "WARN" "NOT supported. Pls rerun wity POODLE SSL check" + fileout "$jsonID" "WARN" "NOT supported. Pls rerun with POODLE SSL check" ret=1 elif [[ "$POODLE" -eq 0 ]]; then pr_svrty_high "Downgrade attack prevention NOT supported and vulnerable to POODLE SSL" fileout "$jsonID" "HIGH" "NOT supported and vulnerable to POODLE SSL" + set_grade_cap "C" "Vulnerable to POODLE" else pr_svrty_medium "Downgrade attack prevention NOT supported" fileout "$jsonID" "MEDIUM" "NOT supported" fi + set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" + elif grep -qa "alert inappropriate fallback" "$TMPFILE"; then pr_svrty_good "Downgrade attack prevention supported (OK)" fileout "$jsonID" "OK" "supported" @@ -16493,6 +16670,7 @@ run_logjam() { if "$vuln_exportdh_ciphers"; then pr_svrty_high "VULNERABLE (NOT ok):"; out " uses DH EXPORT ciphers" fileout "$jsonID" "HIGH" "VULNERABLE, uses DH EXPORT ciphers" "$cve" "$cwe" "$hint" + set_grade_cap "B" "Uses weak DH key exchange parameters (vulnerable to LOGJAM)" if [[ $subret -eq 3 ]]; then out ", no DH key detected with <= TLS 1.2" fileout "$jsonID2" "OK" "no DH key detected with <= TLS 1.2" @@ -16508,6 +16686,7 @@ run_logjam() { else if [[ $subret -eq 1 ]]; then out_common_prime "$jsonID2" "$cve" "$cwe" + set_grade_cap "B" "Uses weak DH key exchange parameters (vulnerable to LOGJAM)" if ! "$openssl_no_expdhciphers"; then outln "," out "${spaces}but no DH EXPORT ciphers${addtl_warning}" @@ -16605,6 +16784,7 @@ run_drown() { else prln_svrty_critical "VULNERABLE (NOT ok), SSLv2 offered with $nr_ciphers_detected ciphers"; fileout "$jsonID" "CRITICAL" "VULNERABLE, SSLv2 offered with $nr_ciphers_detected ciphers. Make sure you don't use this certificate elsewhere, see https://censys.io/ipv4?q=$cert_fingerprint_sha2" "$cve" "$cwe" "$hint" + set_grade_cap "F" "Vulnerable to DROWN" fi outln "$spaces Make sure you don't use this certificate elsewhere, see:" out "$spaces " @@ -16918,6 +17098,7 @@ run_beast(){ pr_svrty_medium "VULNERABLE" outln " -- and no higher protocols as mitigation supported" fileout "$jsonID" "MEDIUM" "VULNERABLE -- and no higher protocols as mitigation supported" "$cve" "$cwe" "$hint" + set_grade_cap "B" "Vulnerable to BEAST" fi fi "$first" && ! "$vuln_beast" && prln_svrty_good "no CBC ciphers found for any protocol (OK)" @@ -17165,6 +17346,11 @@ run_rc4() { fi "$WIDE" && "$SHOW_SIGALGO" && grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TMPFILE && \ sigalg[i]="$(read_sigalg_from_file "$TMPFILE")" + + # If you use RC4 with newer protocols, you are punished harder + if [[ "$proto" == "-tls1_1" ]]; then + set_grade_cap "C" "RC4 ciphers offered on TLS1.1" + fi done done @@ -17245,6 +17431,7 @@ run_rc4() { outln "$WIDE" && out " " && prln_svrty_high "VULNERABLE (NOT ok)" fileout "$jsonID" "HIGH" "VULNERABLE, Detected ciphers: $rc4_detected" "$cve" "$cwe" "$hint" + set_grade_cap "B" "RC4 ciphers offered" elif [[ $nr_ciphers -eq 0 ]]; then prln_local_problem "No RC4 Ciphers configured in $OPENSSL" fileout "$jsonID" "WARN" "RC4 ciphers not supported by local OpenSSL ($OPENSSL)" @@ -17889,6 +18076,7 @@ run_robot() { prln_svrty_critical "VULNERABLE (NOT ok)" fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" fi + set_grade_cap "F" "Vulnerable to ROBOT" else prln_svrty_best "not vulnerable (OK)" fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe" @@ -18383,6 +18571,7 @@ output options (can also be preset via environment variables): --color <0|1|2|3> 0: no escape or other codes, 1: b/w escape codes, 2: color (default), 3: extra color (color all ciphers) --colorblind swap green and blue in the output --debug <0-6> 1: screen output normal but keeps debug output in /tmp/. 2-6: see "grep -A 5 '^DEBUG=' testssl.sh" + --disable-grading Explicitly disables the grading output file output options (can also be preset via environment variables) --log, --logging logs stdout to '\${NODE}-p\${port}\${YYYYMMDD-HHMM}.log' in current working directory (cwd) @@ -20357,6 +20546,215 @@ run_mass_testing_parallel() { return $? } +run_grading() { + local final_score pre_cap_grade final_grade + local c1_score c2_score c3_score c1_wscore c2_wscore c3_wscore + local c1_worst c1_best + local c3_worst c3_best c3_worst_cb c3_best_cb + local old_ifs=$IFS sorted_reasons sorted_warnings reason_loop=0 warning_loop=0 + + # Sort the reasons. This is just nicer to read in genereal + IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) + IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}")) + IFS=$old_ifs + fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' (revision 2009q) (near complete)" + pr_bold " Grading specification "; out "SSLLabs's 'SSL Server Rating Guide' (revision 2009q)"; prln_warning " (near complete)" + pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" + outln + + # No point in calculating a score, if a cap of "F", "T", or "M" has been set + if [[ $GRADE_CAP == "F" || $GRADE_CAP == "T" || $GRADE_CAP == "M" ]]; then + pr_bold " Protocol Support "; out "(weighted) "; outln "0 (0)" + pr_bold " Key Exchange "; out " (weighted) "; outln "0 (0)" + pr_bold " Cipher Stregth "; out " (weighted) "; outln "0 (0)" + pr_bold " Final Score "; outln "0" + pr_bold " Grade "; prln_svrty_critical "$GRADE_CAP" + fileout "grade" "CRITICAL" "$GRADE_CAP" + outln + else + ## Category 1 + # get best score, by searching for the best protocol, until a hit occurs + if [[ $(has_server_protocol "tls1_3") -eq 0 || $(has_server_protocol "tls1_2") -eq 0 ]]; then + c1_best=100 + elif [[ $(has_server_protocol "tls1_1") -eq 0 ]]; then + c1_best=95 + elif [[ $(has_server_protocol "tls1") -eq 0 ]]; then + c1_best=90 + elif [[ $(has_server_protocol "ssl3") -eq 0 ]]; then + c1_best=80 + # If the best protocol offered is SSLv3, cap to F. It is easier done here + set_grade_cap "F" "SSLv3 is the best protocol offered" + else # SSLv2 gives 0 points + c1_best=0 + fi + + # get worst score, by searching for the worst protcol, until a hit occurs + if [[ $(has_server_protocol "ssl2") -eq 0 ]]; then + c1_worst=0 + elif [[ $(has_server_protocol "ssl3") -eq 0 ]]; then + c1_worst=80 + elif [[ $(has_server_protocol "tls1") -eq 0 ]]; then + c1_worst=90 + elif [[ $(has_server_protocol "tls1_1") -eq 0 ]]; then + c1_worst=95 + else # TLS1.2 and TLS1.3 both give 100 points + c1_worst=100 + fi + + let c1_score="($c1_best+$c1_worst)/2" # Gets the category score + let c1_wscore=$c1_score*30/100 # Gets the weighted score for category (30%) + + pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)" + + ## Category 2 + let c2_score=$KEY_EXCH_SCORE + let c2_wscore=$c2_score*30/100 + + pr_bold " Key Exchange "; out " (weighted) "; outln "$c2_score ($c2_wscore)" + + + ## Category 3 + # Get the cipher bits sizes for the best cipher, and the worst cipher + c3_best_cb=$CIPH_STR_BEST + c3_worst_cb=$CIPH_STR_WORST + + # Determine score for the best key + if [[ $c3_best_cb -ge 256 ]]; then + c3_best=100 + elif [[ $c3_best_cb -ge 128 ]]; then + c3_best=80 + elif [[ $c3_best_cb -ge 0 ]]; then + c3_best=20 + else + c3_best=0 + fi + + # Determine the score for the worst key + if [[ $c3_worst_cb -gt 0 && $c3_worst_cb -lt 128 ]]; then + c3_worst=20 + elif [[ $c3_worst_cb -lt 256 ]]; then + c3_worst=80 + elif [[ $c3_worst_cb -ge 256 ]]; then + c3_worst=100 + else + c3_worst=0 + fi + let c3_score="($c3_best+$c3_worst)/2" # Gets the category score + let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) + + pr_bold " Cipher Stregth "; out " (weighted) "; outln "$c3_score ($c3_wscore)" + + ## Calculate final score and grade + let final_score=$c1_wscore+$c2_wscore+$c3_wscore + + pr_bold " Final Score "; outln $final_score + + # get score, and somehow do something about the GRADE_CAP + if [[ $final_score -ge 80 ]]; then + pre_cap_grade="A" + elif [[ $final_score -ge 65 ]]; then + pre_cap_grade="B" + elif [[ $final_score -ge 50 ]]; then + pre_cap_grade="C" + elif [[ $final_score -ge 35 ]]; then + pre_cap_grade="D" + elif [[ $final_score -ge 20 ]]; then + pre_cap_grade="E" + elif [[ $final_score -lt 20 ]]; then + pre_cap_grade="F" + fi + + # If the calculated grade is bigger than the grade cap, then set grade as the cap + if [[ $GRADE_CAP != "" && ! $pre_cap_grade > $GRADE_CAP ]]; then + final_grade=$GRADE_CAP + # For "exceptional" config, an "A+" is awarded, or "A-" for slightly less "exceptional" + elif [[ $GRADE_CAP == "" && $pre_cap_grade == "A" ]]; then + if [[ ${#sorted_warnings[@]} -eq 0 ]]; then + final_grade="A+" + else + final_grade="A-" + fi + else + final_grade=$pre_cap_grade + fi + + case "$final_grade" in + "A"*) pr_bold " Grade " + prln_svrty_best $final_grade + fileout "grade" "OK" "$final_grade" + ;; + "B") pr_bold " Grade " + prln_svrty_medium $final_grade + fileout "grade" "MEDIUM" "$final_grade" + ;; + "C") pr_bold " Grade " + prln_svrty_medium $final_grade + fileout "grade" "MEDIUM" "$final_grade" + ;; + "D") pr_bold " Grade " + prln_svrty_high $final_grade + fileout "grade" "HIGH" "$final_grade" + ;; + "E") pr_bold " Grade " + prln_svrty_high $final_grade + fileout "grade" "HIGH" "$final_grade" + ;; + "F") pr_bold " Grade " + prln_svrty_critical $final_grade + fileout "grade" "CRITICAL" "$final_grade" + ;; + esac + fi + + # Pretty print - again, it's just nicer to read + for reason in "${sorted_reasons[@]}"; do + if [[ $reason_loop -eq 0 ]]; then + pr_bold " Grade cap reasons "; outln "$reason" + let reason_loop++ + else + outln " $reason" + fi + done + + for warning in "${sorted_warnings[@]}"; do + if [[ $warning_loop -eq 0 ]]; then + pr_bold " Grade warning "; prln_svrty_medium "$warning" + let warning_loop++ + else + prln_svrty_medium " $warning" + fi + done + + return 0 +} + +# Checks whether grading can be done or not. +# Grading needs a mix of certificate and vulnerabilities checks, in order to give out a proper grade. +# This function disables grading, if not all required checks are enabled +# Returns "0" if grading is enabled, and "1" if grading is disabled +set_grading_state() { + local gbl + local nr_enabled=0 + + # All of these should be enabled + for gbl in do_protocols do_cipherlists do_fs do_server_defaults do_header \ + do_heartbleed do_ccs_injection do_ticketbleed do_robot do_renego \ + do_crime do_ssl_poodle do_tls_fallback_scsv do_drown do_beast \ + do_rc4 do_logjam; do + [[ "${!gbl}" == true ]] && let nr_enabled++ + done + + # ... atleast one of these has to be set + [[ $do_allciphers == true || $do_cipher_per_proto == true ]] && let nr_enabled++ + + # ... else we can't grade + if [[ $nr_enabled -lt 18 ]]; then + DISABLE_GRADING=true + return 1 + fi + + return 0 +} # This initializes boolean global do_* variables. They keep track of what to do @@ -20722,6 +21120,9 @@ parse_cmd_line() { -g|--grease) do_grease=true ;; + --disable-grading) + DISABLE_GRADING=true + ;; -9|--full) set_scanning_defaults do_allciphers=false @@ -21005,9 +21406,9 @@ parse_cmd_line() { SSL_NATIVE=true ;; --basicauth|--basicauth=*) - BASICAUTH="$(parse_opt_equal_sign "$1" "$2")" - [[ $? -eq 0 ]] && shift - ;; + BASICAUTH="$(parse_opt_equal_sign "$1" "$2")" + [[ $? -eq 0 ]] && shift + ;; (--) shift break ;; @@ -21045,6 +21446,11 @@ parse_cmd_line() { count_do_variables [[ $? -eq 0 ]] && set_scanning_defaults + + # Unless explicit disabled, check if grading can be enabled + # Should be called after set_scanning_defaults + "$DISABLE_GRADING" || set_grading_state + CMDLINE_PARSED=true } @@ -21213,6 +21619,14 @@ lets_roll() { fileout_section_header $section_number true && ((section_number++)) "$do_client_simulation" && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; } + + if ! "$DISABLE_GRADING"; then + outln; pr_headlineln " Calculating grade " + outln + + fileout_section_header $section_number true && ((section_number++)) + { run_grading; ret=$(($? + ret)); stopwatch run_grading; } + fi fi fileout_section_footer true fi From 0a1b632ddcc5f74652d219db5e7e2630573d14d1 Mon Sep 17 00:00:00 2001 From: Pete Cooper Date: Wed, 15 Apr 2020 16:10:11 +0100 Subject: [PATCH 025/211] Update Readme.md Bump version for zip download --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index fdceef6..7454f79 100644 --- a/Readme.md +++ b/Readme.md @@ -52,7 +52,7 @@ You can download testssl.sh by cloning this git repository: git clone --depth 1 https://github.com/drwetter/testssl.sh.git -Or help yourself downloading the 3.0 ZIP archive [https://github.com/drwetter/testssl.sh/archive/3.0.zip](https://github.com/drwetter/testssl.sh/archive/3.0.zip). Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. +Or help yourself downloading the 3.0 ZIP archive [https://github.com/drwetter/testssl.sh/archive/3.0.1.zip](https://github.com/drwetter/testssl.sh/archive/3.0.1.zip). Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. #### Docker From 64735d0241fe1fffa8eb0e3168dd8b7edbf68926 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 17 Apr 2020 13:22:30 +0200 Subject: [PATCH 026/211] Remove env variable DISABLE_GRADING as for run_* functions we currntly don't have that. Also AEAD as WIP we can remove that from the doc --- doc/testssl.1.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 696d9f3..170d426 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -288,7 +288,7 @@ Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and 6. whole 9 yards `--disable-grading` disables grading explicitly. -Grading automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). `DISABLE_GRADING` is the according environment variable which you can use. +Grading automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). ### FILE OUTPUT OPTIONS @@ -394,7 +394,6 @@ This is *not* a reimplementation of the [SSLLab's SSL Server Test](https://www.s Disclaimer: Having a good grade does **NOT** necessary equal to having good security! Never rely solely on a good grade! As of writing, these checks are missing: -* Authenticated encryption (AEAD) - should be graded **B** if not supported * GOLDENDOODLE - should be graded **F** if vulnerable * Insecure renegotiation - should be graded **F** if vulnerable * Padding oracle in AES-NI CBC MAC check (CVE-2016-2107) - should be graded **F** if vulnerable @@ -409,7 +408,7 @@ As of writing, these checks are missing: * Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()` #### Implementing new grades caps or -warnings -To implement at new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: +To implement a new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: ```bash set_grade_cap "D" "Vulnerable to documentation" ``` From 359965dc17b7d111a49d0abc83195d7698944767 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 17 Apr 2020 13:24:32 +0200 Subject: [PATCH 027/211] First round of polishing @magnuslarsen's contribution * instead of DISABLE_GRADING we use do_grading as for run_* functions we currently don't support global variables * Add AEAD cipher set_grade_cap (needs to be tested though) * remove redundant quotes * be to be safe add double quotes at other places * Fix typos * Polishing output Tasks (not complete): * Review whether it is rated as intended * Do we want to mofify SSL Lab's rating? (SSLv3 e.g., T for SHA1 certificate?) * Does JSON output work? * TLS 1.3 only server are not rated properly --> wait for SSLlabs? * SWEET32: rating refers to TLS 1.1 atm. SSLlabs docu doesn't give a hint (is their docu incomplete?) * Rating for STARTTLS at all? --- testssl.sh | 137 ++++++++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 71 deletions(-) diff --git a/testssl.sh b/testssl.sh index c540e33..bfef981 100755 --- a/testssl.sh +++ b/testssl.sh @@ -227,7 +227,6 @@ fi DISPLAY_CIPHERNAMES="openssl" # display OpenSSL ciphername (but both OpenSSL and RFC ciphernames in wide mode) declare -r UA_STD="TLS tester from $SWURL" declare -r UA_SNEAKY="Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" -DISABLE_GRADING=${DISABLE_GRADING:-false} # Whether to disable grading, or not ########### Initialization part, further global vars just being declared here # @@ -990,34 +989,33 @@ f5_port_decode() { echo $((16#${tmp:2:2}${tmp:0:2})) # reverse order and convert it from hex to dec } +###### END universal helper function definitions ###### + + +###### START scoring function definitions ###### + # Sets the grade cap to ARG1 # arg1: A grade to set ("A", "B", "C", "D", "E", "F", "M", or "T") # arg2: A reason why (e.g. "Vulnerable to CRIME") set_grade_cap() { - # Do nothing if disabled - "$DISABLE_GRADING" && return 0 - + "$do_grading" || return 0 GRADE_CAP_REASONS+=("Grade capped to $1. $2") # Always set special attributes. These are hard caps, due to name mismatch or cert being invalid - if [[ "$1" == "T" || "$1" == "M" ]]; then - GRADE_CAP=$1 + if [[ "$1" == T || "$1" == M ]]; then + GRADE_CAP="$1" # Only keep track of the lowest grade cap, since a higher grade cap wont do anything (F = lowest, A = highest) elif [[ ! "$GRADE_CAP" > "$1" ]]; then - GRADE_CAP=$1 + GRADE_CAP="$1" fi - return 0 } # Sets a grade warning, as specified by the grade specification # arg1: A warning message set_grade_warning() { - # Do nothing if disabled - "$DISABLE_GRADING" && return 0 - + "$do_grading" || return 0 GRADE_WARNINGS+=("$1") - return 0 } @@ -1028,18 +1026,17 @@ set_key_str_score() { local type=$1 local size=$2 - # Do nothing if disabled - "$DISABLE_GRADING" && return 0 + "$do_grading" || return 0 # TODO: We need to get the size of DH params (follows the same table as the "else" clause) # For now, verifying the key size will do... - if [[ $type == "EC" || $type == "DH" ]]; then + if [[ $type == EC || $type == DH ]]; then if [[ $size -lt 110 ]]; then let KEY_EXCH_SCORE=20 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" elif [[ $size -lt 123 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" elif [[ $size -lt 163 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak key" @@ -1049,15 +1046,15 @@ set_key_str_score() { let KEY_EXCH_SCORE=100 else let KEY_EXCH_SCORE=0 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" fi else if [[ $size -lt 512 ]]; then let KEY_EXCH_SCORE=20 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" elif [[ $size -lt 1024 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" elif [[ $size -lt 2048 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak key" @@ -1067,10 +1064,9 @@ set_key_str_score() { let KEY_EXCH_SCORE=100 else let KEY_EXCH_SCORE=0 - set_grade_cap "F" "Using a insecure key" + set_grade_cap "F" "Using an insecure key" fi fi - return 0 } @@ -1080,18 +1076,14 @@ set_key_str_score() { set_ciph_str_score() { local size=$1 - # Do nothing if disabled - "$DISABLE_GRADING" && return 0 + "$do_grading" || return 0 [[ $size -gt $CIPH_STR_BEST ]] && let CIPH_STR_BEST=$size [[ $size -lt $CIPH_STR_WORST ]] && let CIPH_STR_WORST=$size - return 0 } -###### START ServerHello/OpenSSL/F5 function definitions ###### -###### END helper function definitions ###### - +###### END scoring function definitions ###### ##################### START output file formatting functions ######################### #################### START JSON file functions #################### @@ -5235,7 +5227,7 @@ run_protocols() { latest_supported="0301" latest_supported_string="TLSv1.0" add_tls_offered tls1 yes - set_grade_cap "B" "TLS1.0 offered" + set_grade_cap "B" "TLS 1.0 offered" ;; # nothing wrong with it -- per se 1) out "not offered" add_tls_offered tls1 no @@ -5282,7 +5274,7 @@ run_protocols() { 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" add_tls_offered tls1 yes - set_grade_cap "B" "TLS1.0 offered" + set_grade_cap "B" "TLS 1.0 offered" ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5315,7 +5307,7 @@ run_protocols() { latest_supported="0302" latest_supported_string="TLSv1.1" add_tls_offered tls1_1 yes - set_grade_cap "B" "TLS1.1 offered" + set_grade_cap "B" "TLS 1.1 offered" ;; # nothing wrong with it 1) out "not offered" add_tls_offered tls1_1 no @@ -5365,7 +5357,7 @@ run_protocols() { 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" add_tls_offered tls1_1 yes - set_grade_cap "B" "TLS1.1 offered" + set_grade_cap "B" "TLS 1.1 offered" ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5430,7 +5422,7 @@ run_protocols() { add_tls_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! 1) add_tls_offered tls1_2 no - set_grade_cap "C" "TLS1.2 is not offered" + set_grade_cap "C" "TLS 1.2 is not offered" if "$offers_tls13"; then out "not offered" else @@ -5449,7 +5441,7 @@ run_protocols() { fi ;; 2) add_tls_offered tls1_2 no - set_grade_cap "C" "TLS1.2 is not offered" + set_grade_cap "C" "TLS 1.2 is not offered" pr_svrty_medium "not offered and downgraded to a weaker protocol" if [[ "$tls12_detected_version" == 0300 ]]; then detected_version_string="SSLv3" @@ -5478,15 +5470,15 @@ run_protocols() { 3) out "not offered, " fileout "$jsonID" "INFO" "not offered" add_tls_offered tls1_2 no - set_grade_cap "C" "TLS1.2 is not offered" + set_grade_cap "C" "TLS 1.2 is not offered" pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" - set_grade_cap "C" "TLS1.2 is not offered" + set_grade_cap "C" "TLS 1.2 is not offered" ;; 4) out "likely "; pr_svrty_medium "not offered, " fileout "$jsonID" "MEDIUM" "not offered" add_tls_offered tls1_2 no - set_grade_cap "C" "TLS1.2 is not offered" + set_grade_cap "C" "TLS 1.2 is not offered" pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; @@ -5854,7 +5846,9 @@ sub_cipherlists() { ;; esac + # Not a perfect place here. A new one should be picked in the future [[ $sclient_success -eq 0 && "$1" =~ (^|:)EXPORT(:|$) ]] && set_grade_cap "F" "Export suite offered" + [[ $sclient_success -eq 0 && "$1" =~ AEAD ]] && set_grade_cap "B" "No AEAD ciphers offered" fi tmpfile_handle ${FUNCNAME[0]}.${5}.txt [[ $DEBUG -ge 1 ]] && tm_out " -- $1" @@ -5959,7 +5953,7 @@ run_cipherlists() { ret=$((ret + $?)) sub_cipherlists "$ossl_good_ciphers" "" " non-FS Strong encryption (AEAD ciphers) " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) - sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secure Strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_strong_ciphers" ALL " FS + Strong encryption (AEAD ciphers) " 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) outln @@ -9756,9 +9750,9 @@ run_fs() { if [[ $sclient_success -ne 0 ]]; then outln - prln_svrty_medium " No ciphers supporting Forward Secrecy offered" - fileout "$jsonID" "MEDIUM" "No ciphers supporting (P)FS offered" - set_grade_cap "B" "Forward Security (PFS) is not supported" + prln_svrty_medium " No ciphers supporting Forward Secrecy (FS) offered" + fileout "$jsonID" "MEDIUM" "No ciphers supporting Forward Secrecy offered" + set_grade_cap "B" "Forward Secrecy (FS) is not supported" else outln fs_offered=true @@ -15961,7 +15955,7 @@ run_sweet32() { local -i nr_sweet32_ciphers=0 nr_supported_ciphers=0 nr_ssl2_sweet32_ciphers=0 nr_ssl2_supported_ciphers=0 local ssl2_sweet=false local using_sockets=true - local tls1_1_vulnable=false + local tls1_1_vulnerable=false [[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for SWEET32 (Birthday Attacks on 64-bit Block Ciphers) " && outln pr_bold " SWEET32"; out " (${cve// /, }) " @@ -16022,7 +16016,7 @@ run_sweet32() { sclient_connect_successful $? $TMPFILE sclient_success=$? [[ $DEBUG -ge 2 ]] && grep -Eq "error|failure" $ERRFILE | grep -Eav "unable to get local|verify error" - [[ $proto == -tls1_1 && $sclient_success -eq 0 ]] && tls1_1_vulnable=true + [[ $proto == -tls1_1 && $sclient_success -eq 0 ]] && tls1_1_vulnerable=true [[ $sclient_success -eq 0 ]] && break done if "$HAS_SSL2"; then @@ -16042,11 +16036,11 @@ run_sweet32() { if [[ $sclient_success -eq 0 ]] && "$ssl2_sweet" ; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers for SSLv2 and above" fileout "SWEET32" "LOW" "uses 64 bit block ciphers for SSLv2 and above" "$cve" "$cwe" "$hint" - "$tls1_1_vulnable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS1.1+ (vulnerable to SWEET32)" + "$tls1_1_vulnerable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS 1.1 (vulnerable to SWEET32)" elif [[ $sclient_success -eq 0 ]]; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers" fileout "SWEET32" "LOW" "uses 64 bit block ciphers" "$cve" "$cwe" "$hint" - "$tls1_1_vulnable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS1.1+ (vulnerable to SWEET32)" + "$tls1_1_vulnerable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS 1.1 (vulnerable to SWEET32)" elif "$ssl2_sweet"; then pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers wth SSLv2 only" fileout "SWEET32" "LOW" "uses 64 bit block ciphers with SSLv2 only" "$cve" "$cwe" "$hint" @@ -17348,8 +17342,8 @@ run_rc4() { sigalg[i]="$(read_sigalg_from_file "$TMPFILE")" # If you use RC4 with newer protocols, you are punished harder - if [[ "$proto" == "-tls1_1" ]]; then - set_grade_cap "C" "RC4 ciphers offered on TLS1.1" + if [[ "$proto" == -tls1_1 ]]; then + set_grade_cap "C" "RC4 ciphers offered on TLS 1.1" fi done done @@ -20553,24 +20547,27 @@ run_grading() { local c3_worst c3_best c3_worst_cb c3_best_cb local old_ifs=$IFS sorted_reasons sorted_warnings reason_loop=0 warning_loop=0 + outln "\n"; + pr_headlineln " Calculating grades (experimental)" + outln + # Sort the reasons. This is just nicer to read in genereal IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}")) IFS=$old_ifs - fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' (revision 2009q) (near complete)" - pr_bold " Grading specification "; out "SSLLabs's 'SSL Server Rating Guide' (revision 2009q)"; prln_warning " (near complete)" + fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' version 2009q from 2020-01-30 (near complete)" + pr_bold " Grading specification "; out "SSL Labs's 'SSL Server Rating Guide' version 2009q from 2020-01-30"; prln_warning " (near complete)" pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" outln # No point in calculating a score, if a cap of "F", "T", or "M" has been set - if [[ $GRADE_CAP == "F" || $GRADE_CAP == "T" || $GRADE_CAP == "M" ]]; then + if [[ $GRADE_CAP == F || $GRADE_CAP == T || $GRADE_CAP == M ]]; then pr_bold " Protocol Support "; out "(weighted) "; outln "0 (0)" pr_bold " Key Exchange "; out " (weighted) "; outln "0 (0)" - pr_bold " Cipher Stregth "; out " (weighted) "; outln "0 (0)" + pr_bold " Cipher Strength "; out " (weighted) "; outln "0 (0)" pr_bold " Final Score "; outln "0" pr_bold " Grade "; prln_svrty_critical "$GRADE_CAP" fileout "grade" "CRITICAL" "$GRADE_CAP" - outln else ## Category 1 # get best score, by searching for the best protocol, until a hit occurs @@ -20642,7 +20639,7 @@ run_grading() { let c3_score="($c3_best+$c3_worst)/2" # Gets the category score let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) - pr_bold " Cipher Stregth "; out " (weighted) "; outln "$c3_score ($c3_wscore)" + pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)" ## Calculate final score and grade let final_score=$c1_wscore+$c2_wscore+$c3_wscore @@ -20668,7 +20665,7 @@ run_grading() { if [[ $GRADE_CAP != "" && ! $pre_cap_grade > $GRADE_CAP ]]; then final_grade=$GRADE_CAP # For "exceptional" config, an "A+" is awarded, or "A-" for slightly less "exceptional" - elif [[ $GRADE_CAP == "" && $pre_cap_grade == "A" ]]; then + elif [[ $GRADE_CAP == "" && $pre_cap_grade == A ]]; then if [[ ${#sorted_warnings[@]} -eq 0 ]]; then final_grade="A+" else @@ -20679,27 +20676,27 @@ run_grading() { fi case "$final_grade" in - "A"*) pr_bold " Grade " + A*) pr_bold " Grade " prln_svrty_best $final_grade fileout "grade" "OK" "$final_grade" ;; - "B") pr_bold " Grade " + B) pr_bold " Grade " prln_svrty_medium $final_grade fileout "grade" "MEDIUM" "$final_grade" ;; - "C") pr_bold " Grade " + C) pr_bold " Grade " prln_svrty_medium $final_grade fileout "grade" "MEDIUM" "$final_grade" ;; - "D") pr_bold " Grade " + D) pr_bold " Grade " prln_svrty_high $final_grade fileout "grade" "HIGH" "$final_grade" ;; - "E") pr_bold " Grade " + E) pr_bold " Grade " prln_svrty_high $final_grade fileout "grade" "HIGH" "$final_grade" ;; - "F") pr_bold " Grade " + F) pr_bold " Grade " prln_svrty_critical $final_grade fileout "grade" "CRITICAL" "$final_grade" ;; @@ -20749,7 +20746,7 @@ set_grading_state() { # ... else we can't grade if [[ $nr_enabled -lt 18 ]]; then - DISABLE_GRADING=true + do_grading=false return 1 fi @@ -20798,6 +20795,7 @@ initialize_globals() { do_client_simulation=false do_display_only=false do_starttls=false + do_grading=false } @@ -20833,6 +20831,7 @@ set_scanning_defaults() { else VULN_COUNT=12 fi + do_grading=true } # returns number of $do variables set = number of run_funcs() to perform @@ -20843,7 +20842,7 @@ count_do_variables() { 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_grease 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 + do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_grading; do [[ "${!gbl}" == true ]] && let true_nr++ done return $true_nr @@ -20856,7 +20855,7 @@ debug_globals() { 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_grease 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 + do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_grading; do printf "%-22s = %s\n" $gbl "${!gbl}" done printf "%-22s : %s\n" URI: "$URI" @@ -21121,7 +21120,7 @@ parse_cmd_line() { do_grease=true ;; --disable-grading) - DISABLE_GRADING=true + do_grading=false ;; -9|--full) set_scanning_defaults @@ -21449,7 +21448,7 @@ parse_cmd_line() { # Unless explicit disabled, check if grading can be enabled # Should be called after set_scanning_defaults - "$DISABLE_GRADING" || set_grading_state + "$do_grading" || set_grading_state CMDLINE_PARSED=true } @@ -21620,13 +21619,9 @@ lets_roll() { fileout_section_header $section_number true && ((section_number++)) "$do_client_simulation" && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; } - if ! "$DISABLE_GRADING"; then - outln; pr_headlineln " Calculating grade " - outln + fileout_section_header $section_number true && ((section_number++)) + "$do_grading" && { run_grading; ret=$(($? + ret)); stopwatch run_grading; } - fileout_section_header $section_number true && ((section_number++)) - { run_grading; ret=$(($? + ret)); stopwatch run_grading; } - fi fi fileout_section_footer true fi From 2c10676e036416466b5f5fe6769008a79330ab1e Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 17 Apr 2020 14:49:35 +0200 Subject: [PATCH 028/211] Output polish, minor code polish to grading ... and squash the TLS 1.2 grading cap for TLS 1.3 only server --- testssl.sh | 69 +++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/testssl.sh b/testssl.sh index bfef981..9522fef 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5422,7 +5422,6 @@ run_protocols() { add_tls_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! 1) add_tls_offered tls1_2 no - set_grade_cap "C" "TLS 1.2 is not offered" if "$offers_tls13"; then out "not offered" else @@ -5434,6 +5433,7 @@ run_protocols() { fileout "$jsonID" "INFO" "not offered" else fileout "$jsonID" "MEDIUM" "not offered" # TLS 1.3, no TLS 1.2 --> no GCM, penalty + set_grade_cap "C" "TLS 1.2 or TLS 1.3 are not offered" fi else prln_svrty_critical " -- connection failed rather than downgrading to $latest_supported_string" @@ -20555,18 +20555,18 @@ run_grading() { IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}")) IFS=$old_ifs - fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' version 2009q from 2020-01-30 (near complete)" - pr_bold " Grading specification "; out "SSL Labs's 'SSL Server Rating Guide' version 2009q from 2020-01-30"; prln_warning " (near complete)" - pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" + pr_bold " Grading specs"; out ", not complete "; outln "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" + pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" outln + fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" # No point in calculating a score, if a cap of "F", "T", or "M" has been set if [[ $GRADE_CAP == F || $GRADE_CAP == T || $GRADE_CAP == M ]]; then - pr_bold " Protocol Support "; out "(weighted) "; outln "0 (0)" - pr_bold " Key Exchange "; out " (weighted) "; outln "0 (0)" - pr_bold " Cipher Strength "; out " (weighted) "; outln "0 (0)" - pr_bold " Final Score "; outln "0" - pr_bold " Grade "; prln_svrty_critical "$GRADE_CAP" + pr_bold " Protocol Support"; out " (weighted) "; outln "0 (0)" + pr_bold " Key Exchange"; out " (weighted) "; outln "0 (0)" + pr_bold " Cipher Strength"; out " (weighted) "; outln "0 (0)" + pr_bold " Final Score "; outln "0" + pr_bold " Grade "; prln_svrty_critical "$GRADE_CAP" fileout "grade" "CRITICAL" "$GRADE_CAP" else ## Category 1 @@ -20601,13 +20601,13 @@ run_grading() { let c1_score="($c1_best+$c1_worst)/2" # Gets the category score let c1_wscore=$c1_score*30/100 # Gets the weighted score for category (30%) - pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)" + pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)" ## Category 2 let c2_score=$KEY_EXCH_SCORE let c2_wscore=$c2_score*30/100 - pr_bold " Key Exchange "; out " (weighted) "; outln "$c2_score ($c2_wscore)" + pr_bold " Key Exchange "; out " (weighted) "; outln "$c2_score ($c2_wscore)" ## Category 3 @@ -20639,12 +20639,12 @@ run_grading() { let c3_score="($c3_best+$c3_worst)/2" # Gets the category score let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) - pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)" + pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)" ## Calculate final score and grade let final_score=$c1_wscore+$c2_wscore+$c3_wscore - pr_bold " Final Score "; outln $final_score + pr_bold " Final Score "; outln $final_score # get score, and somehow do something about the GRADE_CAP if [[ $final_score -ge 80 ]]; then @@ -20662,10 +20662,10 @@ run_grading() { fi # If the calculated grade is bigger than the grade cap, then set grade as the cap - if [[ $GRADE_CAP != "" && ! $pre_cap_grade > $GRADE_CAP ]]; then + if [[ -n "$GRADE_CAP" && ! $pre_cap_grade > $GRADE_CAP ]]; then final_grade=$GRADE_CAP # For "exceptional" config, an "A+" is awarded, or "A-" for slightly less "exceptional" - elif [[ $GRADE_CAP == "" && $pre_cap_grade == A ]]; then + elif [[ -z "$GRADE_CAP" && $pre_cap_grade == A ]]; then if [[ ${#sorted_warnings[@]} -eq 0 ]]; then final_grade="A+" else @@ -20675,30 +20675,25 @@ run_grading() { final_grade=$pre_cap_grade fi + pr_bold " Grade " case "$final_grade" in - A*) pr_bold " Grade " - prln_svrty_best $final_grade - fileout "grade" "OK" "$final_grade" + A*) prln_svrty_best $final_grade + fileout "grade" "OK" "$final_grade" ;; - B) pr_bold " Grade " - prln_svrty_medium $final_grade - fileout "grade" "MEDIUM" "$final_grade" + B) prln_svrty_medium $final_grade + fileout "grade" "MEDIUM" "$final_grade" ;; - C) pr_bold " Grade " - prln_svrty_medium $final_grade - fileout "grade" "MEDIUM" "$final_grade" + C) prln_svrty_medium $final_grade + fileout "grade" "MEDIUM" "$final_grade" ;; - D) pr_bold " Grade " - prln_svrty_high $final_grade - fileout "grade" "HIGH" "$final_grade" + D) prln_svrty_high $final_grade + fileout "grade" "HIGH" "$final_grade" ;; - E) pr_bold " Grade " - prln_svrty_high $final_grade - fileout "grade" "HIGH" "$final_grade" + E) prln_svrty_high $final_grade + fileout "grade" "HIGH" "$final_grade" ;; - F) pr_bold " Grade " - prln_svrty_critical $final_grade - fileout "grade" "CRITICAL" "$final_grade" + F) prln_svrty_critical $final_grade + fileout "grade" "CRITICAL" "$final_grade" ;; esac fi @@ -20706,10 +20701,10 @@ run_grading() { # Pretty print - again, it's just nicer to read for reason in "${sorted_reasons[@]}"; do if [[ $reason_loop -eq 0 ]]; then - pr_bold " Grade cap reasons "; outln "$reason" + pr_bold " Grade cap reasons "; outln "$reason" let reason_loop++ else - outln " $reason" + outln " $reason" fi done @@ -20738,11 +20733,11 @@ set_grading_state() { do_heartbleed do_ccs_injection do_ticketbleed do_robot do_renego \ do_crime do_ssl_poodle do_tls_fallback_scsv do_drown do_beast \ do_rc4 do_logjam; do - [[ "${!gbl}" == true ]] && let nr_enabled++ + "${!gbl}" && let nr_enabled++ done # ... atleast one of these has to be set - [[ $do_allciphers == true || $do_cipher_per_proto == true ]] && let nr_enabled++ + "$do_allciphers" || "$do_cipher_per_proto" && let nr_enabled++ # ... else we can't grade if [[ $nr_enabled -lt 18 ]]; then From d9f2ca80d6c0fd3b766526775a387e9fe443fd9d Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 17 Apr 2020 14:54:11 +0200 Subject: [PATCH 029/211] fix conditional statement (regression) --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 9522fef..316269a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20737,7 +20737,7 @@ set_grading_state() { done # ... atleast one of these has to be set - "$do_allciphers" || "$do_cipher_per_proto" && let nr_enabled++ + [[ "$do_allciphers" || "$do_cipher_per_proto" ]] && let nr_enabled++ # ... else we can't grade if [[ $nr_enabled -lt 18 ]]; then From b4ad0d2425b2f977526545445eaa40631dff336a Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Fri, 17 Apr 2020 15:31:29 +0200 Subject: [PATCH 030/211] Less aggresive TLS_FALLBACK_SCVS checks --- testssl.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index 316269a..4c468fa 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16183,7 +16183,6 @@ run_tls_fallback_scsv() { if [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then prln_svrty_critical "No fallback possible, SSLv2 is the only protocol" fileout "$jsonID" "CRITICAL" "SSLv2 is the only protocol" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 fi for p in tls1_2 tls1_1 tls1 ssl3; do @@ -16212,7 +16211,6 @@ run_tls_fallback_scsv() { "ssl3") prln_svrty_high "No fallback possible, SSLv3 is the only protocol" fileout "$jsonID" "HIGH" "only SSLv3 supported" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 ;; *) if [[ $(has_server_protocol tls1_3) -eq 0 ]]; then @@ -16220,7 +16218,6 @@ run_tls_fallback_scsv() { # then assume it does not support SSLv3, even if SSLv3 cannot be tested. pr_svrty_good "No fallback possible (OK)"; outln ", TLS 1.3 is the only protocol" fileout "$jsonID" "OK" "only TLS 1.3 supported" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" elif [[ $(has_server_protocol tls1_3) -eq 1 ]] && \ ( [[ $(has_server_protocol ssl3) -eq 1 ]] || "$HAS_SSL3" ); then # TLS 1.3, TLS 1.2, TLS 1.1, TLS 1, and SSLv3 are all not supported. @@ -16234,7 +16231,6 @@ run_tls_fallback_scsv() { # it is very likely that SSLv3 is the only supported protocol. pr_svrty_high "NOT ok, no fallback possible"; outln ", TLS 1.3, 1.2, 1.1 and 1.0 not supported" fileout "$jsonID" "HIGH" "TLS 1.3, 1.2, 1.1, 1.0 not supported" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" else # TLS 1.2, TLS 1.1, and TLS 1 are not supported, but can't tell whether TLS 1.3 is supported. # This could be a TLS 1.3 only server, an SSLv3 only server (if SSLv3 support cannot be tested), @@ -16242,7 +16238,6 @@ run_tls_fallback_scsv() { # since this could either be good or bad. outln "No fallback possible, TLS 1.2, TLS 1.1, and TLS 1 not supported" fileout "$jsonID" "INFO" "TLS 1.2, TLS 1.1, and TLS 1 not supported" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" fi return 0 esac @@ -16287,7 +16282,6 @@ run_tls_fallback_scsv() { ;; esac fileout "$jsonID" "OK" "no protocol below $high_proto_str offered" - set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV" return 0 fi case "$low_proto" in From 49608294338adb6a20038b32c47905f2e806d302 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sun, 19 Apr 2020 23:54:42 +0200 Subject: [PATCH 031/211] Fix JSON for grading / rating --- testssl.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/testssl.sh b/testssl.sh index 316269a..bd62c1a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1121,6 +1121,7 @@ fileout_json_section() { 9) echo -e ",\n \"vulnerabilities\" : [" ;; 10) echo -e ",\n \"cipherTests\" : [" ;; 11) echo -e ",\n \"browserSimulations\": [" ;; + 12) echo -e ",\n \"grading\" : [" ;; *) echo "invalid section" ;; esac } From 127cf95e2266384c505d541a79b727b701f160f6 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 12:26:33 +0200 Subject: [PATCH 032/211] Address rating for STARTTLS tests STARTTLS tests should always give a bad rating because of the missing trust 1) . That's why we don't provide more details as "T". Maybe we decide later to provide an environment variable which still shows this warning but divulges more details. TBC. Documentation is missing for STARTTLS + grades. 1) There might be cases also for STARTTLS where encryption is enforced and e.g. the certificate fingerprint is validated. As this is highly protcol specific we won't test that. --- testssl.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/testssl.sh b/testssl.sh index be47c72..74b03ac 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20546,6 +20546,14 @@ run_grading() { pr_headlineln " Calculating grades (experimental)" outln + if [[ -n "$STARTTLS_PROTOCOL" ]]; then + pr_bold " Grade "; pr_svrty_critical "T" + outln " - STARTTLS encryption is opportunistic" + outln " (Further details would lead to a false sense of security)" + fileout "grade" "CRITICAL" "T, No more details shown as it would lead to a false sense of security" + return 0 + fi + # Sort the reasons. This is just nicer to read in genereal IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}")) From c3f09f56f7e92a7358135d5ff1898257f31c3dfe Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 22:41:14 +0200 Subject: [PATCH 033/211] Grading --> Rating but we still hand out grades --- doc/testssl.1.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 170d426..1006684 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -287,7 +287,7 @@ Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and 5. display bytes received via sockets 6. whole 9 yards -`--disable-grading` disables grading explicitly. +`--disable-rating` disables rating explicitly. Grading automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities). @@ -386,12 +386,12 @@ Except the environment variables mentioned above which can replace command line * MAX_OSSL_FAIL: A number which tells testssl.sh how often an OpenSSL s_client connect may fail before the program gives up and terminates. The default is 2. You can increase it to a higher value if you frequently see a message like *Fatal error: repeated TCP connect problems, giving up*. * MAX_HEADER_FAIL: A number which tells testssl.sh how often a HTTP GET request over OpenSSL may return an empty file before the program gives up and terminates. The default is 3. Also here you can incerase the threshold when you spot messages like *Fatal error: repeated HTTP header connect problems, doesn't make sense to continue*. -### GRADING -This script has a near-complete implementation of SSLLabs's '[SSL Server Rating Guide](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide)'. +### RATING +This program has a near-complete implementation of SSL Labs's '[SSL Server Rating Guide](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide)'. -This is *not* a reimplementation of the [SSLLab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but a implementation of the above grading specification, slight discrepancies might occur! +This is *not* a reimplementation of the [SS LLab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but a implementation of the above rating specification, slight discrepancies might occur! -Disclaimer: Having a good grade does **NOT** necessary equal to having good security! Never rely solely on a good grade! +Disclaimer: Having a good grade does **NOT** necessary equal to having good security! Never rely solely on a good rating! As of writing, these checks are missing: * GOLDENDOODLE - should be graded **F** if vulnerable @@ -417,24 +417,24 @@ To implement a new grade warning, simply call the `set_grade_warning()` function set_grade_warning "Documentation is always right" ``` #### Implementing a new check which contains grade caps -When implementing a new check (be it vulnerability or not) that sets grade caps, the `set_grading_state()` has to be updated (i.e. the `$do_mycheck` variable-name has to be added to the loop, and `$nr_enabled` if-statement has to be incremented) +When implementing a new check (be it vulnerability or not) that sets grade caps, the `set_rating_state()` has to be updated (i.e. the `$do_mycheck` variable-name has to be added to the loop, and `$nr_enabled` if-statement has to be incremented) -The `set_grading_state()` automatically disables grading, if all the required checks are *not* enabled. +The `set_rating_state()` automatically disables ratinng, if all the required checks are *not* enabled. This is to prevent giving out a misleading or wrong grade. #### Implementing a new revision -When a new revision of the grading specification comes around, the following has to be done: +When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1. Added to the script wherever relevant, or 2. Added to the above list of missing checks (if *i.* is not possible) * New grade warnings has to be added wherever relevant -* The revision output in `run_grading()` function has to updated +* The revision output in `run_rating()` function has to updated ## EXAMPLES testssl.sh testssl.sh -does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation, and grading. +does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation, and rating. testssl.sh testssl.net:443 From e9e11e213afc3df7bcc4e78c91d014ef345830e1 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 22:45:58 +0200 Subject: [PATCH 034/211] * Grading --> Rating. But we still hand out grades --- testssl.sh | 82 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/testssl.sh b/testssl.sh index 74b03ac..befcc21 100755 --- a/testssl.sh +++ b/testssl.sh @@ -998,7 +998,7 @@ f5_port_decode() { # arg1: A grade to set ("A", "B", "C", "D", "E", "F", "M", or "T") # arg2: A reason why (e.g. "Vulnerable to CRIME") set_grade_cap() { - "$do_grading" || return 0 + "$do_rating" || return 0 GRADE_CAP_REASONS+=("Grade capped to $1. $2") # Always set special attributes. These are hard caps, due to name mismatch or cert being invalid @@ -1014,7 +1014,7 @@ set_grade_cap() { # Sets a grade warning, as specified by the grade specification # arg1: A warning message set_grade_warning() { - "$do_grading" || return 0 + "$do_rating" || return 0 GRADE_WARNINGS+=("$1") return 0 } @@ -1026,7 +1026,7 @@ set_key_str_score() { local type=$1 local size=$2 - "$do_grading" || return 0 + "$do_rating" || return 0 # TODO: We need to get the size of DH params (follows the same table as the "else" clause) # For now, verifying the key size will do... @@ -1076,7 +1076,7 @@ set_key_str_score() { set_ciph_str_score() { local size=$1 - "$do_grading" || return 0 + "$do_rating" || return 0 [[ $size -gt $CIPH_STR_BEST ]] && let CIPH_STR_BEST=$size [[ $size -lt $CIPH_STR_WORST ]] && let CIPH_STR_WORST=$size @@ -1121,7 +1121,7 @@ fileout_json_section() { 9) echo -e ",\n \"vulnerabilities\" : [" ;; 10) echo -e ",\n \"cipherTests\" : [" ;; 11) echo -e ",\n \"browserSimulations\": [" ;; - 12) echo -e ",\n \"grading\" : [" ;; + 12) echo -e ",\n \"rating\" : [" ;; *) echo "invalid section" ;; esac } @@ -3438,7 +3438,7 @@ neat_list(){ enc="${enc//POLY1305/}" # remove POLY1305 enc="${enc//\//}" # remove "/" - # For grading, set bits size + # For rating set bit size set_ciph_str_score $strength [[ "$export" =~ export ]] && strength="$strength,exp" @@ -18560,7 +18560,7 @@ output options (can also be preset via environment variables): --color <0|1|2|3> 0: no escape or other codes, 1: b/w escape codes, 2: color (default), 3: extra color (color all ciphers) --colorblind swap green and blue in the output --debug <0-6> 1: screen output normal but keeps debug output in /tmp/. 2-6: see "grep -A 5 '^DEBUG=' testssl.sh" - --disable-grading Explicitly disables the grading output + --disable-rating Explicitly disables the rating output file output options (can also be preset via environment variables) --log, --logging logs stdout to '\${NODE}-p\${port}\${YYYYMMDD-HHMM}.log' in current working directory (cwd) @@ -20535,7 +20535,7 @@ run_mass_testing_parallel() { return $? } -run_grading() { +run_rating() { local final_score pre_cap_grade final_grade local c1_score c2_score c3_score c1_wscore c2_wscore c3_wscore local c1_worst c1_best @@ -20543,14 +20543,15 @@ run_grading() { local old_ifs=$IFS sorted_reasons sorted_warnings reason_loop=0 warning_loop=0 outln "\n"; - pr_headlineln " Calculating grades (experimental)" + pr_headlineln " Rating (experimental) " outln if [[ -n "$STARTTLS_PROTOCOL" ]]; then pr_bold " Grade "; pr_svrty_critical "T" outln " - STARTTLS encryption is opportunistic" outln " (Further details would lead to a false sense of security)" - fileout "grade" "CRITICAL" "T, No more details shown as it would lead to a false sense of security" + fileout "grade" "CRITICAL" "T" + fileout "grade_cap_reasons" "INFO" "No more details shown as it would lead to a false sense of security" return 0 fi @@ -20558,10 +20559,10 @@ run_grading() { IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}")) IFS=$old_ifs - pr_bold " Grading specs"; out ", not complete "; outln "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" + pr_bold " Rating specs"; out " (not complete) "; outln "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" outln - fileout "grading_spec" "INFO" "SSLLabs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" + fileout "rating_spec" "INFO" "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" # No point in calculating a score, if a cap of "F", "T", or "M" has been set if [[ $GRADE_CAP == F || $GRADE_CAP == T || $GRADE_CAP == M ]]; then @@ -20569,7 +20570,7 @@ run_grading() { pr_bold " Key Exchange"; out " (weighted) "; outln "0 (0)" pr_bold " Cipher Strength"; out " (weighted) "; outln "0 (0)" pr_bold " Final Score "; outln "0" - pr_bold " Grade "; prln_svrty_critical "$GRADE_CAP" + pr_bold " Overall Grade "; prln_svrty_critical "$GRADE_CAP" fileout "grade" "CRITICAL" "$GRADE_CAP" else ## Category 1 @@ -20640,7 +20641,7 @@ run_grading() { c3_worst=0 fi let c3_score="($c3_best+$c3_worst)/2" # Gets the category score - let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) + let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)" @@ -20678,7 +20679,7 @@ run_grading() { final_grade=$pre_cap_grade fi - pr_bold " Grade " + pr_bold " Overall Grade " case "$final_grade" in A*) prln_svrty_best $final_grade fileout "grade" "OK" "$final_grade" @@ -20720,14 +20721,26 @@ run_grading() { fi done + case $GRADE_CAP in + # A-E: WIP + A) fileout "grade_cap_reasons" "INFO" "" ;; + B) fileout "grade_cap_reasons" "INFO" "" ;; + C) fileout "grade_cap_reasons" "INFO" "" ;; + D) fileout "grade_cap_reasons" "INFO" "" ;; + E) fileout "grade_cap_reasons" "INFO" "" ;; + M) fileout "grade_cap_reasons" "INFO" "SAN / CN mismatch" ;; + F) fileout "grade_cap_reasons" "INFO" "Severe vulnerability or cryptographic problem" ;; + T) fileout "grade_cap_reasons" "INFO" "Issue with certificate" ;; + esac + return 0 } -# Checks whether grading can be done or not. -# Grading needs a mix of certificate and vulnerabilities checks, in order to give out a proper grade. -# This function disables grading, if not all required checks are enabled -# Returns "0" if grading is enabled, and "1" if grading is disabled -set_grading_state() { +# Checks whether rating can be done or not. +# Rating needs a mix of certificate and vulnerabilities checks, in order to give out proper grades. +# This function disables rating, if not all required checks are enabled +# Returns "0" if rating is enabled, and "1" if rating is disabled +set_rating_state() { local gbl local nr_enabled=0 @@ -20742,9 +20755,9 @@ set_grading_state() { # ... atleast one of these has to be set [[ "$do_allciphers" || "$do_cipher_per_proto" ]] && let nr_enabled++ - # ... else we can't grade + # ... else we can't do rating if [[ $nr_enabled -lt 18 ]]; then - do_grading=false + do_rating=false return 1 fi @@ -20793,7 +20806,7 @@ initialize_globals() { do_client_simulation=false do_display_only=false do_starttls=false - do_grading=false + do_rating=false } @@ -20829,7 +20842,7 @@ set_scanning_defaults() { else VULN_COUNT=12 fi - do_grading=true + do_rating=true } # returns number of $do variables set = number of run_funcs() to perform @@ -20840,8 +20853,8 @@ count_do_variables() { 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_grease 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_grading; do - [[ "${!gbl}" == true ]] && let true_nr++ + do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do + "${!gbl}" && let true_nr++ done return $true_nr } @@ -20853,7 +20866,7 @@ debug_globals() { 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_grease 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_grading; do + 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}" done printf "%-22s : %s\n" URI: "$URI" @@ -20931,7 +20944,7 @@ parse_cmd_line() { ;; esac - # initializing + # set all globals to false initialize_globals while [[ $# -gt 0 ]]; do @@ -21117,8 +21130,8 @@ parse_cmd_line() { -g|--grease) do_grease=true ;; - --disable-grading) - do_grading=false + --disable-rating) + do_rating=false ;; -9|--full) set_scanning_defaults @@ -21439,14 +21452,13 @@ parse_cmd_line() { grep -q "BEGIN CERTIFICATE" "$fname" || fatal "\"$fname\" is not CA file in PEM format" $ERR_RESOURCE done - [[ "$DEBUG" -ge 5 ]] && debug_globals - count_do_variables [[ $? -eq 0 ]] && set_scanning_defaults + [[ "$DEBUG" -ge 5 ]] && debug_globals - # Unless explicit disabled, check if grading can be enabled + # Unless explicit disabled, check if rating can be enabled # Should be called after set_scanning_defaults - "$do_grading" || set_grading_state + ! "$do_rating" && set_rating_state CMDLINE_PARSED=true } @@ -21618,7 +21630,7 @@ lets_roll() { "$do_client_simulation" && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; } fileout_section_header $section_number true && ((section_number++)) - "$do_grading" && { run_grading; ret=$(($? + ret)); stopwatch run_grading; } + "$do_rating" && { run_rating; ret=$(($? + ret)); stopwatch run_rating; } fi fileout_section_footer true From b1ef3a020f5f3c65e612af10e2b0ca17bd9db07a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 22:48:31 +0200 Subject: [PATCH 035/211] add single blank for pretty JSON --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index befcc21..12f7ae8 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1121,7 +1121,7 @@ fileout_json_section() { 9) echo -e ",\n \"vulnerabilities\" : [" ;; 10) echo -e ",\n \"cipherTests\" : [" ;; 11) echo -e ",\n \"browserSimulations\": [" ;; - 12) echo -e ",\n \"rating\" : [" ;; + 12) echo -e ",\n \"rating\" : [" ;; *) echo "invalid section" ;; esac } From 577370a27223a3f7032483a535684099c3288e11 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 22:49:31 +0200 Subject: [PATCH 036/211] Add rating --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 289f81a..26c1e23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ## Change Log +### Features implemented / improvements in 3.1 +* Renamed PFS into FS +* Improved mass testing +* Security fix: DNS input +* Rating + ### Features implemented / improvements in 3.0 * Full support of TLS 1.3, shows also drafts supported From 7da2e90083de33b289b6497c0cdef0c8d9027389 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 20 Apr 2020 22:49:48 +0200 Subject: [PATCH 037/211] Honor Magnus --- CREDITS.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 38ccced..884d8dd 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -46,9 +46,9 @@ Full contribution, see git log. - Client simulations - CI integration, some test cases for it - * Steven Danneman - - Postgres and MySQL STARTTLS support - - MongoDB support +* Steven Danneman + - Postgres and MySQL STARTTLS support + - MongoDB support * Christian Dresen - Dockerfile @@ -78,6 +78,9 @@ Full contribution, see git log. * Hubert Kario - helped with avoiding accidental TCP fragmentation +* Magnus Larsen + - SSL Labs Rating + * Jacco de Leeuw - skip checks which might trigger an IDS ($OFFENSIVE / --ids-friendly) From 8c7dcbbc3bb6dbc65792eb4ab6c18f68a509ada6 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 21 Apr 2020 19:22:16 +0200 Subject: [PATCH 038/211] Fix misleading phrasing in run of standard ciphers see #1571. Bit size doesn't matter. It only matters to the user which ciphers they are. Additionally phrased the output better (FS + strong enc) and do less indentation. Renamed average_ciphers -> obsoleted_ciphers to refect what's on the output. --- testssl.sh | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/testssl.sh b/testssl.sh index d5d7ee7..0fd691b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5574,7 +5574,7 @@ sub_cipherlists() { local cve="${9}" local cwe="${10}" - pr_bold "$3 " + pr_bold "$3 " [[ "$OPTIMAL_PROTO" == -ssl2 ]] && proto="$OPTIMAL_PROTO" jsonID="${jsonID}_$5" @@ -5743,7 +5743,7 @@ run_cipherlists() { local ossl_exp_ciphers exp_ciphers sslv2_exp_ciphers local ossl_low_ciphers low_ciphers sslv2_low_ciphers local ossl_tdes_ciphers tdes_ciphers sslv2_tdes_cipher - local ossl_average_ciphers average_ciphers + local ossl_obsoleted_ciphers obsoleted_ciphers local strong_ciphers local cwe="CWE-327" local cwe2="CWE-310" @@ -5780,13 +5780,14 @@ run_cipherlists() { tdes_ciphers="00,07, 00,0A, 00,0D, 00,10, 00,13, 00,16, 00,1F, 00,21, 00,8B, 00,8F, 00,93, C0,03, C0,08, C0,0D, C0,12, C0,1A, C0,1B, C0,1C, C0,34, FE,FF, FF,E0, 00,FF" sslv2_tdes_ciphers="07,01,c0" - ossl_average_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' + # # Now all AES, CAMELLIA, ARIA and SEED CBC ciphers plus GOST + ossl_obsoleted_ciphers='HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL:!MD5' # egrep -w "256|128" etc/cipher-mapping.txt | egrep -v "Au=None|AEAD|RC2|RC4|IDEA|MD5" - average_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" + obsoleted_ciphers="00,2F, 00,30, 00,31, 00,32, 00,33, 00,35, 00,36, 00,37, 00,38, 00,39, 00,3C, 00,3D, 00,3E, 00,3F, 00,40, 00,41, 00,42, 00,43, 00,44, 00,45, 00,67, 00,68, 00,69, 00,6A, 00,6B, 00,84, 00,85, 00,86, 00,87, 00,88, 00,8C, 00,8D, 00,90, 00,91, 00,94, 00,95, 00,96, 00,97, 00,98, 00,99, 00,9A, 00,AE, 00,AF, 00,B2, 00,B3, 00,B6, 00,B7, 00,BA, 00,BB, 00,BC, 00,BD, 00,BE, 00,C0, 00,C1, 00,C2, 00,C3, 00,C4, C0,04, C0,05, C0,09, C0,0A, C0,0E, C0,0F, C0,13, C0,14, C0,1D, C0,1E, C0,1F, C0,20, C0,21, C0,22, C0,23, C0,24, C0,25, C0,26, C0,27, C0,28, C0,29, C0,2A, C0,35, C0,36, C0,37, C0,38, C0,3C, C0,3D, C0,3E, C0,3F, C0,40, C0,41, C0,42, C0,43, C0,44, C0,45, C0,48, C0,49, C0,4A, C0,4B, C0,4C, C0,4D, C0,4E, C0,4F, C0,64, C0,65, C0,66, C0,67, C0,68, C0,69, C0,70, C0,71, C0,72, C0,73, C0,74, C0,75, C0,76, C0,77, C0,78, C0,79, C0,94, C0,95, C0,96, C0,97, C0,98, C0,99, C0,9A, C0,9B" # Workaround: If we use sockets and in order not to hit 131+1 ciphers we omit the GOST ciphers if SERVER_SIZE_LIMIT_BUG is true. # This won't be supported by Cisco ACE anyway. - "$SERVER_SIZE_LIMIT_BUG" || average_ciphers="${average_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" - average_ciphers="${average_ciphers}, 00,FF" + "$SERVER_SIZE_LIMIT_BUG" || obsoleted_ciphers="${obsoleted_ciphers}, 00,80, 00,81, FF,01, FF,02, FF,03, FF,85" + obsoleted_ciphers="${obsoleted_ciphers}, 00,FF" ossl_good_ciphers='AESGCM:CHACHA20:CamelliaGCM:AESCCM:ARIAGCM:!kEECDH:!kEDH:!kDHE:!kDHEPSK:!kECDHEPSK:!aNULL' # grep AEAD etc/cipher-mapping.txt | egrep -v 'Au=None|TLS_ECDHE|TLS_DHE|TLS_PSK_DHE|TLSv1.3' @@ -5807,21 +5808,21 @@ run_cipherlists() { # argv[9]: CVE # argv[10]: CWE - sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_null_ciphers" "" " NULL ciphers (no encryption) " 1 "NULL" "$null_ciphers" "$sslv2_null_ciphers" "$using_sockets" "$cve" "$cwe" ret=$? - sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_anon_ciphers" "" " Anonymous NULL Ciphers (no authentication) " 1 "aNULL" "$anon_ciphers" "$sslv2_anon_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_exp_ciphers" "" " Export ciphers (w/o ADH+NULL) " 1 "EXPORT" "$exp_ciphers" "$sslv2_exp_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" + sub_cipherlists "$ossl_low_ciphers" "" " LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) " 2 "LOW" "$low_ciphers" "$sslv2_low_ciphers" "$using_sockets" "$cve" "$cwe" ret=$((ret + $?)) - sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_tdes_ciphers" "" " Triple DES Ciphers / IDEA " 3 "3DES_IDEA" "$tdes_ciphers" "$sslv2_tdes_ciphers" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_average_ciphers" "" " Obsolete: SEED + 128+256 Bit CBC cipher " 4 "AVERAGE" "$average_ciphers" "" "$using_sockets" "$cve" "$cwe2" + sub_cipherlists "$ossl_obsoleted_ciphers" "" " Obsoleted CBC ciphers (AES, ARIA etc.) " 4 "AVERAGE" "$obsoleted_ciphers" "" "$using_sockets" "$cve" "$cwe2" ret=$((ret + $?)) - sub_cipherlists "$ossl_good_ciphers" "" " non-FS Strong encryption (AEAD ciphers) " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_good_ciphers" "" " Strong encryption (AEAD ciphers) with no FS " 6 "GOOD" "$good_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) - sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secure Strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" + sub_cipherlists "$ossl_strong_ciphers" 'ALL' " Forward Secrecy strong encryption (AEAD ciphers)" 7 "STRONG" "$strong_ciphers" "" "$using_sockets" "" "" ret=$((ret + $?)) outln From d6a9360f2ce1e27bcdcf928090e2a8554517955e Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Wed, 22 Apr 2020 14:08:58 +0200 Subject: [PATCH 039/211] Fix known DH but not weak keys to be capped @ A not B --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 12f7ae8..f6f5656 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16675,7 +16675,7 @@ run_logjam() { else if [[ $subret -eq 1 ]]; then out_common_prime "$jsonID2" "$cve" "$cwe" - set_grade_cap "B" "Uses weak DH key exchange parameters (vulnerable to LOGJAM)" + set_grade_cap "A" "Uses known DH key exchange parameters" if ! "$openssl_no_expdhciphers"; then outln "," out "${spaces}but no DH EXPORT ciphers${addtl_warning}" From 32eab3ead9c988e4574776987b09cd0df27176ed Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Wed, 22 Apr 2020 17:14:05 +0200 Subject: [PATCH 040/211] Fix problem with --disable-rating by introducing framework for tests to be skipped, see also #1502. As a first example for the development branch should serve --disable-rating / --no-rating. The latter is for now undocumented. Also the big case statement in parse_cmd_line() may use a general --disable-* or --no-* clause where all --disable-* / --no-* are being parsed/ A new function set_skip_tests() is being introduced which sets do_ according to the new array SKIP_TESTS . Any new test do be skipped needs to be added to that array. The changes in the --devel part come from the tries to fix the syntax highlight in vim -- which in the end difn't work --- testssl.sh | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/testssl.sh b/testssl.sh index f6f5656..5990768 100755 --- a/testssl.sh +++ b/testssl.sh @@ -140,6 +140,7 @@ declare CMDLINE CMDLINE_PARSED="" # This makes sure we don't let early fatal() write into files when files aren't created yet declare -r -a CMDLINE_ARRAY=("$@") # When performing mass testing, the child processes need to be sent the declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and https://mywiki.wooledge.org/BashFAQ/050). +declare -a SKIP_TESTS=() # This array hold the checks to be skipped ########### Defining (and presetting) variables which can be changed @@ -20842,7 +20843,6 @@ set_scanning_defaults() { else VULN_COUNT=12 fi - do_rating=true } # returns number of $do variables set = number of run_funcs() to perform @@ -20869,10 +20869,26 @@ debug_globals() { 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}" done + # ${!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 printf "%-22s : %s\n" URI: "$URI" } +# This is determining the tests which should be skipped by --no-* or --disable-* a a cmdline arg. +# It achieves that by setting the do_ according to the global array $SKIP_TESTS +# +set_skip_tests() { + for t in ${SKIP_TESTS[@]} ; do + t="do_${t}" + # declare won't do it here --> local scope + eval "$t"=false + debugme printf '%s\n' "set $t: ${!t}" + done +} + + + # arg1: either switch+value (=) or switch # arg2: value (if no = provided) parse_opt_equal_sign() { @@ -20944,7 +20960,7 @@ parse_cmd_line() { ;; esac - # set all globals to false + # set all do_* globals to false initialize_globals while [[ $# -gt 0 ]]; do @@ -21130,8 +21146,10 @@ parse_cmd_line() { -g|--grease) do_grease=true ;; - --disable-rating) - do_rating=false + --disable-rating|--no-rating) + SKIP_TESTS+=("rating") + # TODO: a generic thing would be --disable-* / --no-* , + # catch $1 and add it to the array ( #1502 ) ;; -9|--full) set_scanning_defaults @@ -21143,18 +21161,18 @@ parse_cmd_line() { ADDTL_CA_FILES="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift ;; - --devel) ### this development feature will soon disappear + --devel) echo -e "\nthis is a development feature and may disappear at any time" # arg1: SSL/TLS protocol (SSLv2=22) # arg2: list of cipher suites / hostname/ip # arg3: hostname/ip - HEX_CIPHER="$TLS12_CIPHER" - # DEBUG=3 ./testssl.sh --devel 04 "13,02, 13,01" google.com --> TLS 1.3 - # DEBUG=3 ./testssl.sh --devel 03 "cc, 13, c0, 13" google.de --> TLS 1.2, old CHACHA/POLY - # DEBUG=3 ./testssl.sh --devel 03 "cc,a8, cc,a9, cc,aa, cc,ab, cc,ac" blog.cloudflare.com --> new CHACHA/POLY - # DEBUG=3 ./testssl.sh --devel 01 yandex.ru --> TLS 1.0 + # DEBUG=3 ./testssl.sh --devel 04 "13,02, 13,01" google.com --> TLS 1.3 + # DEBUG=3 ./testssl.sh --devel 03 "cc, 13, c0, 13" google.de --> TLS 1.2, old CHACHA/POLY + # DEBUG=3 ./testssl.sh --devel 03 "cc,a8, cc,a9, cc,aa, cc,ab, cc,ac" blog.cloudflare.com --> new CHACHA/POLY + # DEBUG=3 ./testssl.sh --devel 01 yandex.ru --> TLS 1.0 # DEBUG=3 ./testssl.sh --devel 00 # DEBUG=3 ./testssl.sh --devel 22 - TLS_LOW_BYTE="$2"; + HEX_CIPHER="$TLS12_CIPHER" + TLS_LOW_BYTE="$2" if [[ $# -eq 4 ]]; then # protocol AND ciphers specified HEX_CIPHER="$3" shift @@ -21454,6 +21472,7 @@ parse_cmd_line() { count_do_variables [[ $? -eq 0 ]] && set_scanning_defaults + set_skip_tests [[ "$DEBUG" -ge 5 ]] && debug_globals # Unless explicit disabled, check if rating can be enabled From 07c06e0f9442b1f9ae4414769c305a616a316632 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Wed, 22 Apr 2020 17:19:36 +0200 Subject: [PATCH 041/211] declare t variable in set_skip_tests() --- testssl.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testssl.sh b/testssl.sh index 5990768..7358c34 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20879,6 +20879,8 @@ debug_globals() { # It achieves that by setting the do_ according to the global array $SKIP_TESTS # set_skip_tests() { + local t + for t in ${SKIP_TESTS[@]} ; do t="do_${t}" # declare won't do it here --> local scope From f5aa20ceb1fd90839933fe9e2e34d2e9b14bd896 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 22 Apr 2020 12:31:45 -0400 Subject: [PATCH 042/211] Extended run_server_preference() This commit extends run_server_preference() to list every cipher supported by each protocol even in cases in which the server does not enforce a preference order. For protocols where the server enforces a cipher order the list of supported ciphers is ordered by server preference (as now). For protocols where the server does not enforce a cipher order, the ciphers are listed by encryption strength (as run_cipher_per_proto() does). In order to implement this, ciphers_by_strength() was extended to offer a non-wide mode. --- testssl.sh | 214 ++++++++++++++++++----------------------------------- 1 file changed, 72 insertions(+), 142 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..771f8a3 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3970,7 +3970,7 @@ run_allciphers() { # are good or bad) and list them in order to encryption strength. ciphers_by_strength() { local proto="$1" proto_hex="$2" proto_text="$3" - local using_sockets="$4" + local using_sockets="$4" wide="$5" local ossl_ciphers_proto local -i nr_ciphers nr_ossl_ciphers nr_nonossl_ciphers success local n sslvers auth mac hexc sslv2_ciphers="" cipher @@ -3983,13 +3983,12 @@ ciphers_by_strength() { local id local has_dh_bits="$HAS_DH_BITS" - pr_underline "$(printf -- "%b" "$proto_text")" # for local problem if it happens out " " if ! "$using_sockets" && ! locally_supported "$proto"; then return 0 fi - outln + "$wide" && outln [[ $(has_server_protocol "${proto:1}") -eq 1 ]] && return 0 @@ -4006,7 +4005,7 @@ ciphers_by_strength() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]=${TLS_CIPHER_OSSL_SUPPORTED[i]} - if "$using_sockets" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then + if "$using_sockets" && "$wide" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi if [[ ${#hexc} -eq 9 ]]; then @@ -4022,16 +4021,16 @@ ciphers_by_strength() { normalized_hexcode[nr_ciphers]="x${hexc:2:2}${hexc:7:2}${hexc:12:2}" fi if ( "$using_sockets" || "${TLS_CIPHER_OSSL_SUPPORTED[i]}" ); then - if [[ ${#hexc} -eq 9 ]] && [[ "$proto_text" != SSLv2 ]]; then - if [[ "$proto_text" == TLS\ 1.3 ]]; then + if [[ ${#hexc} -eq 9 ]] && [[ "$proto" != -ssl2 ]]; then + if [[ "$proto" == -tls1_3 ]]; then [[ "${hexc:2:2}" == 13 ]] && nr_ciphers+=1 - elif [[ "$proto_text" == TLS\ 1.2 ]]; then + elif [[ "$proto" == -tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM ]] && [[ "${TLS_CIPHER_RFC_NAME[i]}" != *_CCM_8 ]]; then nr_ciphers+=1 fi - elif [[ ${#hexc} -eq 14 ]] && [[ "$proto_text" == SSLv2 ]]; then + elif [[ ${#hexc} -eq 14 ]] && [[ "$proto" == -ssl2 ]]; then sslv2_ciphers+=", ${hexcode[nr_ciphers]}" nr_ciphers+=1 fi @@ -4049,9 +4048,9 @@ ciphers_by_strength() { ossl_ciphers_proto="-tls1" fi while read hexc n ciph[nr_ciphers] sslvers kx[nr_ciphers] auth enc[nr_ciphers] mac export2[nr_ciphers]; do - if [[ "$proto_text" == TLS\ 1.3 ]]; then + if [[ "$proto" == -tls1_3 ]]; then [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || continue - elif [[ "$proto_text" == "TLS 1.2" ]]; then + elif [[ "$proto" == -tls1_2 ]]; then if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]]; then continue fi @@ -4073,11 +4072,11 @@ ciphers_by_strength() { sslv2_sockets "${sslv2_ciphers:2}" "true" if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" + "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" for (( i=0 ; i>$ERRFILE >$TMPFILE - if sclient_connect_successful $? $TMPFILE; then - offered_proto[i]=$(get_protocol $TMPFILE) - offered_cipher[i]=$(get_cipher $TMPFILE) - [[ ${offered_cipher[i]} == "0000" ]] && offered_cipher[i]="" # Hack! - if [[ "$DISPLAY_CIPHERNAMES" =~ rfc ]] && [[ -n "${offered_cipher[i]}" ]]; then - offered_cipher[i]="$(openssl2rfc "${offered_cipher[i]}")" - [[ -z "${offered_cipher[i]}" ]] && offered_cipher[i]=$(get_cipher $TMPFILE) - fi - [[ $DEBUG -ge 2 ]] && tmln_out "Default cipher for ${offered_proto[i]}: ${offered_cipher[i]}" - else - offered_proto[i]="" - offered_cipher[i]="" - fi + out " (server order)" + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" fi - [[ -n "${offered_cipher[i]}" ]] && add_tls_offered "$proto_ossl" yes - i=$((i + 1)) - done + else + if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" + else + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" + fi + fi + done <<< "$(tm_out " ssl2 22 SSLv2\n ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" + outln + outln - for i in 1 2 3 4 5 6; do - if [[ -n "${offered_cipher[i]}" ]]; then # cipher not empty - if [[ -z "$prev_cipher" ]] || [[ "$prev_cipher" != "${offered_cipher[i]}" ]]; then - [[ -n "$prev_cipher" ]] && outln - str_len=${#offered_cipher[i]} - out " " - if [[ "$COLOR" -le 2 ]]; then - out "${offered_cipher[i]}" - else - pr_cipher_quality "${offered_cipher[i]}" - fi - out ":" - if [[ "$DISPLAY_CIPHERNAMES" =~ openssl ]]; then - for (( 1; str_len < 30; str_len++ )); do - out " " - done - else - for (( 1; str_len < 51; str_len++ )); do - out " " - done - fi - else - out ", " # same cipher --> only print out protocol behind it - fi - out "${offered_proto[i]}" - prev_cipher="${offered_cipher[i]}" - fi - fileout "cipher_order_${offered_proto[i]}" "INFO" "${offered_cipher[i]} at ${offered_proto[i]} $limitedsense" - done - outln "\n No further cipher order check has been done as order is determined by the client" - outln - fi return $ret } From 8566ca80bcf8a18676ba038c34cdbddabd2bbc64 Mon Sep 17 00:00:00 2001 From: Dirk Date: Thu, 23 Apr 2020 09:23:21 +0200 Subject: [PATCH 043/211] Enable rating again was per default disabled by accident previously --- testssl.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/testssl.sh b/testssl.sh index 7358c34..75fbd7a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20843,6 +20843,7 @@ set_scanning_defaults() { else VULN_COUNT=12 fi + do_rating=true } # returns number of $do variables set = number of run_funcs() to perform From a9ab2bcd91fcd8164cd497f153c1a2d8c0c885bc Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 23 Apr 2020 11:20:46 +0200 Subject: [PATCH 044/211] Update documentation (ADDITIONAL_CA_FILES -> ADDTL_CA_FILES) which happened in d44a643fab6be6755a917f85ed491c38990d15ae in testssl.sh . This fixes it in the related files. See also #1581 --- Coding_Convention.md | 2 +- doc/testssl.1 | 4 ++-- doc/testssl.1.html | 4 ++-- doc/testssl.1.md | 4 ++-- etc/README.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Coding_Convention.md b/Coding_Convention.md index 933b30c..03122d1 100644 --- a/Coding_Convention.md +++ b/Coding_Convention.md @@ -65,7 +65,7 @@ Bash is actually quite powerful -- not only with respect to sockets. It's not as ### Misc -* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDITIONAL_CA_FILES`) +* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDTL_CA_FILES`) * Test before doing a PR! Best if you check with two bad and two good examples which should then work as expected. Maybe compare results e.g. with SSLlabs. * Unit tests are done automatically done with Perl using Travis. The trigger is `~/.travis.yml`. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start. You are encouraged to write own checks. You can use e.g. `t/20_baseline_ipv4_http.t` as an example. * If it's an OpenSSL feature you want to use and it could be not available for older OpenSSL versions testssl.sh needs to find out whether OpenSSL has that feature. Best do this with OpenSSL itself and not by checking the version as some vendors do backports. See the examples for `HAS_SSL2` or proxy option check of OpenSSL in `check_proxy()`. diff --git a/doc/testssl.1 b/doc/testssl.1 index 9c0f684..a8e8626 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -176,7 +176,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-phone\-out\fR Checking for revoked certificates via CRL and OCSP is not done per default\. This switch instructs testssl\.sh to query external \-\- in a sense of the current run \-\- URIs\. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl\.sh doesn\'t handle\. PHONE_OUT is the environment variable for this which needs to be set to true if you want this\. . .P -\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDITIONAL_CA_FILES is the environment variable for this\. +\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDTL_CA_FILES is the environment variable for this\. . .SS "SINGLE CHECK OPTIONS" Any single check switch supplied as an argument prevents testssl\.sh from doing a default run\. It just takes this and if supplied other options and runs them \- in the order they would also appear in the default run\. @@ -282,7 +282,7 @@ Certificate Transparency info (if provided by server)\. .IP "" 0 . .P -For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDITIONAL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. +For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDTL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. . .P \fB\-x , \-\-single\-cipher \fR tests matched \fBpattern\fR of ciphers against a server\. Patterns are similar to \fB\-V pattern , \-\-local pattern\fR, see above about matching\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index e7f3c34..5a6c392 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -221,7 +221,7 @@ in /etc/hosts. The use of the switch is only useful if you either

--phone-out Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this.

--add-ca <cafile> enables you to add your own CA(s) for trust chain checks. cafile can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this.

+ADDTL_CA_FILES is the environment variable for this.

SINGLE CHECK OPTIONS

@@ -278,7 +278,7 @@ ADDITIONAL_CA_FILES is the environment variable for this.

For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock gmt_unix_time, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly.

-x <pattern>, --single-cipher <pattern> tests matched pattern of ciphers against a server. Patterns are similar to -V pattern , --local pattern, see above about matching.

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index f347c20..8f29a92 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -143,7 +143,7 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. `--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this. +ADDTL_CA_FILES is the environment variable for this. ### SINGLE CHECK OPTIONS @@ -191,7 +191,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a - Certificate Transparency info (if provided by server). For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock `gmt_unix_time`, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly. `-x , --single-cipher ` tests matched `pattern` of ciphers against a server. Patterns are similar to `-V pattern , --local pattern`, see above about matching. diff --git a/etc/README.md b/etc/README.md index 3437195..fc619cc 100644 --- a/etc/README.md +++ b/etc/README.md @@ -18,7 +18,7 @@ The certificate trust stores were retrieved from Google Chromium uses basically the trust stores above, see https://www.chromium.org/Home/chromium-security/root-ca-policy. -If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDITIONAL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. +If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDTL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. #### Further files From a86ccb6968f116ffc50c19f06b01a9d3021252f0 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 23 Apr 2020 14:11:33 +0200 Subject: [PATCH 045/211] First round of polish of David's PR to extend run_server_preference() See #1580. This commit brings: * If there's no cipher for a protocol it adds a "\n - \n" (also for run_cipher_per_proto() ) * further output improvements * Cipher order --> Cipher listing per protocol * make some conditional statement easier to read (at least for me) New open points: - cipher_pref_check() doesn't save to PROTOS_OFFERED (was there before) (just stumbled over this but how about we also use get_protocol() / parse_tls_serverhello() - do we want run_allciphers() to be started by default? - $WIDE per default for run_cipher_per_proto() ? - probably better not to display text in round square brackets when there's no cipher: Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- SSLv2 (listed by strength) SSLv3 (server order) TLSv1 (server order) TLSv1.1 (server order) TLSv1.2 (server order) xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH 256 AESGCM 256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 [..] - when a server has no preference at all it shows in wide mode: Has server cipher order? no (NOT ok) -- only for TLS 1.3 Negotiated protocol TLSv1.3 Negotiated cipher TLS_AES_256_GCM_SHA384, 253 bit ECDH (X25519) Cipher listing per protocol Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC) ----------------------------------------------------------------------------------------------------------------------------- SSLv2 - SSLv3 - TLSv1 (no server order, thus listed by strength) xc014 ECDHE-RSA-AES256-SHA ECDH 521 AES 256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA [..] e.g. dev.testssl.sh --- testssl.sh | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/testssl.sh b/testssl.sh index 771f8a3..37c4e57 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3984,13 +3984,17 @@ ciphers_by_strength() { local has_dh_bits="$HAS_DH_BITS" # for local problem if it happens - out " " + "$wide" || out " " if ! "$using_sockets" && ! locally_supported "$proto"; then + pr_local_problem "Your $OPENSSL does not support $proto" + "$wide" && outln return 0 fi - "$wide" && outln - [[ $(has_server_protocol "${proto:1}") -eq 1 ]] && return 0 + if [[ $(has_server_protocol "${proto:1}") -eq 1 ]]; then + "$wide" && outln " - " + return 0 + fi # get a list of all the cipher suites to test nr_ciphers=0 @@ -4005,7 +4009,8 @@ ciphers_by_strength() { ciphers_found[nr_ciphers]=false sigalg[nr_ciphers]="" ossl_supported[nr_ciphers]=${TLS_CIPHER_OSSL_SUPPORTED[i]} - if "$using_sockets" && "$wide" && ! "$has_dh_bits" && ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then + if "$using_sockets" && "$wide" && ! "$has_dh_bits" && \ + ( [[ ${kx[nr_ciphers]} == "Kx=ECDH" ]] || [[ ${kx[nr_ciphers]} == "Kx=DH" ]] || [[ ${kx[nr_ciphers]} == "Kx=EDH" ]] ); then ossl_supported[nr_ciphers]=false fi if [[ ${#hexc} -eq 9 ]]; then @@ -4289,7 +4294,7 @@ run_cipher_per_proto() { outln neat_header while read proto proto_hex proto_text; do - pr_underline "$(printf -- "%b" "$proto_text")" + prln_underline "$(printf -- "%b" "$proto_text")" ciphers_by_strength "$proto" "$proto_hex" "$proto_text" "$using_sockets" "true" done <<< "$(tm_out " -ssl2 22 SSLv2\n -ssl3 00 SSLv3\n -tls1 01 TLS 1\n -tls1_1 02 TLS 1.1\n -tls1_2 03 TLS 1.2\n -tls1_3 04 TLS 1.3")" return 0 @@ -6527,23 +6532,32 @@ run_server_preference() { # TODO: Shouldn't say "Cipher order" if the server does not always impose an order. # TODO: In non-wide mode, need to distinguish between those order by server preference and those ordered by encryption strength. - pr_bold " Cipher order" - "$WIDE" && outln && neat_header + pr_bold " Cipher listing per protocol" + "$WIDE" && outln "\n" && neat_header while read proto_ossl proto_hex proto_txt; do if "$WIDE"; then pr_underline "$(printf -- "%b" "$proto_txt")" - if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + # TODO: If there's no cipher we should consider not displaying the text in the round brackets) + # the following takes care of that but only if we know the protocol is not supported + if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then + "$WIDE" && outln "\n - " + continue + fi + # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() + # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) + if [[ "$proto_ossl" == ssl2 ]] || \ + ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then if [[ "$proto_ossl" == ssl2 ]]; then - out " (listed by strength)" + outln " (listed by strength)" elif [[ "$proto_ossl" == tls1_3 ]]; then - out " (no server order, thus listed by strength)" + outln " (no server order, thus listed by strength)" else - pr_svrty_high " (no server order, thus listed by strength)" + prln_svrty_high " (no server order, thus listed by strength)" fi ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" else - out " (server order)" + outln " (server order)" cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" fi else @@ -6559,6 +6573,7 @@ run_server_preference() { outln return $ret + # end of run_server_preference() } # arg1: true if the list that is returned does not need to be ordered by preference. @@ -6663,8 +6678,6 @@ cipher_pref_check() { return 0 fi - [[ $(has_server_protocol "$p") -eq 1 ]] && return 0 - if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$WIDE")" @@ -6869,7 +6882,6 @@ cipher_pref_check() { order="$rfc_order" fi - "$WIDE" && outln if [[ -n "$order" ]]; then add_tls_offered "$p" yes if "$WIDE"; then From bb1c649513dbb8952dc3d64bf14a87ddca978c02 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 23 Apr 2020 14:52:14 -0400 Subject: [PATCH 046/211] Fix run_logjam() in --ssl-native mode This commit fixes a problem with run_logjam() when run in --ssl-native mode. If $OPENSSL does not support any DH export ciphers, then no test for such cipher is performed. However, the results of "test" is still checked, leading to testssl.sh incorrectly reporting that the server supports DH EXPORT ciphers. --- testssl.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..8c1e395 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16413,15 +16413,13 @@ run_logjam() { tls_sockets "03" "$exportdh_cipher_list_hex, 00,ff" sclient_success=$? [[ $sclient_success -eq 2 ]] && sclient_success=0 + [[ $sclient_success -eq 0 ]] && vuln_exportdh_ciphers=true elif [[ $nr_supported_ciphers -ne 0 ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -cipher $exportdh_cipher_list -connect $NODEIP:$PORT $PROXY $SNI") >$TMPFILE 2>$ERRFILE Date: Thu, 23 Apr 2020 15:04:06 -0400 Subject: [PATCH 047/211] Improve compatibility with OpenSSL 3.0 This commit fixes a couple of issues related to the use of testssl.sh with OpenSSL 3.0.0-alpha1. First, when the command line includes an unknown option (e.g., -ssl2), OpenSSL 3.0.0-alpha responds with "Unknown option: -ssl2" rather than "Option unknown option -ssl2". This commit addresses this by making the check for "unknown option" case insensitve. Second, the printing a DH key, OpenSSL 3.0.0-alpha1 labels the prime and the generator using "prime P:" and "generator G:" rather than just "prime:" and "generator:". This commit by changing testssl.sh to match on either string. --- testssl.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..2ea71a7 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4794,7 +4794,7 @@ run_client_simulation() { # locally_supported() { [[ -n "$2" ]] && out "$2 " - if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option"; then + if $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option"; then prln_local_problem "$OPENSSL doesn't support \"s_client $1\"" return 7 fi @@ -4816,7 +4816,7 @@ run_prototest_openssl() { local protos proto # check whether the protocol being tested is supported by $OPENSSL - $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aq "unknown option" && return 7 + $OPENSSL s_client "$1" -connect invalid. 2>&1 | grep -aiq "unknown option" && return 7 case "$1" in -ssl2) protos="-ssl2" ;; -ssl3) protos="-ssl3" ;; @@ -13684,8 +13684,8 @@ parse_tls_serverhello() { esac [[ -z "$key_bitstring" ]] && named_curve=0 && named_curve_str="" if "$HAS_PKEY" && [[ $named_curve -ne 0 ]] && [[ "${TLS13_KEY_SHARES[named_curve]}" =~ BEGIN ]]; then - ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -A 1000 "prime:")" - rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -A 1000 "prime:")" + ephemeral_param="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | grep -EA 1000 "prime:|prime P:")" + rfc7919_param="$($OPENSSL pkey -text -noout 2>>$ERRFILE <<< "${TLS13_KEY_SHARES[named_curve]}" | grep -EA 1000 "prime:|prime P:")" [[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str="" fi @@ -16302,7 +16302,7 @@ get_common_prime() { local -i lineno_matched=0 "$HAS_PKEY" || return 2 - dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:/,/generator:/' | grep -Ev "prime|generator")" + dh_p="$($OPENSSL pkey -pubin -text -noout 2>>$ERRFILE <<< "$key_bitstring" | awk '/prime:|prime P:/,/generator:|generator G:/' | grep -Ev "prime|generator")" dh_p="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$dh_p")")")" [[ "${dh_p:0:2}" == "00" ]] && dh_p="${dh_p:2}" DH_GROUP_LEN_P="$((4*${#dh_p}))" @@ -18094,18 +18094,18 @@ find_openssl_binary() { HAS_AES256_GCM=false HAS_ZLIB=false - $OPENSSL ciphers -s 2>&1 | grep -aq "unknown option" || \ + $OPENSSL ciphers -s 2>&1 | grep -aiq "unknown option" || \ OSSL_CIPHERS_S="-s" # This and all other occurences we do a little trick using "invalid." to avoid plain and # link level DNS lookups. See issue #1418 and https://tools.ietf.org/html/rfc6761#section-6.4 - $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL2=true - $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ssl3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_SSL3=true - $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_TLS13=true $OPENSSL genpkey -algorithm X448 -out - 2>&1 | grep -aq "not found" || \ @@ -18114,19 +18114,19 @@ find_openssl_binary() { $OPENSSL genpkey -algorithm X25519 -out - 2>&1 | grep -aq "not found" || \ HAS_X25519=true - $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_SSL2=true - $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -noservername -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NOSERVERNAME=true - $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -ciphersuites -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_CIPHERSUITES=true - $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_COMP=true - $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aq "unknown option" || \ + $OPENSSL s_client -no_comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_COMP=true OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") @@ -19551,7 +19551,7 @@ determine_optimal_proto() { elif "$all_failed" && ! "$ALL_FAILED_SOCKETS"; then if ! "$HAS_TLS13" && "$TLS13_ONLY"; then pr_magenta " $NODE:$PORT appears to support TLS 1.3 ONLY. You better use --openssl=" - if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aq "unknown option"; then + if ! "$OSSL_SHORTCUT" || [[ ! -x /usr/bin/openssl ]] || /usr/bin/openssl s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option"; then outln ignore_no_or_lame " Type \"yes\" to proceed and accept all scan problems" "yes" [[ $? -ne 0 ]] && exit $ERR_CLUELESS From 3e54f4e4cd158c11975222798316776b19add659 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Fri, 24 Apr 2020 13:32:26 +0200 Subject: [PATCH 048/211] Further changes to run_server_preference() In order not to provide redundant information run_allciphers() is now not being run via default (1). Therefore run_server_preference() runs always in wide mode. In order to archieve that cipher_pref_check() was modified to accept a fifth argument whether it'll run in wide mode. As of now cipher_pref_check() is only called by run_server_preference(), so the code referring to non-wide mode in cipher_pref_check() may also be deleted in the future. To provide a better view the run_fs() section is now being run after run_server_preference(). (1) saves also 5-6 seconds --- testssl.sh | 75 +++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/testssl.sh b/testssl.sh index 37c4e57..90c655c 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6530,47 +6530,35 @@ run_server_preference() { "$FAST" && using_sockets=false [[ $TLS_NR_CIPHERS == 0 ]] && using_sockets=false - # TODO: Shouldn't say "Cipher order" if the server does not always impose an order. - # TODO: In non-wide mode, need to distinguish between those order by server preference and those ordered by encryption strength. - pr_bold " Cipher listing per protocol" - "$WIDE" && outln "\n" && neat_header + pr_bold " Cipher per protocol" + outln "\n" && neat_header while read proto_ossl proto_hex proto_txt; do - if "$WIDE"; then - pr_underline "$(printf -- "%b" "$proto_txt")" - # TODO: If there's no cipher we should consider not displaying the text in the round brackets) - # the following takes care of that but only if we know the protocol is not supported - if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then - "$WIDE" && outln "\n - " - continue - fi - # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() - # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) - if [[ "$proto_ossl" == ssl2 ]] || \ - ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then - if [[ "$proto_ossl" == ssl2 ]]; then - outln " (listed by strength)" - elif [[ "$proto_ossl" == tls1_3 ]]; then - outln " (no server order, thus listed by strength)" - else - prln_svrty_high " (no server order, thus listed by strength)" - fi - ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" + pr_underline "$(printf -- "%b" "$proto_txt")" + # TODO: If there's no cipher we should consider not displaying the text in the round brackets) + # the following takes care of that but only if we know the protocol is not supported + if [[ $(has_server_protocol "$proto_ossl") -eq 1 ]]; then + outln "\n - " + continue + fi + # TODO: Also the fact that a protocol is not supported seems not to be saved by cipher_pref_check() + # (./testssl.sh --wide -p -P -E vs ./testssl.sh --wide -P -E ) + if [[ "$proto_ossl" == ssl2 ]] || \ + ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ + ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then + if [[ "$proto_ossl" == ssl2 ]]; then + outln " (listed by strength)" + elif [[ "$proto_ossl" == tls1_3 ]]; then + outln " (no server order, thus listed by strength)" else - outln " (server order)" - cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" + prln_svrty_high " (no server order, thus listed by strength)" fi + ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" else - if [[ "$proto_ossl" == ssl2 ]] || ( [[ "$proto_ossl" != tls1_3 ]] && ! "$has_cipher_order" ]] ) || \ - ( [[ "$proto_ossl" == tls1_3 ]] && ! "$has_tls13_cipher_order" ]] ); then - ciphers_by_strength "-$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "$WIDE" - else - cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" - fi + outln " (server order)" + cipher_pref_check "$proto_ossl" "$proto_hex" "$proto_txt" "$using_sockets" "true" fi done <<< "$(tm_out " ssl2 22 SSLv2\n ssl3 00 SSLv3\n tls1 01 TLSv1\n tls1_1 02 TLSv1.1\n tls1_2 03 TLSv1.2\n tls1_3 04 TLSv1.3\n")" outln - outln return $ret # end of run_server_preference() @@ -6658,6 +6646,7 @@ check_tls12_pref() { cipher_pref_check() { local p="$1" proto_hex="$2" proto="$3" local using_sockets="$4" + local wide="$5" # at the moment this is called ALWAYS via run_server_preference and ALWAYS w true local tested_cipher cipher order rfc_cipher rfc_order local overflow_probe_cipherlist="ALL:-ECDHE-RSA-AES256-GCM-SHA384:-AES128-SHA:-DES-CBC3-SHA" local -i i nr_ciphers nr_nonossl_ciphers num_bundles bundle_size bundle end_of_bundle success @@ -6680,11 +6669,11 @@ cipher_pref_check() { if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then - order="$(check_tls12_pref "$WIDE")" + order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" fi - if "$WIDE" || [[ -z "$order" ]]; then + if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do if [[ $p != tls1_3 ]]; then @@ -6713,7 +6702,7 @@ cipher_pref_check() { order+="$cipher " tested_cipher+=":-"$cipher "$FAST" && break - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_OSSL_NAME[i]} ]] && break done @@ -6843,7 +6832,7 @@ cipher_pref_check() { for (( i=0; i < nr_ciphers; i++ )); do [[ "$cipher" == ${rfc_ciph[i]} ]] && ciphers_found2[i]=true && break done - if "$WIDE"; then + if "$wide"; then for (( i=0; i < TLS_NR_CIPHERS; i++ )); do [[ "$cipher" == ${TLS_CIPHER_RFC_NAME[i]} ]] && break done @@ -6884,7 +6873,7 @@ cipher_pref_check() { if [[ -n "$order" ]]; then add_tls_offered "$p" yes - if "$WIDE"; then + if "$wide"; then for (( i=0 ; i Date: Sat, 25 Apr 2020 11:12:36 +0200 Subject: [PATCH 049/211] Negotiated protocol showed no warning for TLS 1.1/1.0 .. whereas the protocol section did that. This fixes the inconsistency. --- testssl.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0fd691b..eedbb3a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6419,12 +6419,12 @@ run_server_preference() { fileout "$jsonID" "OK" "Default protocol TLS1.2" ;; *TLSv1.1) - prln_svrty_good $default_proto - fileout "$jsonID" "OK" "Default protocol TLS1.1" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.1" ;; *TLSv1) - outln $default_proto - fileout "$jsonID" "INFO" "Default protocol TLS1.0" + prln_svrty_low $default_proto + fileout "$jsonID" "LOW" "Default protocol TLS1.0" ;; *SSLv2) prln_svrty_critical $default_proto From 0a859d7b986291ba8aca055ff33375d47f5c51f0 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 15:32:43 +0200 Subject: [PATCH 050/211] rename $p --> $proto_ossl in cipher_pref_check() plus remove redundant quotes for that --- testssl.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/testssl.sh b/testssl.sh index 90c655c..f9567af 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6282,7 +6282,7 @@ run_server_preference() { "$SSL_NATIVE" && using_sockets=false outln - pr_headlineln " Testing server preferences " + pr_headlineln " Testing server's cipher preferences " outln pr_bold " Has server cipher order? " @@ -6644,7 +6644,7 @@ check_tls12_pref() { cipher_pref_check() { - local p="$1" proto_hex="$2" proto="$3" + local proto_ossl="$1" proto_hex="$2" proto="$3" local using_sockets="$4" local wide="$5" # at the moment this is called ALWAYS via run_server_preference and ALWAYS w true local tested_cipher cipher order rfc_cipher rfc_order @@ -6658,17 +6658,17 @@ cipher_pref_check() { local ciphers_found_with_sockets order=""; ciphers_found_with_sockets=false - if [[ $p == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then + if [[ $proto_ossl == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi - if [[ $p == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then + if [[ $proto_ossl == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi - if ( [[ $p != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $p != ssl3 ]] || "$HAS_SSL3" ); then - if [[ $p == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then + if ( [[ $proto_ossl != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto_ossl != ssl3 ]] || "$HAS_SSL3" ); then + if [[ $proto_ossl == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" @@ -6676,7 +6676,7 @@ cipher_pref_check() { if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do - if [[ $p != tls1_3 ]]; then + if [[ $proto_ossl != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then ciphers_to_test="" for cipher in $ciphers_found; do @@ -6695,7 +6695,7 @@ cipher_pref_check() { [[ -z "$ciphers_to_test" ]] && break ciphers_to_test="-ciphersuites ${ciphers_to_test:1}" fi - $OPENSSL s_client $(s_client_options "$STARTTLS -"$p" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE + $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto_ossl" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || break cipher=$(get_cipher $TMPFILE) [[ -z "$cipher" ]] && break @@ -6710,7 +6710,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" + [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TMPFILE" quiet)" fi @@ -6739,9 +6739,9 @@ cipher_pref_check() { rfc_ciph[nr_nonossl_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" index[nr_nonossl_ciphers]=$i # Only test ciphers that are relevant to the protocol. - if [[ "$p" == tls1_3 ]]; then - [[ "${hexc:2:2}" == "13" ]] && nr_nonossl_ciphers+=1 - elif [[ "$p" == tls1_2 ]]; then + if [[ $proto_ossl == tls1_3 ]]; then + [[ "${hexc:2:2}" == 13 ]] && nr_nonossl_ciphers+=1 + elif [[ $proto_ossl == tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6756,7 +6756,7 @@ cipher_pref_check() { if [[ $nr_nonossl_ciphers -eq 0 ]]; then num_bundles=0 - elif [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + elif [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then num_bundles=1 bundle_size=$nr_nonossl_ciphers else @@ -6785,7 +6785,7 @@ cipher_pref_check() { i=${index[i]} ciphers_found[i]=true ciphers_found_with_sockets=true - if [[ $p != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + if [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then # Throw out the results found so far and start over using just sockets bundle=$num_bundles for (( i=0; i < TLS_NR_CIPHERS; i++ )); do @@ -6808,9 +6808,9 @@ cipher_pref_check() { ciphers_found2[nr_ciphers]=false hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}" rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" - if [[ "$p" == "tls1_3" ]]; then + if [[ $proto_ossl == "tls1_3" ]]; then [[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1 - elif [[ "$p" == "tls1_2" ]]; then + elif [[ $proto_ossl == "tls1_2" ]]; then [[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6840,7 +6840,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ "$p" == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet)" fi @@ -6872,12 +6872,12 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$p" yes + add_tls_offered "$proto_ossl" yes if "$wide"; then for (( i=0 ; i Date: Mon, 27 Apr 2020 16:51:45 +0200 Subject: [PATCH 051/211] Remember better protocol settings in ciphers_by_strength() / cipher_pref_check() ... in cases where the protcol section has not been run before. Also add " -\n" on the screen/html if protocol is not supported. Also for SSLv2 which can be supported but at the same time not offer any ciphers mention there will be an output on the screen. --- testssl.sh | 63 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/testssl.sh b/testssl.sh index f9567af..e4c5ab0 100755 --- a/testssl.sh +++ b/testssl.sh @@ -259,6 +259,7 @@ APP_TRAF_KEY_INFO="" # Information about the application traf TLS13_ONLY=false # Does the server support TLS 1.3 ONLY? OSSL_SHORTCUT=${OSSL_SHORTCUT:-false} # Hack: if during the scan turns out the OpenSSL binary suports TLS 1.3 would be a better choice, this enables it. TLS_EXTENSIONS="" +V2_HELLO_CIPHERSPEC_LENGTH=0 declare -r NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1" # alpn_protos needs to be space-separated, not comma-seperated, including odd ones observed @ facebook and others, old ones like h2-17 omitted as they could not be found declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 grpc-exp h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp" @@ -4045,7 +4046,7 @@ ciphers_by_strength() { # The OpenSSL ciphers function, prior to version 1.1.0, could only understand -ssl2, -ssl3, and -tls1. if [[ "$OSSL_NAME" =~ LibreSSL ]]; then ossl_ciphers_proto="" - elif [[ "$proto" == -ssl2 ]] || [[ "$proto" == -ssl3 ]] || \ + elif [[ $proto == -ssl2 ]] || [[ $proto == -ssl3 ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] || [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]]; then ossl_ciphers_proto="$proto" @@ -4072,23 +4073,32 @@ ciphers_by_strength() { done < <(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 'ALL' "$ossl_ciphers_proto -V") fi - if [[ "$proto" == -ssl2 ]]; then + if [[ $proto == -ssl2 ]]; then if "$using_sockets"; then sslv2_sockets "${sslv2_ciphers:2}" "true" - if [[ $? -eq 3 ]] && [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then - supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" - "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" - for (( i=0 ; i$TMPFILE 2>$ERRFILE Date: Mon, 27 Apr 2020 17:08:43 +0200 Subject: [PATCH 052/211] Renaming proto variables in cipher_pref_check() ... to be consistent with ciphers_by_strength: - proto --> proto_text - proto_ossl --> proto --- testssl.sh | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/testssl.sh b/testssl.sh index e4c5ab0..fe29a31 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4088,7 +4088,7 @@ ciphers_by_strength() { fi done else - outln " protocol support with no cipher " + outln " protocol supported with no cipher " fi else add_tls_offered ssl2 no @@ -6667,7 +6667,7 @@ check_tls12_pref() { # At the moment only called from run_server_preference() cipher_pref_check() { - local proto_ossl="$1" proto_hex="$2" proto="$3" + local proto="$1" proto_hex="$2" proto_text="$3" local using_sockets="$4" local wide="$5" # at the moment always = true local tested_cipher cipher order rfc_cipher rfc_order @@ -6681,17 +6681,17 @@ cipher_pref_check() { local ciphers_found_with_sockets order=""; ciphers_found_with_sockets=false - if [[ $proto_ossl == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then + if [[ $proto == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi - if [[ $proto_ossl == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then + if [[ $proto == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi - if ( [[ $proto_ossl != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto_ossl != ssl3 ]] || "$HAS_SSL3" ); then - if [[ $proto_ossl == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then + if ( [[ $proto != tls1_3 ]] || "$HAS_TLS13" ) && ( [[ $proto != ssl3 ]] || "$HAS_SSL3" ); then + if [[ $proto == tls1_2 ]] && "$SERVER_SIZE_LIMIT_BUG"; then order="$(check_tls12_pref "$wide")" [[ "${order:0:1}" == \ ]] && order="${order:1}" ciphers_found="$order" @@ -6699,7 +6699,7 @@ cipher_pref_check() { if "$wide" || [[ -z "$order" ]]; then tested_cipher=""; order=""; nr_ciphers_found=0 while true; do - if [[ $proto_ossl != tls1_3 ]]; then + if [[ $proto != tls1_3 ]]; then if [[ -n "$ciphers_found" ]]; then ciphers_to_test="" for cipher in $ciphers_found; do @@ -6718,7 +6718,7 @@ cipher_pref_check() { [[ -z "$ciphers_to_test" ]] && break ciphers_to_test="-ciphersuites ${ciphers_to_test:1}" fi - $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto_ossl" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE + $OPENSSL s_client $(s_client_options "$STARTTLS -"$proto" $BUGS $ciphers_to_test -connect $NODEIP:$PORT $PROXY $SNI") >$ERRFILE >$TMPFILE sclient_connect_successful $? $TMPFILE || break cipher=$(get_cipher $TMPFILE) [[ -z "$cipher" ]] && break @@ -6733,7 +6733,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file $TMPFILE)" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TMPFILE" quiet)" fi @@ -6762,9 +6762,9 @@ cipher_pref_check() { rfc_ciph[nr_nonossl_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" index[nr_nonossl_ciphers]=$i # Only test ciphers that are relevant to the protocol. - if [[ $proto_ossl == tls1_3 ]]; then + if [[ $proto == tls1_3 ]]; then [[ "${hexc:2:2}" == 13 ]] && nr_nonossl_ciphers+=1 - elif [[ $proto_ossl == tls1_2 ]]; then + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != 13 ]] && nr_nonossl_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6779,7 +6779,7 @@ cipher_pref_check() { if [[ $nr_nonossl_ciphers -eq 0 ]]; then num_bundles=0 - elif [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + elif [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then num_bundles=1 bundle_size=$nr_nonossl_ciphers else @@ -6808,7 +6808,7 @@ cipher_pref_check() { i=${index[i]} ciphers_found[i]=true ciphers_found_with_sockets=true - if [[ $proto_ossl != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then + if [[ $proto != tls1_2 ]] || ! "$SERVER_SIZE_LIMIT_BUG"; then # Throw out the results found so far and start over using just sockets bundle=$num_bundles for (( i=0; i < TLS_NR_CIPHERS; i++ )); do @@ -6831,9 +6831,9 @@ cipher_pref_check() { ciphers_found2[nr_ciphers]=false hexcode[nr_ciphers]="${hexc:2:2},${hexc:7:2}" rfc_ciph[nr_ciphers]="${TLS_CIPHER_RFC_NAME[i]}" - if [[ $proto_ossl == "tls1_3" ]]; then + if [[ $proto == tls1_3 ]]; then [[ "${hexc:2:2}" == "13" ]] && nr_ciphers+=1 - elif [[ $proto_ossl == "tls1_2" ]]; then + elif [[ $proto == tls1_2 ]]; then [[ "${hexc:2:2}" != "13" ]] && nr_ciphers+=1 elif [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA256 ]] && \ [[ ! "${TLS_CIPHER_RFC_NAME[i]}" =~ SHA384 ]] && \ @@ -6863,7 +6863,7 @@ cipher_pref_check() { normalized_hexcode[nr_ciphers_found]="$(normalize_ciphercode "${TLS_CIPHER_HEXCODE[i]}")" ciph[nr_ciphers_found]="${TLS_CIPHER_OSSL_NAME[i]}" kx[nr_ciphers_found]="${TLS_CIPHER_KX[i]}" - [[ $proto_ossl == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" + [[ $proto == tls1_3 ]] && kx[nr_ciphers_found]="$(read_dhtype_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt")" if ( [[ ${kx[nr_ciphers_found]} == Kx=ECDH ]] || [[ ${kx[nr_ciphers_found]} == Kx=DH ]] || [[ ${kx[nr_ciphers_found]} == Kx=EDH ]] ); then kx[nr_ciphers_found]+=" $(read_dhbits_from_file "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" quiet)" fi @@ -6895,31 +6895,31 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$proto_ossl" yes + add_tls_offered "$proto" yes if "$wide"; then for (( i=0 ; i Date: Mon, 27 Apr 2020 17:12:25 +0200 Subject: [PATCH 053/211] Rename add_tls_offered --> add_proto_offered ... last but not least SSLv2 and SSLv3 are no TLS protocols --- testssl.sh | 188 ++++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/testssl.sh b/testssl.sh index fe29a31..a85be9d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4077,7 +4077,7 @@ ciphers_by_strength() { if "$using_sockets"; then sslv2_sockets "${sslv2_ciphers:2}" "true" if [[ $? -eq 3 ]] ; then - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes if [[ "$V2_HELLO_CIPHERSPEC_LENGTH" -ne 0 ]]; then supported_sslv2_ciphers="$(grep "Supported cipher: " "$TEMPDIR/$NODEIP.parse_sslv2_serverhello.txt")" "$wide" && "$SHOW_SIGALGO" && s="$(read_sigalg_from_file "$HOSTCERT")" @@ -4091,14 +4091,14 @@ ciphers_by_strength() { outln " protocol supported with no cipher " fi else - add_tls_offered ssl2 no + add_proto_offered ssl2 no "$wide" && outln " - " fi else $OPENSSL s_client $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY -ssl2 >$TMPFILE 2>$ERRFILE comes from run_prototest_openssl fileout "$jsonID" "HIGH" "$supported_no_ciph1" - add_tls_offered ssl3 yes + add_proto_offered ssl3 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5153,10 +5153,10 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0301" latest_supported_string="TLSv1.0" - add_tls_offered tls1 yes + add_proto_offered tls1 yes ;; # nothing wrong with it -- per se 1) out "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "not offered" # neither good or bad @@ -5166,7 +5166,7 @@ run_protocols() { fi ;; 2) pr_svrty_medium "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5187,19 +5187,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "likely not offered" - add_tls_offered tls1 no + add_proto_offered tls1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1 yes + add_proto_offered tls1 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5231,10 +5231,10 @@ run_protocols() { fileout "$jsonID" "LOW" "offered (deprecated)" latest_supported="0302" latest_supported_string="TLSv1.1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes ;; # nothing wrong with it 1) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ -z $latest_supported ]]; then outln fileout "$jsonID" "INFO" "is not offered" # neither good or bad @@ -5244,7 +5244,7 @@ run_protocols() { fi ;; 2) out "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no if [[ "$DETECTED_TLS_VERSION" == "$latest_supported" ]]; then [[ $DEBUG -ge 1 ]] && tm_out " -- downgraded" outln @@ -5268,19 +5268,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "OK" "not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "is not offered" - add_tls_offered tls1_1 no + add_proto_offered tls1_1 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_1 yes + add_proto_offered tls1_1 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5342,9 +5342,9 @@ run_protocols() { fileout "$jsonID" "OK" "offered" latest_supported="0303" latest_supported_string="TLSv1.2" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; # GCM cipher in TLS 1.2: very good! - 1) add_tls_offered tls1_2 no + 1) add_proto_offered tls1_2 no if "$offers_tls13"; then out "not offered" else @@ -5362,7 +5362,7 @@ run_protocols() { fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi ;; - 2) add_tls_offered tls1_2 no + 2) add_proto_offered tls1_2 no pr_svrty_medium "not offered and downgraded to a weaker protocol" if [[ "$tls12_detected_version" == 0300 ]]; then detected_version_string="SSLv3" @@ -5390,19 +5390,19 @@ run_protocols() { ;; 3) out "not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely "; pr_svrty_medium "not offered, " fileout "$jsonID" "MEDIUM" "not offered" - add_tls_offered tls1_2 no + add_proto_offered tls1_2 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected, but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_2 yes + add_proto_offered tls1_2 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -5496,7 +5496,7 @@ run_protocols() { fi latest_supported="0304" latest_supported_string="TLSv1.3" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 1) pr_svrty_low "not offered" if [[ -z $latest_supported ]]; then @@ -5506,7 +5506,7 @@ run_protocols() { prln_svrty_critical " -- connection failed rather than downgrading to $latest_supported_string" fileout "$jsonID" "CRITICAL" "connection failed rather than downgrading to $latest_supported_string" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 2) if [[ "$DETECTED_TLS_VERSION" == 0300 ]]; then detected_version_string="SSLv3" @@ -5529,23 +5529,23 @@ run_protocols() { prln_svrty_critical " -- server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fileout "$jsonID" "CRITICAL" "server responded with version number ${DETECTED_TLS_VERSION:0:2}.${DETECTED_TLS_VERSION:2:2}" fi - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no ;; 3) out "not offered " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "TLS downgraded to STARTTLS plaintext"; outln fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext" ;; 4) out "likely not offered, " fileout "$jsonID" "INFO" "not offered" - add_tls_offered tls1_3 no + add_proto_offered tls1_3 no pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm" fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}" ;; 5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl fileout "$jsonID" "INFO" "$supported_no_ciph1" - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes ;; 7) if "$using_sockets" ; then # can only happen in debug mode @@ -6330,14 +6330,14 @@ run_server_preference() { "ephemeralkey" sclient_success=$? if [[ $sclient_success -eq 0 ]]; then - add_tls_offered tls1_3 yes + add_proto_offered tls1_3 yes elif [[ $sclient_success -eq 2 ]]; then sclient_success=0 # 2: downgraded case $DETECTED_TLS_VERSION in - 0303) add_tls_offered tls1_2 yes ;; - 0302) add_tls_offered tls1_1 yes ;; - 0301) add_tls_offered tls1 yes ;; - 0300) add_tls_offered ssl3 yes ;; + 0303) add_proto_offered tls1_2 yes ;; + 0302) add_proto_offered tls1_1 yes ;; + 0301) add_proto_offered tls1 yes ;; + 0300) add_proto_offered ssl3 yes ;; esac fi if [[ $sclient_success -eq 0 ]] ; then @@ -6895,7 +6895,7 @@ cipher_pref_check() { fi if [[ -n "$order" ]]; then - add_tls_offered "$proto" yes + add_proto_offered "$proto" yes if "$wide"; then for (( i=0 ; i$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE /dev/null)") debugme tm_out " ($lines lines) " - add_tls_offered ssl2 yes + add_proto_offered ssl2 yes if [[ "$lines" -gt 1 ]]; then nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3)) if [[ 0 -eq "$nr_ciphers_detected" ]]; then @@ -16684,7 +16684,7 @@ run_beast(){ $OPENSSL s_client $(s_client_options "-state -"${proto}" $STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY $SNI") 2>>$ERRFILE >$TMPFILE $TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then all_failed=false - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes break fi done @@ -19479,11 +19479,11 @@ determine_optimal_proto() { tmp=${tmp/\./_} tmp=${tmp/v/} tmp="$(tolower $tmp)" - add_tls_offered "${tmp}" yes + add_proto_offered "${tmp}" yes debugme echo "one proto determined: $tmp" OPTIMAL_PROTO="" else - add_tls_offered "${proto/-/}" yes + add_proto_offered "${proto/-/}" yes OPTIMAL_PROTO="$proto" fi all_failed=false From 680aff48e4c999a859c2af4862968537f646c632 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 17:19:30 +0200 Subject: [PATCH 054/211] Update documentation related to extended run_server_preference() --- doc/testssl.1 | 9 +++------ doc/testssl.1.html | 8 +++----- doc/testssl.1.md | 8 +++----- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index a8e8626..460996d 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -46,10 +46,10 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 2) standard cipher categories to give you upfront an idea for the ciphers supported . .P -3) checks forward secrecy: ciphers and elliptical curves +3) server's cipher preferences (server order) . .P -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves . .P 5) server defaults (certificate info, TLS extensions, session information) @@ -61,10 +61,7 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions 7) vulnerabilities . .P -8) testing each of 370 preconfigured ciphers -. -.P -9) client simulation +8) client simulation . .SH "OPTIONS AND PARAMETERS" Options are either short or long options\. Any long or short option requiring a value can be called with or without an equal sign\. E\.g\. \fBtestssl\.sh \-t=smtp \-\-wide \-\-openssl=/usr/bin/openssl \fR (short options with equal sign) is equivalent to \fBtestssl\.sh \-\-starttls smtp \-\-wide \-\-openssl /usr/bin/openssl \fR (long option without equal sign)\. Some command line options can also be preset via ENV variables\. \fBWIDE=true OPENSSL=/usr/bin/openssl testssl\.sh \-\-starttls=smtp \fR would be the equivalent to the aforementioned examples\. Preference has the command line over any environment variables\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 5a6c392..4746b25 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -123,9 +123,9 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

2) standard cipher categories to give you upfront an idea for the ciphers supported

-

3) checks forward secrecy: ciphers and elliptical curves

+

3) server's cipher preferences (server order)

-

4) server preferences (server order)

+

4) forward secrecy: ciphers and elliptical curves

5) server defaults (certificate info, TLS extensions, session information)

@@ -133,9 +133,7 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

7) vulnerabilities

-

8) testing each of 370 preconfigured ciphers

- -

9) client simulation

+

8) client simulation

OPTIONS AND PARAMETERS

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 8f29a92..ac54ec4 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -42,9 +42,9 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 2) standard cipher categories to give you upfront an idea for the ciphers supported -3) checks forward secrecy: ciphers and elliptical curves +3) server's cipher preferences (server order?) -4) server preferences (server order) +4) forward secrecy: ciphers and elliptical curves 5) server defaults (certificate info, TLS extensions, session information) @@ -52,9 +52,7 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 7) vulnerabilities -8) testing each of 370 preconfigured ciphers - -9) client simulation +8) client simulation ## OPTIONS AND PARAMETERS From 50d10d00f7afabc81ec142d1a56d9a0bcecea244 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 27 Apr 2020 19:19:19 +0200 Subject: [PATCH 055/211] Add latest changes including the one since 3.0 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 289f81a..675f757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ ## Change Log +### Features implemented / improvements in 3.1dev + +* Extend Server (cipher) preference: always now in wide mode instead of running all ciphers in the end (per default) +* Improved compatibility with OpenSSL 3.0 +* Renamed PFS/perfect forward secrecy --> FS/forward secrecy +* Improved mass testing +* Align better colors of ciphers with standard cipherlists +* Added several ciphers to colored ciphers +* Percent output char problem fixed +* Several display/output fixes +* Security fix: DNS input +* Don't use external pwd anymore +* Rating (pending) + ### Features implemented / improvements in 3.0 * Full support of TLS 1.3, shows also drafts supported From 88c04f534525685da43f6d301e0be2f1a030274c Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 28 Apr 2020 10:06:29 +0200 Subject: [PATCH 056/211] Relax the possible GPL license contradiction ... see also #1590 --- Readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 7454f79..071828e 100644 --- a/Readme.md +++ b/Readme.md @@ -30,9 +30,12 @@ cryptographic flaws. ### License This software is free. You can use it under the terms of GPLv2, see LICENSE. -In addition starting from version 3.0rc1 if you're offering a scanner based on testssl.sh -as a public and / or paid service in the internet you need to mention to your audience that you're using -this program and where to get this program from. + +Attribution is important for the future of this project -- also in the +internet. Thus if you're offering a scanner based on testssl.sh as a public and/or +paid service in the internet you are strongly encouraged to mention to your audience +that you're using this program and where to get this program from. That helps us +to get bugfixes, other feedback and more contributions. ### Compatibility From 13a76bc7198577f72cfb8f91306ef745489ac9c1 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 28 Apr 2020 13:35:24 +0200 Subject: [PATCH 057/211] (try to) resolve merge conflict --- CHANGELOG.md | 14 +++++++++++--- doc/testssl.1.md | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26c1e23..2b2a34b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,19 @@ ## Change Log -### Features implemented / improvements in 3.1 -* Renamed PFS into FS +### Features implemented / improvements in 3.1dev + +* Extend Server (cipher) preference: always now in wide mode instead of running all ciphers in the end (per default) +* Improved compatibility with OpenSSL 3.0 +* Renamed PFS/perfect forward secrecy --> FS/forward secrecy * Improved mass testing +* Align better colors of ciphers with standard cipherlists +* Added several ciphers to colored ciphers +* Percent output char problem fixed +* Several display/output fixes * Security fix: DNS input -* Rating +* Don't use external pwd anymore +* Rating (via SSL Labs) ### Features implemented / improvements in 3.0 diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 1006684..e81ae78 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -40,9 +40,9 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 1) SSL/TLS protocol check -2) standard cipher categories to give you upfront an idea for the ciphers supported +2) checks forward secrecy: ciphers and elliptical curves -3) checks forward secrecy: ciphers and elliptical curves +3) standard cipher categories to give you upfront an idea for the ciphers supported 4) server preferences (server order) @@ -54,9 +54,9 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`. 8) testing each of 370 preconfigured ciphers -9) client simulation +8) client simulation -10) Result of script in form of a grade +9) Result of script in form of a grade ## OPTIONS AND PARAMETERS From db84e5c87c853758e7af7ac1689f94d423952692 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 28 Apr 2020 13:38:23 +0200 Subject: [PATCH 058/211] Add grade cap reasons and warnings to JSON/CSV --- testssl.sh | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/testssl.sh b/testssl.sh index 75fbd7a..3971364 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20541,7 +20541,7 @@ run_rating() { local c1_score c2_score c3_score c1_wscore c2_wscore c3_wscore local c1_worst c1_best local c3_worst c3_best c3_worst_cb c3_best_cb - local old_ifs=$IFS sorted_reasons sorted_warnings reason_loop=0 warning_loop=0 + local old_ifs=$IFS sorted_reasons sorted_warnings reason_nr=0 warning_nr=0 outln "\n"; pr_headlineln " Rating (experimental) " @@ -20705,35 +20705,25 @@ run_rating() { # Pretty print - again, it's just nicer to read for reason in "${sorted_reasons[@]}"; do - if [[ $reason_loop -eq 0 ]]; then + if [[ $reason_nr -eq 0 ]]; then pr_bold " Grade cap reasons "; outln "$reason" - let reason_loop++ else outln " $reason" fi + let reason_nr++ + fileout "grade_cap_reason_${reason_nr}" "INFO" "$reason" done for warning in "${sorted_warnings[@]}"; do - if [[ $warning_loop -eq 0 ]]; then - pr_bold " Grade warning "; prln_svrty_medium "$warning" - let warning_loop++ + if [[ $warning_nr -eq 0 ]]; then + pr_bold " Grade warning "; prln_svrty_medium "$warning" else - prln_svrty_medium " $warning" + prln_svrty_medium " $warning" fi + let warning_nr++ + fileout "grade_cap_warning_${warning_nr}" "INFO" "$warning" done - case $GRADE_CAP in - # A-E: WIP - A) fileout "grade_cap_reasons" "INFO" "" ;; - B) fileout "grade_cap_reasons" "INFO" "" ;; - C) fileout "grade_cap_reasons" "INFO" "" ;; - D) fileout "grade_cap_reasons" "INFO" "" ;; - E) fileout "grade_cap_reasons" "INFO" "" ;; - M) fileout "grade_cap_reasons" "INFO" "SAN / CN mismatch" ;; - F) fileout "grade_cap_reasons" "INFO" "Severe vulnerability or cryptographic problem" ;; - T) fileout "grade_cap_reasons" "INFO" "Issue with certificate" ;; - esac - return 0 } From 97ac4c452e441b2e7f34bc573f19c60d8b8b0b5d Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 23 Apr 2020 11:20:46 +0200 Subject: [PATCH 060/211] Update documentation (ADDITIONAL_CA_FILES -> ADDTL_CA_FILES) which happened in d44a643fab6be6755a917f85ed491c38990d15ae in testssl.sh . This fixes it in the related files. See also #1581 --- Coding_Convention.md | 2 +- doc/testssl.1 | 4 ++-- doc/testssl.1.html | 4 ++-- doc/testssl.1.md | 5 +++-- etc/README.md | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Coding_Convention.md b/Coding_Convention.md index 933b30c..03122d1 100644 --- a/Coding_Convention.md +++ b/Coding_Convention.md @@ -65,7 +65,7 @@ Bash is actually quite powerful -- not only with respect to sockets. It's not as ### Misc -* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDITIONAL_CA_FILES`) +* If you're implementing a new feature a cmd line switch, there has to be also a global ENV variable which can be used without the switch (see e.g. `SNEAKY`, `ASSUME_HTTP` or `ADDTL_CA_FILES`) * Test before doing a PR! Best if you check with two bad and two good examples which should then work as expected. Maybe compare results e.g. with SSLlabs. * Unit tests are done automatically done with Perl using Travis. The trigger is `~/.travis.yml`. The general documentation for [Test::More](https://perldoc.perl.org/Test/More.html) is a good start. You are encouraged to write own checks. You can use e.g. `t/20_baseline_ipv4_http.t` as an example. * If it's an OpenSSL feature you want to use and it could be not available for older OpenSSL versions testssl.sh needs to find out whether OpenSSL has that feature. Best do this with OpenSSL itself and not by checking the version as some vendors do backports. See the examples for `HAS_SSL2` or proxy option check of OpenSSL in `check_proxy()`. diff --git a/doc/testssl.1 b/doc/testssl.1 index 9c0f684..a8e8626 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -176,7 +176,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-phone\-out\fR Checking for revoked certificates via CRL and OCSP is not done per default\. This switch instructs testssl\.sh to query external \-\- in a sense of the current run \-\- URIs\. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl\.sh doesn\'t handle\. PHONE_OUT is the environment variable for this which needs to be set to true if you want this\. . .P -\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDITIONAL_CA_FILES is the environment variable for this\. +\fB\-\-add\-ca \fR enables you to add your own CA(s) for trust chain checks\. \fBcafile\fR can be a single path or multiple paths as a comma separated list of root CA files\. Internally they will be added during runtime to all CA stores\. This is (only) useful for internal hosts whose certificates is issued by internal CAs\. Alternatively ADDTL_CA_FILES is the environment variable for this\. . .SS "SINGLE CHECK OPTIONS" Any single check switch supplied as an argument prevents testssl\.sh from doing a default run\. It just takes this and if supplied other options and runs them \- in the order they would also appear in the default run\. @@ -282,7 +282,7 @@ Certificate Transparency info (if provided by server)\. .IP "" 0 . .P -For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDITIONAL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. +For the trust chain check 5 certificate stores are provided\. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed \- in addition the ones which succeeded are displayed too\. You can configure your own CA via ADDTL_CA_FILES, see section \fBFILES\fR below\. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated\. Also for multiple server certificates are being checked for as well as for the certificate reply to a non\-SNI (Server Name Indication) client hello to the IP address\. Regarding the TLS clock skew: it displays the time difference to the client\. Only a few TLS stacks nowadays still support this and return the local clock \fBgmt_unix_time\fR, e\.g\. IIS, openssl < 1\.0\.1f\. In addition to the HTTP date you could e\.g\. derive that there are different hosts where your TLS and your HTTP request ended \-\- if the time deltas differ significantly\. . .P \fB\-x , \-\-single\-cipher \fR tests matched \fBpattern\fR of ciphers against a server\. Patterns are similar to \fB\-V pattern , \-\-local pattern\fR, see above about matching\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index e7f3c34..5a6c392 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -221,7 +221,7 @@ in /etc/hosts. The use of the switch is only useful if you either

--phone-out Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this.

--add-ca <cafile> enables you to add your own CA(s) for trust chain checks. cafile can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDITIONAL_CA_FILES is the environment variable for this.

+ADDTL_CA_FILES is the environment variable for this.

SINGLE CHECK OPTIONS

@@ -278,7 +278,7 @@ ADDITIONAL_CA_FILES is the environment variable for this.

For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section FILES below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock gmt_unix_time, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly.

-x <pattern>, --single-cipher <pattern> tests matched pattern of ciphers against a server. Patterns are similar to -V pattern , --local pattern, see above about matching.

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index e81ae78..96a774c 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -144,7 +144,8 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. -`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively ADDITIONAL_CA_FILES is the environment variable for this. +`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively +ADDTL_CA_FILES is the environment variable for this. ### SINGLE CHECK OPTIONS @@ -192,7 +193,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a - Certificate Transparency info (if provided by server). For the trust chain check 5 certificate stores are provided. If the test against one of the trust stores failed, the one is being identified and the reason for the failure is displayed - in addition the ones which succeeded are displayed too. -You can configure your own CA via ADDITIONAL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. +You can configure your own CA via ADDTL_CA_FILES, see section `FILES` below. If the server provides no matching record in Subject Alternative Name (SAN) but in Common Name (CN), it will be indicated as this is deprecated. Also for multiple server certificates are being checked for as well as for the certificate reply to a non-SNI (Server Name Indication) client hello to the IP address. Regarding the TLS clock skew: it displays the time difference to the client. Only a few TLS stacks nowadays still support this and return the local clock `gmt_unix_time`, e.g. IIS, openssl < 1.0.1f. In addition to the HTTP date you could e.g. derive that there are different hosts where your TLS and your HTTP request ended -- if the time deltas differ significantly. `-x , --single-cipher ` tests matched `pattern` of ciphers against a server. Patterns are similar to `-V pattern , --local pattern`, see above about matching. diff --git a/etc/README.md b/etc/README.md index 3437195..fc619cc 100644 --- a/etc/README.md +++ b/etc/README.md @@ -18,7 +18,7 @@ The certificate trust stores were retrieved from Google Chromium uses basically the trust stores above, see https://www.chromium.org/Home/chromium-security/root-ca-policy. -If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDITIONAL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. +If you want to check trust against e.g. a company internal CA you need to use ``./testssl.sh --add-ca companyCA1.pem,companyCA2.pem `` or ``ADDTL_CA_FILES=companyCA1.pem,companyCA2.pem ./testssl.sh ``. #### Further files From a9d28949fe27605e2bb365b2dd855d9cf4c8eb08 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 28 Apr 2020 21:13:36 +0200 Subject: [PATCH 061/211] Clarify responsilility for rating --- doc/testssl.1.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 96a774c..1f5be5d 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -390,9 +390,9 @@ Except the environment variables mentioned above which can replace command line ### RATING This program has a near-complete implementation of SSL Labs's '[SSL Server Rating Guide](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide)'. -This is *not* a reimplementation of the [SS LLab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but a implementation of the above rating specification, slight discrepancies might occur! +This is *not* a 100% reimplementation of the [SSL Lab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide. -Disclaimer: Having a good grade does **NOT** necessary equal to having good security! Never rely solely on a good rating! +Disclaimer: Having a good grade is **NOT** necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it. As of writing, these checks are missing: * GOLDENDOODLE - should be graded **F** if vulnerable @@ -435,7 +435,7 @@ When a new revision of the rating specification comes around, the following has testssl.sh testssl.sh -does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation, and rating. +does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, FS, server defaults, vulnerabilities, client simulation, and rating. testssl.sh testssl.net:443 From 3db9d74c2162f334023ab49b7401e83204faefa9 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 29 Apr 2020 10:13:22 -0400 Subject: [PATCH 062/211] Ticketbleed and TLS 1.3 run_ticketbleed() and sub_session_ticket_tls() each include one call to "$OPENSSL s_client". For each of these calls the expected response is a TLS 1.2 or earlier ServerHello. However, if $OPENSSL supports TLS 1.3, then a TLS 1.3 ClientHello will be sent. This commit fixes this problem in two ways. For the call in run_ticketbleed(), "-no_tls1_3" is added to the command line if "$OPENSSL" supports TLS 1.3. For the call in sub_session_ticket_tls(), this commit changes the function so that the same ClientHello version is sent as will sent by run_ticketbleed() via sockets. --- testssl.sh | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/testssl.sh b/testssl.sh index 995196f..b7f60ee 100755 --- a/testssl.sh +++ b/testssl.sh @@ -15134,13 +15134,14 @@ run_ccs_injection(){ } sub_session_ticket_tls() { + local tls_proto="$1" local sessticket_tls="" #FIXME: we likely have done this already before (either @ run_server_defaults() or at least the output # from a previous handshake) --> would save 1x connect. We have TLS_TICKET but not yet the ticket itself #FIXME #ATTENTION: we DO NOT use SNI here as we assume ticketbleed is a vulnerability of the TLS stack. If we'd do SNI here, we'd also need # it in the ClientHello of run_ticketbleed() otherwise the ticket will be different and the whole thing won't work! # - sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $OPTIMAL_PROTO $PROXY -connect $NODEIP:$PORT") $ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" + sessticket_tls="$($OPENSSL s_client $(s_client_options "$BUGS $tls_proto $PROXY -connect $NODEIP:$PORT") $ERRFILE | awk '/TLS session ticket:/,/^$/' | awk '!/TLS session ticket/')" sessticket_tls="$(sed -e 's/^.* - /x/g' -e 's/ .*$//g' <<< "$sessticket_tls" | tr '\n' ',')" sed -e 's/ /,x/g' -e 's/-/,x/g' <<< "$sessticket_tls" @@ -15149,6 +15150,7 @@ sub_session_ticket_tls() { # see https://blog.filippo.io/finding-ticketbleed/ | https://filippo.io/ticketbleed/ run_ticketbleed() { + local tls_hexcode tls_proto="" local session_tckt_tls="" local -i len_ch=300 # fixed len of prepared clienthello below local sid="x00,x0B,xAD,xC0,xDE,x00," # some abitratry bytes @@ -15186,25 +15188,26 @@ run_ticketbleed() { fi if [[ 0 -eq $(has_server_protocol tls1) ]]; then - tls_hexcode="x03, x01" + tls_hexcode="x03, x01"; tls_proto="-tls1" elif [[ 0 -eq $(has_server_protocol tls1_1) ]]; then - tls_hexcode="x03, x02" + tls_hexcode="x03, x02"; tls_proto="-tls1_1" elif [[ 0 -eq $(has_server_protocol tls1_2) ]]; then - tls_hexcode="x03, x03" + tls_hexcode="x03, x03"; tls_proto="-tls1_2" elif [[ 0 -eq $(has_server_protocol ssl3) ]]; then - tls_hexcode="x03, x00" + tls_hexcode="x03, x00"; tls_proto="-ssl3" else # no protocol for some reason defined, determine TLS versions offered with a new handshake - $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -connect $NODEIP:$PORT $PROXY") >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE Date: Thu, 30 Apr 2020 10:26:56 -0400 Subject: [PATCH 063/211] Use $HAS_X25519 and $HAS_X448 generate_key_share_extension() and prepare_tls_clienthello() currently check the $OPENSSL version number to determine whether X25519 and X448 are supported. The commit changes these functions to use $HAS_X25519 and $HAS_X448. --- testssl.sh | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..a8a4bf4 100755 --- a/testssl.sh +++ b/testssl.sh @@ -13835,26 +13835,13 @@ generate_key_share_extension() { # with X25519 keys, so don't include the X25519 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.0* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 29 ]] && [[ "$2" == all ]] && ! "$HAS_X25519" && continue # Versions of OpenSSL prior to 1.1.1 cannot perform operations # with X448 keys, so don't include the X448 key share # if the server's response needs to be decrypted and an # older version of OpenSSL is being used. - if [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]]; then - [[ "$OSSL_NAME" =~ LibreSSL ]] && continue - if [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 1.1.1* ]] && \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR != 3.0.0* ]]; then - continue - fi - fi + [[ $i -gt 12 ]] && [[ $group -eq 30 ]] && [[ "$2" == all ]] && ! "$HAS_X448" && continue # NOTE: The public keys could be extracted from the private keys # (TLS13_KEY_SHARES) using $OPENSSL, but only OpenSSL 1.1.0 and newer can @@ -14016,9 +14003,7 @@ prepare_tls_clienthello() { 00, 01, 00, 02, 00, 03, 00, 0f, 00, 10, 00, 11" elif [[ 0x$tls_low_byte -gt 0x03 ]]; then # Supported Groups Extension - if [[ ! "$process_full" =~ all ]] || ( [[ ! "$OSSL_NAME" =~ LibreSSL ]] && \ - ( [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.1* ]] || \ - [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 3.0.0* ]] ) ); then + if [[ ! "$process_full" =~ all ]] || ( "$HAS_X25519" && "$HAS_X448" ); then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths @@ -14027,13 +14012,13 @@ prepare_tls_clienthello() { # OpenSSL prior to 1.1.1 does not support X448, so list it as the least # preferred option if the response needs to be decrypted, and do not # list it at all if the response MUST be decrypted. - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == 1.1.0* ]] && [[ "$process_full" == all+ ]]; then + elif "$HAS_X25519" && [[ "$process_full" == all+ ]]; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,0e, 00,0c, # lengths 00,1d, 00,17, 00,18, 00,19, 01,00, 01,01" - elif [[ $OSSL_VER_MAJOR.$OSSL_VER_MINOR == "1.1.0"* ]]; then + elif "$HAS_X25519"; then extension_supported_groups=" 00,0a, # Type: Supported Groups, see RFC 8446 00,10, 00,0e, # lengths From cb67d9141786656002682d1780122c221e1aa4b6 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 30 Apr 2020 10:37:12 -0400 Subject: [PATCH 064/211] Improve compatibility with LibreSSL 3.0.2 and earlier This commit addresses two compatibility issues with LibreSSL. First, with LibreSSL, "$OPENSSL s_client" does not support the "-curves" option, so the "-groups" option needs to be used instead. Note that with LibreSSL, the command line "$OPENSSL s_client -groups $curve -connect invalid." will not work, as it will complain "no port defined," but will not indicate whether the specified curve is supported. Adding a port number fixes that problem. (There does not seem to be a need to include a port number for other tests, such as whether the "-curves" option itself is supported.) Second, including "-out -" in the command line for "$OPENSSL genpkey" causes LibreSSL to create a file with the name "-" if the algorithm is supported. This is not an issue at the moment, since LibreSSL's genpkey does not support X25519 or X448. However, both genpkey with both OpenSSL and LibreSSL uses stdout as the default output if no "-out" is specified, so the "-out -" is not necessary. --- testssl.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..3125688 100755 --- a/testssl.sh +++ b/testssl.sh @@ -297,6 +297,7 @@ OSSL_VER_MINOR=0 OSSL_VER_APPENDIX="none" CLIENT_PROB_NO=1 HAS_DH_BITS=${HAS_DH_BITS:-false} # initialize openssl variables +HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_SSL2=false HAS_SSL3=false @@ -2034,6 +2035,7 @@ s_client_options() { # (e.g. client simulations) we replace it with the name which OpenSSL understands # This shouldn't be needed. We have this here as a last resort if [[ "$1" =~ " -curves " ]]; then + ! "$HAS_CURVES" && options="${options// -curves / -groups }" [[ "$1" =~ secp192r1 ]] && options="${options//secp192r1/prime192v1}" [[ "$1" =~ secp256r1 ]] && options="${options//secp256r1/prime256v1}" fi @@ -18035,6 +18037,7 @@ find_openssl_binary() { HAS_CIPHERSUITES=false HAS_COMP=false HAS_NO_COMP=false + HAS_CURVES=false OSSL_SUPPORTED_CURVES="" HAS_PKEY=false HAS_PKUTIL=false @@ -18067,10 +18070,10 @@ find_openssl_binary() { $OPENSSL s_client -tls1_3 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_TLS13=true - $OPENSSL genpkey -algorithm X448 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X448 2>&1 | grep -aq "not found" || \ HAS_X448=true - $OPENSSL genpkey -algorithm X25519 -out - 2>&1 | grep -aq "not found" || \ + $OPENSSL genpkey -algorithm X25519 2>&1 | grep -aq "not found" || \ HAS_X25519=true $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ @@ -18090,10 +18093,18 @@ find_openssl_binary() { OPENSSL_NR_CIPHERS=$(count_ciphers "$(actually_supported_osslciphers 'ALL:COMPLEMENTOFALL' 'ALL')") - for curve in "${curves_ossl[@]}"; do - $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" - [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " - done + if $OPENSSL s_client -curves "${curves_ossl[0]}" -connect invalid. 2>&1 | grep -aiq "unknown option"; then + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -groups $curve -connect invalid.:8443 2>&1 | grep -Eiaq "Error with command|unknown option|Failed to set groups" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + else + HAS_CURVES=true + for curve in "${curves_ossl[@]}"; do + $OPENSSL s_client -curves $curve -connect invalid. 2>&1 | grep -Eiaq "Error with command|unknown option" + [[ $? -ne 0 ]] && OSSL_SUPPORTED_CURVES+=" $curve " + done + fi $OPENSSL pkey -help 2>&1 | grep -q Error || \ HAS_PKEY=true @@ -18423,6 +18434,7 @@ OSSL_VER_PLATFORM: $OSSL_VER_PLATFORM OPENSSL_NR_CIPHERS: $OPENSSL_NR_CIPHERS OPENSSL_CONF: $OPENSSL_CONF +HAS_CURVES: $HAS_CURVES OSSL_SUPPORTED_CURVES: $OSSL_SUPPORTED_CURVES HAS_IPv6: $HAS_IPv6 From a5a28d2457b6aa9ab2944057dec8adf2beded14a Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 30 Apr 2020 10:54:41 -0400 Subject: [PATCH 065/211] Improve LibreSSL 3.1.0 compatibility This commit addresses two compatibility issues with LibreSSL 3.1.0, which has added client support for TLS 1.3. The first issue is that LibreSSL has named the TLS 1.3 ciphers that it supports AEAD-AES256-GCM-SHA384, AEAD-CHACHA20-POLY1305-SHA256, and AEAD-AES128-GCM-SHA256, rather than using the OpenSSL names, which are TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, and TLS_AES_128_GCM_SHA256. (Draft versions of OpenSSL 1.1.1 names these ciphers TLS13-AES-256-GCM-SHA384, TLS13-CHACHA20-POLY1305-SHA256, TLS13-AES-128-GCM-SHA256.) There are several places where testssl.sh checks whether a cipher suite is a TLS 1.3 cipher by checking whether its OpenSSL name begins with "TLS_" (or "TLS13"). In order to work with LibreSSL 3.1.0, these checks also need to consider names that begin with "AEAD-" to be TLS 1.3 ciphers. Second, in sub_session_resumption() there is code that adds "-no_ssl2" to the "$OPENSSL s_client" command line if that option is supported. If "-no_ssl2" is not supported, then other protocol information is added to the command line. I believe this code was written with the assumption that any version of OpenSSL that supports "-no_ssl2" does not support TLS 1.3. However, LibreSSL 3.1.0 supports both. So, this commit changes the code to add the "-no_ssl2" option only if TLS 1.3 is not supported. --- testssl.sh | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/testssl.sh b/testssl.sh index b7f60ee..05515fa 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3575,7 +3575,7 @@ run_cipher_match(){ tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3593,7 +3593,7 @@ run_cipher_match(){ [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == "Kx=ECDH" ]] || [[ ${kx[i]} == "Kx=DH" ]] || [[ ${kx[i]} == "Kx=EDH" ]]; then @@ -3849,7 +3849,7 @@ run_allciphers() { tls13_ciphers_to_test="" for (( i=bundle*bundle_size; i < end_of_bundle; i++ )); do if ! "${ciphers_found2[i]}"; then - if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]]; then + if [[ "${ciph2[i]}" == TLS13* ]] || [[ "${ciph2[i]}" == TLS_* ]] || [[ "${ciph2[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph2[i]}" else ciphers_to_test+=":${ciph2[i]}" @@ -3867,7 +3867,7 @@ run_allciphers() { [[ $i -eq $end_of_bundle ]] && break i=${index[i]} ciphers_found[i]=true - if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]]; then + if [[ "$cipher" == TLS13* ]] || [[ "$cipher" == TLS_* ]] || [[ "$cipher" == AEAD-* ]]; then kx[i]="$(read_dhtype_from_file $TMPFILE)" fi if [[ ${kx[i]} == Kx=ECDH ]] || [[ ${kx[i]} == Kx=DH ]] || [[ ${kx[i]} == Kx=EDH ]]; then @@ -4055,9 +4055,9 @@ ciphers_by_strength() { fi while read hexc n ciph[nr_ciphers] sslvers kx[nr_ciphers] auth enc[nr_ciphers] mac export2[nr_ciphers]; do if [[ "$proto" == -tls1_3 ]]; then - [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || continue + [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]] || continue elif [[ "$proto" == -tls1_2 ]]; then - if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]]; then + if [[ "${ciph[nr_ciphers]}" == TLS13* ]] || [[ "${ciph[nr_ciphers]}" == TLS_* ]] || [[ "${ciph[nr_ciphers]}" == AEAD-* ]]; then continue fi elif [[ "${ciph[nr_ciphers]}" == *-SHA256 ]] || [[ "${ciph[nr_ciphers]}" == *-SHA384 ]] || \ @@ -6203,7 +6203,7 @@ sub_session_resumption() { fi fi "$CLIENT_AUTH" && return 6 - if "$HAS_NO_SSL2"; then + if ! "$HAS_TLS13" && "$HAS_NO_SSL2"; then addcmd+=" -no_ssl2" else protocol=${protocol/\./_} @@ -9586,7 +9586,7 @@ run_fs() { tls13_ciphers_to_test="" for (( i=0; i < nr_supported_ciphers; i++ )); do if ! "${ciphers_found[i]}" && "${ossl_supported[i]}"; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_ciphers_to_test+=":${ciph[i]}" else ciphers_to_test+=":${ciph[i]}" @@ -9603,7 +9603,7 @@ run_fs() { done [[ $i -eq $nr_supported_ciphers ]] && break ciphers_found[i]=true - if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]]; then + if [[ "$fs_cipher" == TLS13* ]] || [[ "$fs_cipher" == TLS_* ]] || [[ "$fs_cipher" == AEAD-* ]]; then fs_tls13_offered=true "$WIDE" && kx[i]="$(read_dhtype_from_file $TMPFILE)" fi @@ -9662,11 +9662,12 @@ run_fs() { fi fs_ciphers+="$fs_cipher " - if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then + if [[ "${ciph[i]}" == ECDHE-* ]] || [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || \ + [[ "${ciph[i]}" == AEAD-* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == TLS_ECDHE_* ]] ); then ecdhe_offered=true ecdhe_cipher_list_hex+=", ${hexcode[i]}" if [[ "${ciph[i]}" != "-" ]]; then - if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + if [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then tls13_cipher_list+=":$fs_cipher" else ecdhe_cipher_list+=":$fs_cipher" @@ -9676,7 +9677,7 @@ run_fs() { if [[ "${ciph[i]}" == "DHE-"* ]] || ( "$using_sockets" && [[ "${rfc_ciph[i]}" == "TLS_DHE_"* ]] ); then ffdhe_offered=true ffdhe_cipher_list_hex+=", ${hexcode[i]}" - elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]]; then + elif [[ "${ciph[i]}" == TLS13* ]] || [[ "${ciph[i]}" == TLS_* ]] || [[ "${ciph[i]}" == AEAD-* ]]; then ffdhe_cipher_list_hex+=", ${hexcode[i]}" fi fi From 4daf20585d74af79415843ea482775be2e77e014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Tue, 20 Aug 2019 18:48:06 +0200 Subject: [PATCH 066/211] STARTTLS: add support for xmpp-server XMPP client-to-server and server-to-server links historically use different XML namespaces. Some server implementations are strict about this and will not proceed with the connection attempt when the client namespace (`jabber:client`) is used on a server-to-server link. openssl s_client also supports `xmpp-server`. --- doc/testssl.1 | 2 +- testssl.sh | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 9c0f684..9d6decb 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -134,7 +134,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-basicauth \fR This can be set to provide HTTP basic auth credentials which are used during checks for security headers\. BASICAUTH is the ENV variable you can use instead\. . .SS "SPECIAL INVOCATIONS" -\fB\-t , \-\-starttls \fR does a default run against a STARTTLS enabled \fBprotocol\fR\. \fBprotocol\fR must be one of \fBftp\fR, \fBsmtp\fR, \fBpop3\fR, \fBimap\fR, \fBxmpp\fR, \fBtelnet\fR, \fBldap\fR, \fBirc\fR, \fBlmtp\fR, \fBnntp\fR, \fBpostgres\fR, \fBmysql\fR\. For the latter four you need e\.g\. the supplied OpenSSL or OpenSSL version 1\.1\.1\. Please note: MongoDB doesn\'t offer a STARTTLS connection, LDAP currently only works with \fB\-\-ssl\-native\fR\. \fBtelnet\fR and \fBirc\fR is WIP\. +\fB\-t , \-\-starttls \fR does a default run against a STARTTLS enabled \fBprotocol\fR\. \fBprotocol\fR must be one of \fBftp\fR, \fBsmtp\fR, \fBpop3\fR, \fBimap\fR, \fBxmpp\fR, \fBxmpp-server\fR, \fBtelnet\fR, \fBldap\fR, \fBirc\fR, \fBlmtp\fR, \fBnntp\fR, \fBpostgres\fR, \fBmysql\fR\. For the latter four you need e\.g\. the supplied OpenSSL or OpenSSL version 1\.1\.1\. Please note: MongoDB doesn\'t offer a STARTTLS connection, LDAP currently only works with \fB\-\-ssl\-native\fR\. \fBtelnet\fR and \fBirc\fR is WIP\. . .P \fB\-\-xmpphost \fR is an additional option for STARTTLS enabled XMPP: It expects the jabber domain as a parameter\. This is only needed if the domain is different from the URI supplied\. diff --git a/testssl.sh b/testssl.sh index d5d7ee7..19e0031 100755 --- a/testssl.sh +++ b/testssl.sh @@ -10296,7 +10296,10 @@ starttls_xmpp_dialog() { debugme echo "=== starting xmpp STARTTLS dialog ===" [[ -z $XMPP_HOST ]] && XMPP_HOST="$NODE" - starttls_io "" 'starttls(.*)features' 1 && + namespace="jabber:client" + [[ "$STARTTLS_PROTOCOL" == xmpp-server ]] && namespace="jabber:server" + + starttls_io "" 'starttls(.*)features' 1 && starttls_io "" '", where [options] is: -t, --starttls Does a default run against a STARTTLS enabled + protocol is --xmpphost For STARTTLS enabled XMPP it supplies the XML stream to-'' domain -- sometimes needed --mx Tests MX records from high to low priority (STARTTLS, port 25) --file/-iL Mass testing option: Reads one testssl.sh command line per line from . @@ -19646,9 +19649,9 @@ determine_service() { fi case "$protocol" in - ftp|smtp|lmtp|pop3|imap|xmpp|telnet|ldap|postgres|mysql|nntp) + ftp|smtp|lmtp|pop3|imap|xmpp|xmpp-server|telnet|ldap|postgres|mysql|nntp) STARTTLS="-starttls $protocol" - if [[ "$protocol" == xmpp ]]; then + if [[ "$protocol" == xmpp ]] || [[ "$protocol" == xmpp-server ]]; then # for XMPP, openssl has a problem using -connect $NODEIP:$PORT. thus we use -connect $NODE:$PORT instead! NODEIP="$NODE" if [[ -n "$XMPP_HOST" ]]; then @@ -20591,7 +20594,7 @@ parse_cmd_line() { STARTTLS_PROTOCOL="$(parse_opt_equal_sign "$1" "$2")" [[ $? -eq 0 ]] && shift case $STARTTLS_PROTOCOL in - ftp|smtp|lmtp|pop3|imap|xmpp|telnet|ldap|irc|nntp|postgres|mysql) ;; + ftp|smtp|lmtp|pop3|imap|xmpp|xmpp-server|telnet|ldap|irc|nntp|postgres|mysql) ;; ftps|smtps|lmtps|pop3s|imaps|xmpps|telnets|ldaps|ircs|nntps|mysqls) ;; *) tmln_magenta "\nunrecognized STARTTLS protocol \"$1\", see help" 1>&2 help 1 ;; From 53ee37b046cffa5ed74e28e8369c071f3e41ba3c Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 18:03:19 +0200 Subject: [PATCH 067/211] XMPP server --- CHANGELOG.md | 1 + CREDITS.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31f0325..36d0236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Several display/output fixes * Security fix: DNS input * Don't use external pwd anymore +* STARTTLS: XMPP server support * Rating (SSL Labs, not complete) ### Features implemented / improvements in 3.0 diff --git a/CREDITS.md b/CREDITS.md index 884d8dd..3c0cd24 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -143,6 +143,9 @@ Full contribution, see git log. * Dmitri S - inspiration & help for Darwin port +* Jonas Schäfer + - XMPP server patch + * Marcin Szychowski - Quick'n'dirty client certificate support From 0e6fb44bd34ece23f610d12e55ec748bb85e1565 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 18:31:35 +0200 Subject: [PATCH 068/211] add xmpp-server --- doc/testssl.1.html | 2 +- doc/testssl.1.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 662cfae..37fb4dc 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -189,7 +189,7 @@ The same can be achieved by setting the environment variable WARNINGSSPECIAL INVOCATIONS -

-t <protocol>, --starttls <protocol> does a default run against a STARTTLS enabled protocol. protocol must be one of ftp, smtp, pop3, imap, xmpp, telnet, ldap, irc, lmtp, nntp, postgres, mysql. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with --ssl-native. telnet and irc is WIP.

+

-t <protocol>, --starttls <protocol> does a default run against a STARTTLS enabled protocol. protocol must be one of ftp, smtp, pop3, imap, xmpp,xmpp-server, telnet, ldap, irc, lmtp, nntp, postgres, mysql. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with --ssl-native. telnet and irc is WIP.

--xmpphost <jabber_domain> is an additional option for STARTTLS enabled XMPP: It expects the jabber domain as a parameter. This is only needed if the domain is different from the URI supplied.

diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 4cce65b..63cfa88 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -113,7 +113,7 @@ The same can be achieved by setting the environment variable `WARNINGS`. ### SPECIAL INVOCATIONS -`-t , --starttls ` does a default run against a STARTTLS enabled `protocol`. `protocol` must be one of `ftp`, `smtp`, `pop3`, `imap`, `xmpp`, `telnet`, `ldap`, `irc`, `lmtp`, `nntp`, `postgres`, `mysql`. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with `--ssl-native`. `telnet` and `irc` is WIP. +`-t , --starttls ` does a default run against a STARTTLS enabled `protocol`. `protocol` must be one of `ftp`, `smtp`, `pop3`, `imap`, `xmpp`, `xmpp-server`, `telnet`, `ldap`, `irc`, `lmtp`, `nntp`, `postgres`, `mysql`. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with `--ssl-native`. `telnet` and `irc` is WIP. `--xmpphost ` is an additional option for STARTTLS enabled XMPP: It expects the jabber domain as a parameter. This is only needed if the domain is different from the URI supplied. From 1d7adebb4e882949d457c3a8b3b7f1f99a741185 Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 18:32:22 +0200 Subject: [PATCH 069/211] Add HAS_XMPP_SERVER ... see also #1575 --- testssl.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index d5888d7..6520366 100755 --- a/testssl.sh +++ b/testssl.sh @@ -317,6 +317,7 @@ HAS_NPN=false HAS_FALLBACK_SCSV=false HAS_PROXY=false HAS_XMPP=false +HAS_XMPP_SERVER=false HAS_POSTGRES=false HAS_MYSQL=false HAS_LMTP=false @@ -18214,6 +18215,7 @@ find_openssl_binary() { HAS_FALLBACK_SCSV=false HAS_PROXY=false HAS_XMPP=false + HAS_XMPP_SERVER=false HAS_POSTGRES=false HAS_MYSQL=false HAS_LMTP=false @@ -18298,9 +18300,12 @@ find_openssl_binary() { grep -q '\-proxy' $s_client_has && \ HAS_PROXY=true - grep -q '\-xmpp' $s_client_has && \ + grep -q 'xmpp' $s_client_starttls_has && \ HAS_XMPP=true + grep -q 'xmpp-server' $s_client_starttls_has && \ + HAS_XMPP_SERVER=true + grep -q 'postgres' $s_client_starttls_has && \ HAS_POSTGRES=true @@ -18623,6 +18628,7 @@ HAS_PKEY: $HAS_PKEY HAS_PKUTIL: $HAS_PKUTIL HAS_PROXY: $HAS_PROXY HAS_XMPP: $HAS_XMPP +HAS_XMPP_SERVER: $HAS_XMPP_SERVER HAS_POSTGRES: $HAS_POSTGRES HAS_MYSQL: $HAS_MYSQL HAS_LMTP: $HAS_LMTP @@ -19811,6 +19817,9 @@ determine_service() { fi fi fi + if [[ "$protocol" == xmpp-server ]] && ! "$HAS_XMPP_SERVER"; then + fatal "Your $OPENSSL does not support the \"-xmpphost\" option" $ERR_OSSLBIN + fi elif [[ "$protocol" == postgres ]]; then # Check if openssl version supports postgres. if ! "$HAS_POSTGRES"; then From 191c69fbdde8cd90aa33f5cf914e071e3e0d1d8c Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 18:39:36 +0200 Subject: [PATCH 070/211] Minor probe for STARTTLS xmpp-server ... don't know whether this gets through -- depends on the version openssl used (1.0.2 doesn't have that) --- t/25_baseline_starttls.t | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/t/25_baseline_starttls.t b/t/25_baseline_starttls.t index 0179e4a..18c9e27 100755 --- a/t/25_baseline_starttls.t +++ b/t/25_baseline_starttls.t @@ -99,6 +99,14 @@ $openssl_out = `./testssl.sh --ssl-native $check2run -t xmpp $uri 2>&1`; unlike($openssl_out, qr/$openssl_regex_bl/, ""); $tests++; +uri="jabber.ccc.de:5269" +printf "\n%s\n", "Quick STARTTLS XMPP S2S unit tests via sockets --> $uri ..."; +$openssl_out = `./testssl.sh --openssl=/usr/bin/openssl -p $check2run -t xmpp-server $uri 2>&1`; +# $openssl_json = json('tmp.json'); +unlike($openssl_out, qr/$openssl_regex_bl/, ""); +$tests++; + + $uri="ldap.uni-rostock.de:21"; From 9e61d6605e87309f4ff6dfb8f92a9c264af4c55f Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 19:17:58 +0200 Subject: [PATCH 071/211] Perl needs a semicolon ;-/ --- t/25_baseline_starttls.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/25_baseline_starttls.t b/t/25_baseline_starttls.t index 18c9e27..9537604 100755 --- a/t/25_baseline_starttls.t +++ b/t/25_baseline_starttls.t @@ -99,7 +99,7 @@ $openssl_out = `./testssl.sh --ssl-native $check2run -t xmpp $uri 2>&1`; unlike($openssl_out, qr/$openssl_regex_bl/, ""); $tests++; -uri="jabber.ccc.de:5269" +uri="jabber.ccc.de:5269"; printf "\n%s\n", "Quick STARTTLS XMPP S2S unit tests via sockets --> $uri ..."; $openssl_out = `./testssl.sh --openssl=/usr/bin/openssl -p $check2run -t xmpp-server $uri 2>&1`; # $openssl_json = json('tmp.json'); From 5da54b9ce8cdf1e49cde3de284820a458e911d4e Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 1 May 2020 21:42:41 +0200 Subject: [PATCH 072/211] fix var declaration --- t/25_baseline_starttls.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/25_baseline_starttls.t b/t/25_baseline_starttls.t index 9537604..0902b4c 100755 --- a/t/25_baseline_starttls.t +++ b/t/25_baseline_starttls.t @@ -99,7 +99,7 @@ $openssl_out = `./testssl.sh --ssl-native $check2run -t xmpp $uri 2>&1`; unlike($openssl_out, qr/$openssl_regex_bl/, ""); $tests++; -uri="jabber.ccc.de:5269"; +$uri="jabber.ccc.de:5269"; printf "\n%s\n", "Quick STARTTLS XMPP S2S unit tests via sockets --> $uri ..."; $openssl_out = `./testssl.sh --openssl=/usr/bin/openssl -p $check2run -t xmpp-server $uri 2>&1`; # $openssl_json = json('tmp.json'); From 485bcc1888e717b059166521a3d9d6fa874112f0 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 2 May 2020 18:34:10 +0200 Subject: [PATCH 073/211] Change Travis/CI environment to bionic ... as it comes with openssl 1.1.1 and we can check also XMPP S2S protocol --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 950e14b..d1d660b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: perl +dist: bionic perl: - "5.26" addons: From 05c90d4c3ad5f63776fe227ed2f0e3a330f64603 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 2 May 2020 18:37:02 +0200 Subject: [PATCH 074/211] remove add_tls_offered --- testssl.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 6520366..ba67b8e 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5182,7 +5182,6 @@ run_protocols() { 5) prln_svrty_high "CVE-2015-3197: $supported_no_ciph2"; fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310" add_proto_offered ssl2 yes - add_tls_offered ssl2 yes set_grade_cap "F" "SSLv2 is offered" ;; 7) prln_local_problem "$OPENSSL doesn't support \"s_client -ssl2\"" @@ -5211,7 +5210,6 @@ run_protocols() { latest_supported_string="SSLv3" fi add_proto_offered ssl3 yes - add_tls_offered ssl3 yes set_grade_cap "B" "SSLv3 is offered" ;; 1) prln_svrty_best "not offered (OK)" From 7981a238a5c7016833c40cbeb4ee029b7d3679e5 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 2 May 2020 19:40:45 +0200 Subject: [PATCH 075/211] Comment out S2S XMPP server test for now --- t/25_baseline_starttls.t | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/t/25_baseline_starttls.t b/t/25_baseline_starttls.t index 0902b4c..efb795e 100755 --- a/t/25_baseline_starttls.t +++ b/t/25_baseline_starttls.t @@ -99,13 +99,12 @@ $openssl_out = `./testssl.sh --ssl-native $check2run -t xmpp $uri 2>&1`; unlike($openssl_out, qr/$openssl_regex_bl/, ""); $tests++; -$uri="jabber.ccc.de:5269"; -printf "\n%s\n", "Quick STARTTLS XMPP S2S unit tests via sockets --> $uri ..."; -$openssl_out = `./testssl.sh --openssl=/usr/bin/openssl -p $check2run -t xmpp-server $uri 2>&1`; -# $openssl_json = json('tmp.json'); -unlike($openssl_out, qr/$openssl_regex_bl/, ""); -$tests++; - +# $uri="jabber.ccc.de:5269"; +# printf "\n%s\n", "Quick STARTTLS XMPP S2S unit tests via sockets --> $uri ..."; +# $openssl_out = `./testssl.sh --openssl=/usr/bin/openssl -p $check2run -t xmpp-server $uri 2>&1`; +# # $openssl_json = json('tmp.json'); +# unlike($openssl_out, qr/$openssl_regex_bl/, ""); +# $tests++; $uri="ldap.uni-rostock.de:21"; From 381fdfa985ea4a891c2de82c4b0fa1a4cf81019f Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 2 May 2020 19:49:01 +0200 Subject: [PATCH 076/211] Fix typo in docs: Strong grade Ciphers / AEAD --- doc/testssl.1 | 2 +- doc/testssl.1.html | 2 +- doc/testssl.1.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index fb6cb33..05cbe3d 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -212,7 +212,7 @@ Any single check switch supplied as an argument prevents testssl\.sh from doing \fBAverage grade Ciphers\fR: \'HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL\' . .IP "\(bu" 4 -\fBStrong grade Ciphers\fR (AEAD): \'AESGCM:CHACHA20:AESGCM:CamelliaGCM:AESCCM8:AESCCM\' +\fBStrong grade Ciphers\fR (AEAD): \'AESGCM:CHACHA20:CamelliaGCM:AESCCM\' . .IP "" 0 . diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 37fb4dc..2609c6f 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -240,7 +240,7 @@ ADDTL_CA_FILES is the environment variable for this.

  • LOW (64 Bit + DES ciphers, without EXPORT ciphers): 'LOW:DES:RC2:RC4:!ADH:!EXP:!NULL:!eNULL'
  • 3DES + IDEA Ciphers: '3DES:IDEA:!aNULL:!ADH'
  • Average grade Ciphers: 'HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL'
  • -
  • Strong grade Ciphers (AEAD): 'AESGCM:CHACHA20:AESGCM:CamelliaGCM:AESCCM8:AESCCM'
  • +
  • Strong grade Ciphers (AEAD): 'AESGCM:CHACHA20:CamelliaGCM:AESCCM8:AESCCM'
  • diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 63cfa88..536762e 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -165,7 +165,7 @@ Any single check switch supplied as an argument prevents testssl.sh from doing a * `LOW` (64 Bit + DES ciphers, without EXPORT ciphers): 'LOW:DES:RC2:RC4:!ADH:!EXP:!NULL:!eNULL' * `3DES + IDEA Ciphers`: '3DES:IDEA:!aNULL:!ADH' * `Average grade Ciphers`: 'HIGH:MEDIUM:AES:CAMELLIA:ARIA:!IDEA:!CHACHA20:!3DES:!RC2:!RC4:!AESCCM8:!AESCCM:!AESGCM:!ARIAGCM:!aNULL' -* `Strong grade Ciphers` (AEAD): 'AESGCM:CHACHA20:AESGCM:CamelliaGCM:AESCCM8:AESCCM' +* `Strong grade Ciphers` (AEAD): 'AESGCM:CHACHA20:CamelliaGCM:AESCCM8:AESCCM' `-f, --fs, --nsa, --forward-secrecy` Checks robust forward secrecy key exchange. "Robust" means that ciphers having intrinsic severe weaknesses like Null Authentication or Encryption, 3DES and RC4 won't be considered here. There shouldn't be the wrong impression that a secure key exchange has been taking place and everything is fine when in reality the encryption sucks. Also this section lists the available elliptical curves and Diffie Hellman groups, as well as FFDHE groups (TLS 1.2 and TLS 1.3). From ae8a056afe2a1129e02cf5dd0c9115d1d72c158a Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 4 May 2020 14:29:13 -0400 Subject: [PATCH 077/211] Check server's response There is code at the beginning of parse_tls_serverhello() that checks whether the server's response appears to consist of a sequence of messages of the form . However, at the moment the check is only performed if "$do_starttls" is false. This commit changes parse_tls_serverhello() so that the check is always performed. --- testssl.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index ba67b8e..ccbe7a6 100755 --- a/testssl.sh +++ b/testssl.sh @@ -12793,7 +12793,8 @@ parse_tls_serverhello() { [[ $DEBUG -ge 1 ]] && tmpfile_handle ${FUNCNAME[0]}.txt return 3 fi - elif [[ $tls_content_type != 14 ]] && [[ $tls_content_type != 15 ]] && \ + fi + if [[ $tls_content_type != 14 ]] && [[ $tls_content_type != 15 ]] && \ [[ $tls_content_type != 16 ]] && [[ $tls_content_type != 17 ]]; then debugme tmln_warning "Content type other than alert, handshake, change cipher spec, or application data detected." [[ $DEBUG -ge 1 ]] && tmpfile_handle ${FUNCNAME[0]}.txt From cdc1a088198e9210f4bdd6d74fbca01b012abfc1 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 4 May 2020 20:34:32 +0200 Subject: [PATCH 078/211] Address further potential license restriction .. see https://github.com/drwetter/testssl.sh/issues/1590#issuecomment-623526604 Added some formatting and verbal improvements in the intruductory comment section. --- testssl.sh | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/testssl.sh b/testssl.sh index ba67b8e..b86432a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -3,23 +3,25 @@ # vim:ts=5:sw=5:expandtab # we have a spaces softtab, that ensures readability with other editors too -# testssl.sh is a program for spotting weak SSL encryption, ciphers, version and some -# vulnerabilities or features +# testssl.sh is a program for spotting weak SSL/TLS encryption, ciphers, protocols and some +# vulnerabilities or features. It may or may be not distributed by your distribution. +# The upstream versions are available (please leave the links intact): # -# Devel version is available from https://github.com/drwetter/testssl.sh -# Stable version from https://testssl.sh -# Please file bugs at github! https://github.com/drwetter/testssl.sh/issues +# Development version https://github.com/drwetter/testssl.sh +# Stable version https://testssl.sh +# File bugs at github https://github.com/drwetter/testssl.sh/issues # # Project lead and initiator: Dirk Wetter, copyleft: 2007-today, contributions so far see CREDITS.md # Main contributions from David Cooper +# Project lead and initiator: Dirk Wetter, copyleft: 2007-today. +# Main contributions from David Cooper. Further contributors see CREDITS.md . # -# License: GPLv2, see https://opensource.org/licenses/gpl-2.0.php -# and accompanying license "LICENSE.txt". Redistribution + modification under this +# License: GPLv2, see https://opensource.org/licenses/gpl-2.0.php and +# ccompanying license "LICENSE.txt". Redistribution + modification under this # license permitted. -# If you enclose this script or parts of it in your software, it has to -# be accompanied by the same license (see link) and the place where to get -# the recent version of this program. Do not violate the license and if -# you do not agree to all of these terms, do not use it in the first place. +# If you enclose this program or parts of it in your software, it has to be +# accompanied by the same license (see link). Do not violate the license. +# If you do not agree to these terms, do not use it in the first place! # # OpenSSL, which is being used and maybe distributed via one of this projects' # web sites, is subject to their licensing: https://www.openssl.org/source/license.txt @@ -30,13 +32,13 @@ # # Please note: USAGE WITHOUT ANY WARRANTY, THE SOFTWARE IS PROVIDED "AS IS". # USE IT AT your OWN RISK! -# Seriously! The threat is you run this code on your computer and input could be / -# is being supplied via untrusted sources. +# Seriously! The threat is you run this code on your computer and untrusted input e.g. +# could be supplied from a server you are querying. # # HISTORY: # Back in 2006 it all started with a few openssl commands... # That's because openssl is a such a good swiss army knife (see e.g. -# wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist +# https://wiki.openssl.org/index.php/Command_Line_Utilities) that it was difficult to resist # wrapping some shell commands around it, which I used for my pen tests. This is how # everything started. # Now it has grown up, it has bash socket support for most features, which has been basically From d949b305c016607cb18df9cda0c3b2d54ba21b6b Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 4 May 2020 22:38:02 +0200 Subject: [PATCH 079/211] Fix STARTTLS pretests, improve XMPP handshakes There was a empty variable in determine_optimal_proto() which prevented to save STARTTLS_OPTIMAL_PROTO. This is fixed. The buffers and return codes for XMPP in starttls_io() were under not every circumstances correct. This fixes those cases and making that in general more robust (hopefully). (There's still code commented out which I'll leave it for now). When openssl did not support -starttls xmpp-server there was a copy and paste error saying that -xmpphost option was not supported. --- testssl.sh | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/testssl.sh b/testssl.sh index ba67b8e..4915eae 100755 --- a/testssl.sh +++ b/testssl.sh @@ -5038,8 +5038,9 @@ run_prototest_openssl() { # arg1: protocol # arg2: available (yes) or not (no) add_proto_offered() { + # the ":" is mandatory here (and @ other places), otherwise e.g. tls1 will match tls1_2 if [[ "$PROTOS_OFFERED" =~ $1: ]]; then - # the ":" is mandatory here (and @ other places), otherwise e.g. tls1 will match tls1_2 + # we got that protcol already : else PROTOS_OFFERED+="${1}:$2 " @@ -10231,12 +10232,13 @@ run_alpn() { return $ret } -# arg1: string to send -# arg2: possible success strings a egrep pattern, needed! -# arg3: wait in seconds +# arg1: send string +# arg2: success string: an egrep pattern +# arg3: number of loops we should read from the buffer (optional, otherwise STARTTLS_SLEEP) starttls_io() { - local waitsleep=$STARTTLS_SLEEP + local nr_waits=$STARTTLS_SLEEP local buffer="" + local -i i [[ -n "$3" ]] && waitsleep=$3 [[ -z "$2" ]] && echo "FIXME $((LINENO))" @@ -10244,27 +10246,31 @@ starttls_io() { # If there's a sending part it's IO. Postgres sends via socket and replies via # strings "S". So there's no I part of IO ;-) if [[ -n "$1" ]]; then - debugme echo -en "C: \"$1\"" + debugme echo -en "C: $1" echo -en "$1" >&5 fi + if [[ "$2" == JUSTSEND ]]; then + debugme echo -e "\n (only sent)\n" + dd of=/dev/null bs=512 count=1 <&5 2>/dev/null & + return 0 + fi # This seems a bit dangerous but works. No blockings yet. "if=nonblock" doesn't work on BSDs buffer="$(dd bs=512 count=1 <&5 2>/dev/null)" - [[ "$DEBUG" -ge 2 ]] && echo -en "\nS: " && echo $buffer - for ((i=1; i < $waitsleep; i++ )); do + for ((i=1; i < $nr_waits; i++ )); do + [[ "$DEBUG" -ge 2 ]] && echo -en "\nS: " && echo $buffer if [[ "$buffer" =~ $2 ]]; then debugme echo " ---> reply matched \"$2\"" # the fd sometimes still seem to contain chars which confuses the following TLS handshake, trying to empty: - dd of=/dev/null bs=512 count=1 <&5 2>/dev/null + # dd of=/dev/null bs=512 count=1 <&5 2>/dev/null return 0 else # no match yet, more reading from fd helps. buffer+=$(dd bs=512 count=1 <&5 2>/dev/null) fi - sleep 0.5 done - return 0 + return 1 } @@ -10413,8 +10419,9 @@ starttls_xmpp_dialog() { namespace="jabber:client" [[ "$STARTTLS_PROTOCOL" == xmpp-server ]] && namespace="jabber:server" - starttls_io "" 'starttls(.*)features' 1 && + starttls_io "" 'starttls(.*)features' 1 && starttls_io "" '" 'JUSTSEND' 2 local ret=$? debugme echo "=== finished xmpp STARTTLS dialog with ${ret} ===" return $ret @@ -19640,7 +19647,7 @@ determine_optimal_proto() { $OPENSSL s_client $(s_client_options "$STARTTLS_OPTIMAL_PROTO $BUGS -connect "$NODEIP:$PORT" $PROXY -msg $STARTTLS $SNI") $TMPFILE 2>>$ERRFILE if sclient_auth $? $TMPFILE; then all_failed=false - add_proto_offered "${proto/-/}" yes + add_proto_offered "${STARTTLS_OPTIMAL_PROTO/-/}" yes break fi done @@ -19747,7 +19754,7 @@ determine_optimal_proto() { } -# arg1 (optional): ftp smtp, lmtp, pop3, imap, xmpp, telnet, ldap, postgres, mysql, irc, nntp (maybe with trailing s) +# arg1 (optional): ftp smtp, lmtp, pop3, imap, xmpp, xmpp-server, telnet, ldap, postgres, mysql, irc, nntp (maybe with trailing s) # determine_service() { local ua @@ -19816,7 +19823,7 @@ determine_service() { fi fi if [[ "$protocol" == xmpp-server ]] && ! "$HAS_XMPP_SERVER"; then - fatal "Your $OPENSSL does not support the \"-xmpphost\" option" $ERR_OSSLBIN + fatal "Your $OPENSSL does not support the \"-starttls xmpp-server\" option" $ERR_OSSLBIN fi elif [[ "$protocol" == postgres ]]; then # Check if openssl version supports postgres. @@ -19850,7 +19857,7 @@ determine_service() { outln ;; *) outln - fatal "momentarily only ftp, smtp, lmtp, pop3, imap, xmpp, telnet, ldap, nntp, postgres and mysql allowed" $ERR_CMDLINE + fatal "momentarily only ftp, smtp, lmtp, pop3, imap, xmpp, xmpp-server, telnet, ldap, nntp, postgres and mysql allowed" $ERR_CMDLINE ;; esac fi From 918d358e952e80bd2b084c5987ad1731425c6c9f Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 5 May 2020 13:37:58 +0200 Subject: [PATCH 080/211] Add X-TYPO3-Parsetime --- testssl.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 5778047..a30973b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -2865,6 +2865,7 @@ emphasize_stuff_in_headers(){ -e "s/CentOS/${yellow}CentOS${off}/g" \ -e "s/Via/${yellow}Via${off}/g" \ -e "s/X-Forwarded/${yellow}X-Forwarded${off}/g" \ + -e "s/X-TYPO3-Parsetime/${yellow}X-TYPO3-Parsetime${off}/g" \ -e "s/Liferay-Portal/${yellow}Liferay-Portal${off}/g" \ -e "s/X-Cache-Lookup/${yellow}X-Cache-Lookup${off}/g" \ -e "s/X-Cache/${yellow}X-Cache${off}/g" \ @@ -2912,6 +2913,7 @@ emphasize_stuff_in_headers(){ -e "s/CentOS/${html_yellow}CentOS${html_off}/g" \ -e "s/Via/${html_yellow}Via${html_off}/g" \ -e "s/X-Forwarded/${html_yellow}X-Forwarded${html_off}/g" \ + -e "s/X-TYPO3-Parsetime/${yellow}X-TYPO3-Parsetime${html_off}/g" \ -e "s/Liferay-Portal/${html_yellow}Liferay-Portal${html_off}/g" \ -e "s/X-Cache-Lookup/${html_yellow}X-Cache-Lookup${html_off}/g" \ -e "s/X-Cache/${html_yellow}X-Cache${html_off}/g" \ @@ -2991,7 +2993,7 @@ run_appl_banner() { run_http_header "$1" || return 1 fi pr_bold " Application banner " - grep -Eai '^X-Powered-By|^X-AspNet-Version|^X-Version|^Liferay-Portal|^X-OWA-Version^|^MicrosoftSharePointTeamServices' $HEADERFILE >$TMPFILE + grep -Eai '^X-Powered-By|^X-AspNet-Version|^X-Version|^Liferay-Portal|^X-TYPO3-Parsetime|^X-OWA-Version^|^MicrosoftSharePointTeamServices' $HEADERFILE >$TMPFILE if [[ $? -ne 0 ]]; then outln "--" fileout "$jsonID" "INFO" "No application banner found" From 908975380d0e595afd6c63babc15563e78856ab4 Mon Sep 17 00:00:00 2001 From: Dirk Date: Wed, 6 May 2020 09:17:42 +0200 Subject: [PATCH 081/211] Amendment to "Relax the possible GPL license contradiction" fix it also in the man pages. See #1590 / #1593 --- doc/testssl.1 | 11 ++++++++--- doc/testssl.1.html | 16 ++++++++++++---- doc/testssl.1.md | 13 +++++++++---- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 05cbe3d..6aea659 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -79,7 +79,7 @@ Options are either short or long options\. Any long or short option requiring a \fB\-\-help\fR (or no arg) display command line help . .P -\fB\-b, \-\-banner\fR displays testssl\.sh banner, including license, usage conditions, version of testssl\.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture +\fB\-b, \-\-banner\fR displays testssl\.sh banner, including license, usage conditions, version of testssl\.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture\. . .P \fB\-v, \-\-version\fR same as before @@ -902,10 +902,15 @@ TLSWG Draft: The Transport Layer Security (TLS) Protocol Version 1\.3 Developed by Dirk Wetter, David Cooper and many others, see CREDITS\.md \. . .SH "COPYRIGHT" -Copyright © 2012 Dirk Wetter\. License GPLv2: Free Software Foundation, Inc\. This is free software: you are free to change and redistribute it under the terms of the license\. Usage WITHOUT ANY WARRANTY\. USE at your OWN RISK! +Copyright © 2012 Dirk Wetter\. License GPLv2: Free Software Foundation, Inc\. This is free software: you are free to change and redistribute it under the terms of the license, see LICENSE\. +.P +Attribution is important for the future of this project - also in the internet\. Thus if you\'re offering a scanner based on testssl\.sh as a public +and/or paid service in the internet you are strongly encouraged to mention to your audience that you\'re using this program and where to get this program +from\. That helps us to get bugfixes, other feedback and more contributions\. . .P -If you\'re offering testssl\.sh as a public and / or paid service in the internet you need to mention to your audience that you\'re using this program and where to get this program from\. +Usage WITHOUT ANY WARRANTY\. USE at your OWN RISK! +. . .SH "LIMITATION" All native Windows platforms emulating Linux are known to be slow\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 2609c6f..8422223 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -147,7 +147,7 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

    --help (or no arg) display command line help

    -

    -b, --banner displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture

    +

    -b, --banner displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture.

    -v, --version same as before

    @@ -638,10 +638,18 @@ This is to prevent giving out a misleading or wrong grade.

    Copyright © 2012 Dirk Wetter. License GPLv2: Free Software Foundation, Inc. - This is free software: you are free to change and redistribute it under the terms of the license. Usage WITHOUT ANY WARRANTY. USE at your OWN RISK!

    + This is free software: you are free to change and redistribute it under the terms of the license, see LICENSE.

    + +

    +Attribution is important for the future of this project - also in the +internet. Thus if you're offering a scanner based on testssl.sh as a public +and/or paid service in the internet you are strongly encouraged to mention to +your audience that you're using this program and where to get this program +from. That helps us to get bugfixes, other feedback and more contributions. +

    + +

    Usage WITHOUT ANY WARRANTY. USE at your OWN RISK!

    -

    If you're offering testssl.sh as a public and / or paid service in the internet you need to mention to your audience that you're using this program and -where to get this program from.

    LIMITATION

    diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 536762e..a63fb26 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -70,7 +70,7 @@ Options are either short or long options. Any long or short option requiring a v `--help` (or no arg) display command line help -`-b, --banner` displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture +`-b, --banner` displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture. `-v, --version` same as before @@ -536,10 +536,15 @@ Developed by Dirk Wetter, David Cooper and many others, see CREDITS.md . ## COPYRIGHT Copyright © 2012 Dirk Wetter. License GPLv2: Free Software Foundation, Inc. - This is free software: you are free to change and redistribute it under the terms of the license. Usage WITHOUT ANY WARRANTY. USE at your OWN RISK! + This is free software: you are free to change and redistribute it under the terms of the license, see LICENSE. -If you're offering testssl.sh as a public and / or paid service in the internet you need to mention to your audience that you're using this program and -where to get this program from. +Attribution is important for the future of this project - also in the +internet. Thus if you're offering a scanner based on testssl.sh as a public +and/or paid service in the internet you are strongly encouraged to mention to +your audience that you're using this program and where to get this program +from. That helps us to get bugfixes, other feedback and more contributions. + +Usage WITHOUT ANY WARRANTY. USE at your OWN RISK! ## LIMITATION From 84a82dbddc1bfac50d86e82afd476924b35f888a Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 6 May 2020 08:31:09 -0400 Subject: [PATCH 082/211] tls_sockets() return value There is one place in parse_tls_serverhello() that returns 8 if the server's response is not well-formed TLS. However, there is no code in testssl.sh that is prepared to handle this return value. Every function except run_protocols() only distinguishes between 0, 2, and everything else. run_protocols(), however, gets confused if tls_sockets() returns a value that it is not expecting. So, this commit changes parse_tls_serverhello() to return 1 whenever the server's response can not be parsed. --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index a30973b..b83eb41 100755 --- a/testssl.sh +++ b/testssl.sh @@ -12809,7 +12809,7 @@ parse_tls_serverhello() { [[ $tls_content_type != 16 ]] && [[ $tls_content_type != 17 ]]; then debugme tmln_warning "Content type other than alert, handshake, change cipher spec, or application data detected." [[ $DEBUG -ge 1 ]] && tmpfile_handle ${FUNCNAME[0]}.txt - return 8 + return 1 elif [[ "${tls_protocol:0:2}" != 03 ]]; then debugme tmln_warning "Protocol record_version.major is not 03." [[ $DEBUG -ge 1 ]] && tmpfile_handle ${FUNCNAME[0]}.txt From 329ba95d5bae296495eb15d0e4c142d899b5d9f8 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 6 May 2020 08:43:13 -0400 Subject: [PATCH 083/211] Fix #1433 This commit fixes #1433 by adding "@SECLEVEL=0" to the "$OPENSSL s_client" and "$OPENSSL ciphers" command lines if that option is supported. Adding this option configures OpenSSL to support some weak ciphers that it would not use in the default configuration. --- testssl.sh | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/testssl.sh b/testssl.sh index a30973b..8055fbe 100755 --- a/testssl.sh +++ b/testssl.sh @@ -312,6 +312,7 @@ HAS_PKEY=false HAS_NO_SSL2=false HAS_NOSERVERNAME=false HAS_CIPHERSUITES=false +HAS_SECLEVEL=false HAS_COMP=false HAS_NO_COMP=false HAS_ALPN=false @@ -870,15 +871,17 @@ is_ipv6addr() { #arg2: TLS 1.3 ciphers #arg3: options (e.g., -V) actually_supported_osslciphers() { + local ciphers="$1" local tls13_ciphers="$TLS13_OSSL_CIPHERS" [[ "$2" != ALL ]] && tls13_ciphers="$2" + "$HAS_SECLEVEL" && [[ -n "$ciphers" ]] && ciphers="@SECLEVEL=0:$1" if "$HAS_CIPHERSUITES"; then - $OPENSSL ciphers $3 $OSSL_CIPHERS_S -ciphersuites "$tls13_ciphers" "$1" 2>/dev/null || echo "" + $OPENSSL ciphers $3 $OSSL_CIPHERS_S -ciphersuites "$tls13_ciphers" "$ciphers" 2>/dev/null || echo "" elif [[ -n "$tls13_ciphers" ]]; then - $OPENSSL ciphers $3 $OSSL_CIPHERS_S "$tls13_ciphers:$1" 2>/dev/null || echo "" + $OPENSSL ciphers $3 $OSSL_CIPHERS_S "$tls13_ciphers:$ciphers" 2>/dev/null || echo "" else - $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$1" 2>/dev/null || echo "" + $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$ciphers" 2>/dev/null || echo "" fi } @@ -2121,6 +2124,13 @@ s_client_options() { options+=" -no_tls1_3" fi + if "$HAS_SECLEVEL"; then + if [[ "$ciphers" == notpresent ]]; then + [[ ! " $options " =~ \ -tls1_3\ ]] && ciphers="@SECLEVEL=0:ALL:COMPLEMENTOFALL" + else + ciphers="@SECLEVEL=0:$ciphers" + fi + fi if [[ "$ciphers" != notpresent ]] || [[ "$tls13_ciphers" != notpresent ]]; then if ! "$HAS_CIPHERSUITES"; then [[ "$ciphers" == notpresent ]] && ciphers="" @@ -5717,15 +5727,17 @@ run_protocols() { listciphers() { local -i ret local debugname="" + local ciphers="$1" local tls13_ciphers="$TLS13_OSSL_CIPHERS" [[ "$2" != ALL ]] && tls13_ciphers="$2" + "$HAS_SECLEVEL" && [[ -n "$ciphers" ]] && ciphers="@SECLEVEL=0:$1" if "$HAS_CIPHERSUITES"; then - $OPENSSL ciphers $OSSL_CIPHERS_S $3 -ciphersuites "$tls13_ciphers" "$1" &>$TMPFILE + $OPENSSL ciphers $OSSL_CIPHERS_S $3 -ciphersuites "$tls13_ciphers" "$ciphers" &>$TMPFILE elif [[ -n "$tls13_ciphers" ]]; then - $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$tls13_ciphers:$1" &>$TMPFILE + $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$tls13_ciphers:$ciphers" &>$TMPFILE else - $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$1" &>$TMPFILE + $OPENSSL ciphers $OSSL_CIPHERS_S $3 "$ciphers" &>$TMPFILE fi ret=$? debugme cat $TMPFILE @@ -16080,7 +16092,7 @@ run_ssl_poodle() { nr_cbc_ciphers=$(count_ciphers $cbc_ciphers) nr_supported_ciphers=$(count_ciphers $(actually_supported_osslciphers $cbc_ciphers)) # SNI not needed as SSLv3 has none: - $OPENSSL s_client -ssl3 $STARTTLS $BUGS -cipher $cbc_ciphers -connect $NODEIP:$PORT $PROXY >$TMPFILE 2>$ERRFILE $TMPFILE 2>$ERRFILE &1 | grep -aiq "unknown option" || \ HAS_CIPHERSUITES=true + $OPENSSL ciphers @SECLEVEL=0:ALL > /dev/null 2> /dev/null + [[ $? -eq 0 ]] && HAS_SECLEVEL=true + $OPENSSL s_client -comp -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_COMP=true @@ -18634,6 +18650,7 @@ HAS_FALLBACK_SCSV: $HAS_FALLBACK_SCSV HAS_COMP: $HAS_COMP HAS_NO_COMP: $HAS_NO_COMP HAS_CIPHERSUITES: $HAS_CIPHERSUITES +HAS_SECLEVEL: $HAS_SECLEVEL HAS_PKEY: $HAS_PKEY HAS_PKUTIL: $HAS_PKUTIL HAS_PROXY: $HAS_PROXY From 9dba2a8c9cf3bf29c609c051d9c7223d81ea84d3 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 6 May 2020 12:16:15 -0400 Subject: [PATCH 084/211] Fix #1576 This commit adds additional information to the "Server key size" line for a certificate if the subject public key is RSA, ECDSA, or DH. For RSA it show the public exponent. For ECDSA, it shows the curve. For DH, it shows the group used, if it is a common prime. --- testssl.sh | 89 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/testssl.sh b/testssl.sh index dfb976f..9daae5a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8290,7 +8290,10 @@ certificate_info() { local sni_used="${10}" local ct="${11}" local certificate_list_ordering_problem="${12}" - local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_keyusage cert_ext_keyusage short_keyAlgo + local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info + local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt" + local -i lineno_matched=0 + local cert_keyusage cert_ext_keyusage short_keyAlgo local outok=true local expire days2expire secs2warn ocsp_uri crl local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn @@ -8308,6 +8311,7 @@ certificate_info() { local -i ret=0 local json_postfix="" # string to place at the end of JSON IDs when there is more than one certificate local jsonID="" # string to place at beginning of JSON IDs + local json_rating json_msg local indent="" local days2warn2=$DAYS2WARN2 local days2warn1=$DAYS2WARN1 @@ -8476,51 +8480,51 @@ certificate_info() { if [[ $cert_key_algo =~ ecdsa ]] || [[ $cert_key_algo =~ ecPublicKey ]]; then if [[ "$cert_keysize" -le 110 ]]; then # a guess pr_svrty_critical "$cert_keysize" - fileout "${jsonID}${json_postfix}" "CRITICAL" "$short_keyAlgo $cert_keysize bits" + json_rating="CRITICAL"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 123 ]]; then # a guess pr_svrty_high "$cert_keysize" - fileout "${jsonID}${json_postfix}" "HIGH" "$short_keyAlgo $cert_keysize bits" + json_rating="HIGH"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 163 ]]; then pr_svrty_medium "$cert_keysize" - fileout "${jsonID}${json_postfix}" "MEDIUM" "$short_keyAlgo $cert_keysize bits" + json_rating="MEDIUM"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 224 ]]; then out "$cert_keysize" - fileout "${jsonID}${json_postfix}" "INFO" "$short_keyAlgo $cert_keysize bits" + json_rating="INFO"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 533 ]]; then pr_svrty_good "$cert_keysize" - fileout "${jsonID}${json_postfix}" "OK" "$short_keyAlgo $cert_keysize bits" + json_rating="OK"; json_msg="$short_keyAlgo $cert_keysize bits" else out "keysize: $cert_keysize (not expected, FIXME)" - fileout "${jsonID}${json_postfix}" "DEBUG" " $cert_keysize bits (not expected)" + json_rating="DEBUG"; json_msg=" $cert_keysize bits (not expected)" ((ret++)) fi - outln " bits" + out " bits" set_key_str_score "$short_keyAlgo" "$cert_keysize" # TODO: should be $dh_param_size elif [[ $cert_key_algo =~ RSA ]] || [[ $cert_key_algo =~ rsa ]] || [[ $cert_key_algo =~ dsa ]] || \ [[ $cert_key_algo =~ dhKeyAgreement ]] || [[ $cert_key_algo == X9.42\ DH ]]; then if [[ "$cert_keysize" -le 512 ]]; then pr_svrty_critical "$cert_keysize" - outln " bits" - fileout "${jsonID}${json_postfix}" "CRITICAL" "$short_keyAlgo $cert_keysize bits" + out " bits" + json_rating="CRITICAL"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 768 ]]; then pr_svrty_high "$cert_keysize" - outln " bits" - fileout "${jsonID}${json_postfix}" "HIGH" "$short_keyAlgo $cert_keysize bits" + out " bits" + json_rating="HIGH"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 1024 ]]; then pr_svrty_medium "$cert_keysize" - outln " bits" - fileout "${jsonID}${json_postfix}" "MEDIUM" "$short_keyAlgo $cert_keysize bits" + out " bits" + json_rating="MEDIUM"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 2048 ]]; then - outln "$cert_keysize bits" - fileout "${jsonID}${json_postfix}" "INFO" "$short_keyAlgo $cert_keysize bits" + out "$cert_keysize bits" + json_rating="INFO"; json_msg="$short_keyAlgo $cert_keysize bits" elif [[ "$cert_keysize" -le 4096 ]]; then pr_svrty_good "$cert_keysize" - fileout "${jsonID}${json_postfix}" "OK" "$short_keyAlgo $cert_keysize bits" - outln " bits" + json_rating="OK"; json_msg="$short_keyAlgo $cert_keysize bits" + out " bits" else - pr_warning "weird key size: $cert_keysize bits"; outln " (could cause compatibility problems)" - fileout "${jsonID}${json_postfix}" "WARN" "$cert_keysize bits (Odd)" + pr_warning "weird key size: $cert_keysize bits"; out " (could cause compatibility problems)" + json_rating="WARN"; json_msg="$cert_keysize bits (Odd)" ((ret++)) fi @@ -8528,12 +8532,53 @@ certificate_info() { else out "$cert_key_algo + $cert_keysize bits (" pr_warning "FIXME: can't tell whether this is good or not" - outln ")" - fileout "${jsonID}${json_postfix}" "WARN" "Server keys $cert_keysize bits, unknown public key algorithm $cert_key_algo" + out ")" + json_rating="WARN"; json_msg="Server keys $cert_keysize bits, unknown public key algorithm $cert_key_algo" ((ret++)) fi fi + case "$short_keyAlgo" in + "RSA") cert_spki_info="${cert_txt##*Subject Public Key Info:}" + cert_spki_info="${cert_spki_info##*Public Key Algorithm:}" + cert_spki_info="${cert_spki_info#*Exponent:}" + cert_spki_info="$(strip_leading_space "$cert_spki_info")" + cert_spki_info="${cert_spki_info%%[[:space:]]*}" + if [[ -n "$cert_spki_info" ]]; then + out " (exponent is $cert_spki_info)" + json_msg+=" (exponent is $cert_spki_info)" + fi + ;; + "EC") cert_spki_info="${cert_txt##*Subject Public Key Info:}" + cert_spki_info="${cert_spki_info##*Public Key Algorithm:}" + cert_spki_info="${cert_spki_info##*ASN1 OID: }" + [[ "$cert_spki_info" =~ NIST\ CURVE:\ ]] && cert_spki_info="${cert_spki_info##*NIST CURVE: }" + cert_spki_info="${cert_spki_info%%[[:space:]]*}" + cert_spki_info="$(strip_lf "$(strip_spaces "$cert_spki_info")")" + if [[ -n "$cert_spki_info" ]]; then + out " (curve $cert_spki_info)" + json_msg+=" (curve $cert_spki_info)" + fi + ;; + "DH") if [[ -s "$common_primes_file" ]]; then + cert_spki_info="${cert_txt##*Subject Public Key Info:}" + cert_spki_info="${cert_spki_info##*Public Key Algorithm:}" + cert_spki_info="$(awk '/prime:|prime P:/,/generator:|generator G:/' <<< "$cert_spki_info" | grep -Ev "prime|generator")" + cert_spki_info="$(strip_spaces "$(colon_to_spaces "$(newline_to_spaces "$cert_spki_info")")")" + [[ "${cert_spki_info:0:2}" == 00 ]] && cert_spki_info="${cert_spki_info:2}" + cert_spki_info="$(toupper "$cert_spki_info")" + lineno_matched=$(grep -n "$cert_spki_info" "$common_primes_file" 2>/dev/null | awk -F':' '{ print $1 }') + if [[ "$lineno_matched" -ne 0 ]]; then + cert_spki_info="$(awk "NR == $lineno_matched-1" "$common_primes_file" | awk -F'"' '{ print $2 }')" + out " ($cert_spki_info)" + json_msg+=" ($cert_spki_info)" + fi + fi + ;; + esac + outln + fileout "${jsonID}${json_postfix}" "$json_rating" "$json_msg" + out "$indent"; pr_bold " Server key usage "; outok=true jsonID="cert_keyUsage" From a8c9133fc6632f7a758c7b950d6fc0443384dbe2 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 6 May 2020 14:25:59 -0400 Subject: [PATCH 085/211] Check for RSA exponent of 1 As suggested in #1576, set the grade cap to F if the RSA key has an exponent of 1. --- testssl.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/testssl.sh b/testssl.sh index 9daae5a..099dcfd 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8547,6 +8547,7 @@ certificate_info() { if [[ -n "$cert_spki_info" ]]; then out " (exponent is $cert_spki_info)" json_msg+=" (exponent is $cert_spki_info)" + [[ $cert_spki_info -eq 1 ]] && set_grade_cap "F" "RSA certificate uses exponent of 1" fi ;; "EC") cert_spki_info="${cert_txt##*Subject Public Key Info:}" From 46bf8c90acd59eaa75c1f7c9d905c00ae3968ecd Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 6 May 2020 14:30:44 -0400 Subject: [PATCH 086/211] Improve key exchange grading This commit makes a couple of improvements to set_key_str_score(). It rates (finite-field) DH keys the same as RSA and DSA keys. Second, in the case of a server that has more than one certificate, the current code sets $KEY_EXCH_SCORE based on the length of the public key in the last certificate that is parsed. This commit changes set_key_str_score() so that $KEY_EXCH_SCORE is set based on the weakest public key. Note that there is still the issue that the key exchange score does not take into account any ephemeral keys used. However, that needs to be addressed by callling set_key_str_score() from run_fs() and run_logjam(), as certificate_info() cannot provide information about ephemeral keys. --- testssl.sh | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/testssl.sh b/testssl.sh index dfb976f..954a6c3 100755 --- a/testssl.sh +++ b/testssl.sh @@ -382,7 +382,7 @@ HEX_CIPHER="" # " GRADE_CAP="" # Keeps track of the current grading cap GRADE_CAP_REASONS=() # Keeps track of all the reasons why grades are capped GRADE_WARNINGS=() # Keeps track of all the grade warnings -KEY_EXCH_SCORE=0 # Keeps track of the score for category 2 "Key Exchange Strength" +KEY_EXCH_SCORE=100 # Keeps track of the score for category 2 "Key Exchange Strength" CIPH_STR_BEST=0 # Keeps track of the best bit size for category 3 "Cipher Strength" CIPH_STR_WORST=100000 # Keeps track of the worst bit size for category 3 "Cipher Strength" # Intentionally set very high, so it can be set to 0, if necessary @@ -1039,41 +1039,31 @@ set_key_str_score() { # TODO: We need to get the size of DH params (follows the same table as the "else" clause) # For now, verifying the key size will do... - if [[ $type == EC || $type == DH ]]; then - if [[ $size -lt 110 ]]; then + if [[ $type == EC ]]; then + if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then let KEY_EXCH_SCORE=20 set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 123 ]]; then + elif [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 163 ]]; then + elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak key" - elif [[ $size -lt 225 ]]; then + elif [[ $size -lt 225 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then let KEY_EXCH_SCORE=90 - elif [[ $size -ge 225 ]]; then - let KEY_EXCH_SCORE=100 - else - let KEY_EXCH_SCORE=0 - set_grade_cap "F" "Using an insecure key" fi else - if [[ $size -lt 512 ]]; then + if [[ $size -lt 512 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then let KEY_EXCH_SCORE=20 set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 1024 ]]; then + elif [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 2048 ]]; then + elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak key" - elif [[ $size -lt 4096 ]]; then + elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then let KEY_EXCH_SCORE=90 - elif [[ $size -ge 4096 ]]; then - let KEY_EXCH_SCORE=100 - else - let KEY_EXCH_SCORE=0 - set_grade_cap "F" "Using an insecure key" fi fi return 0 From 7b3adf8195f2490bfa7472f3017cc2a36ce50847 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 7 May 2020 10:50:41 +0200 Subject: [PATCH 087/211] Labelling, look @ 3.0.2 * label 3.1dev it as a ~rolling release * outlook to 3.0.2 (~tomorrow) --- Readme.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Readme.md b/Readme.md index 071828e..6267975 100644 --- a/Readme.md +++ b/Readme.md @@ -17,11 +17,9 @@ cryptographic flaws. * Machine readable output (CSV, two JSON formats) * No need to install or to configure something. No gems, CPAN, pip or the like. * Works out of the box: Linux, OSX/Darwin, FreeBSD, NetBSD, MSYS2/Cygwin, WSL (bash on Windows). Only OpenBSD needs bash. -* A Dockerfile is provided, there's also an offical container @ dockerhub. -* Flexibility: You can test any SSL/TLS enabled and STARTTLS service, not - only web servers at port 443. -* Toolbox: Several command line options help you to run *your* test and - configure *your* output. +* A Dockerfile is provided, there's also an offical container build @ dockerhub. +* Flexibility: You can test any SSL/TLS enabled and STARTTLS service, not only web servers at port 443. +* Toolbox: Several command line options help you to run *your* test and configure *your* output. * Reliability: features are tested thoroughly. * Privacy: It's only you who sees the result, not a third party. * Freedom: It's 100% open source. You can look at the code, see what's going on. @@ -51,18 +49,20 @@ Update notification here or @ [twitter](https://twitter.com/drwetter). ### Installation -You can download testssl.sh by cloning this git repository: +You can download testssl.sh version 3.1dev by cloning this git repository: git clone --depth 1 https://github.com/drwetter/testssl.sh.git -Or help yourself downloading the 3.0 ZIP archive [https://github.com/drwetter/testssl.sh/archive/3.0.1.zip](https://github.com/drwetter/testssl.sh/archive/3.0.1.zip). Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. +Think of 3.1dev like a rolling release, see below. For the stable version help yourself by downloading the [ZIP](https://github.com/drwetter/testssl.sh/archive/3.0.2.zip) or [tar.gz](https://github.com/drwetter/testssl.sh/archive/3.0.2.zip) archive. Just ``cd`` to the directory created (=INSTALLDIR) and run it off there. #### Docker Testssl.sh has minimal requirements. As stated you don't have to install or build anything. You can just run it from the pulled/cloned directory. Still if you don't want to pull the github repo to your directory of choice you can pull a container from dockerhub and run it: + ``` -docker run --rm -ti drwetter/testssl.sh +docker run --rm -ti drwetter/testssl.sh:3.1dev ``` + Or if you have cloned this repo you also can just ``cd`` to the INSTALLDIR and run ``` docker build . @@ -77,10 +77,9 @@ Successfully built 889fa2f99933 ### Status -We're currently in the developement pase of 3.1dev. That means **occasionally** things can break. But we plan to keep it usable, think more of a rolling development. For missing critical purposes or when you don't like changes you should wait a bit until we're in the release phase. As soon as we reach that or the "dev" disappears in the branch you should rather use this version. - -Support for 2.9.5 has been dropped. Supported is 3.0 only. +We're currently in the development phase, version 3.1dev. 3.1dev will eventually become 3.2. Bigger features are developed in a seperate branch before merged into 3.1dev to avoid hiccups or inconsitencies. Albeit we try to keep 3.1dev as solid as possible things will certainly change in 3.1dev. So if you need stability the 3.0 branch is better for you. Think of the 3.1dev branch like a rolling release. +Support for 2.9.5 has been dropped. Supported is 3.0.x only. ### Documentation From 6fa82ea2f70d7557d9817ee6c05507e6837fb048 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 7 May 2020 17:15:11 +0200 Subject: [PATCH 088/211] Fix typos --- Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 6267975..fe16248 100644 --- a/Readme.md +++ b/Readme.md @@ -37,7 +37,7 @@ to get bugfixes, other feedback and more contributions. ### Compatibility -testssl.sh is working on every Linux/BSD distribution out of the box. Latest by 2.9dev +Testssl.sh is working on every Linux/BSD distribution out of the box. Latest by 2.9dev most of the limitations of disabled features from the openssl client are gone due to bash-socket-based checks. As a result you can also use e.g. LibreSSL or OpenSSL >= 1.1.1 . testssl.sh also works on other unixoid system out of the box, supposed they have @@ -49,7 +49,7 @@ Update notification here or @ [twitter](https://twitter.com/drwetter). ### Installation -You can download testssl.sh version 3.1dev by cloning this git repository: +You can download testssl.sh branch 3.1dev just by cloning this git repository: git clone --depth 1 https://github.com/drwetter/testssl.sh.git @@ -77,7 +77,7 @@ Successfully built 889fa2f99933 ### Status -We're currently in the development phase, version 3.1dev. 3.1dev will eventually become 3.2. Bigger features are developed in a seperate branch before merged into 3.1dev to avoid hiccups or inconsitencies. Albeit we try to keep 3.1dev as solid as possible things will certainly change in 3.1dev. So if you need stability the 3.0 branch is better for you. Think of the 3.1dev branch like a rolling release. +We're currently in the development phase, version 3.1dev. 3.1dev will eventually become 3.2. Bigger features are developed in a separate branch before merged into 3.1dev to avoid hiccups or inconsistencies. Albeit we try to keep 3.1dev as solid as possible things will certainly change in 3.1dev. So if you need stability the 3.0 branch is better for you. Think of the 3.1dev branch like a rolling release. Support for 2.9.5 has been dropped. Supported is 3.0.x only. @@ -85,7 +85,7 @@ Support for 2.9.5 has been dropped. Supported is 3.0.x only. * .. it is there for reading. Please do so :-) -- at least before asking questions. See man page in groff, html and markdown format in `~/doc/`. * [https://testssl.sh/](https://testssl.sh/) will help to get you started. -* Will Hunt provides a longer, good [description](https://www.4armed.com/blog/doing-your-own-ssl-tls-testing/) for the (older) version 2.8, including useful background info. +* For the (older) version 2.8 will Hunt provides a longer, good [description](https://www.4armed.com/blog/doing-your-own-ssl-tls-testing/), including useful background info. ### Contributing From 8697e3417a2843840e7703883985453fab3a13f4 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 7 May 2020 12:30:40 -0400 Subject: [PATCH 089/211] Fix copy and paste error This commit fixes a bug that was introduced by an accidental copy and paste. --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index dfb976f..813f6b1 100755 --- a/testssl.sh +++ b/testssl.sh @@ -9276,7 +9276,7 @@ run_server_defaults() { if [[ -z "${ciphers_to_test[n+1]}" ]]; then ciphers_to_test[n+1]="${ciphers_to_test[n]/aDSS/}" ciphers_to_test[n+1]="${ciphers_to_test[n+1]/::/:}" - [[ "${ciphe-S 127.0.0.1rs_to_test[n+1]:0:1}" == : ]] && ciphers_to_test[n+1]="${ciphers_to_test[n+1]:1}" + [[ "${ciphers_to_test[n+1]:0:1}" == : ]] && ciphers_to_test[n+1]="${ciphers_to_test[n+1]:1}" fi ciphers_to_test[n]="aDSS" elif [[ "$ciph" == TLS_DH_* ]]; then From cde2ecdc5d35961bffd11fb12ef4c59688efa119 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 8 May 2020 08:23:14 -0400 Subject: [PATCH 090/211] Rate RSA e=1 as CRITICAL PR #1619 set the grade cap to 'F' is the server has a certificate with an RSA with e=1, however, it did not change the rating in the JSON/CSV output. This commit changes the cert_keySize rating to CRITICAL for an RSA key with e=1, regardless of the size of the modulus. It also uses pr_svrty_critical() to print the exponent in this case. --- testssl.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 7f3bfb9..91e1f3e 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8535,9 +8535,14 @@ certificate_info() { cert_spki_info="$(strip_leading_space "$cert_spki_info")" cert_spki_info="${cert_spki_info%%[[:space:]]*}" if [[ -n "$cert_spki_info" ]]; then - out " (exponent is $cert_spki_info)" + if [[ $cert_spki_info -eq 1 ]]; then + out " (exponent is "; pr_svrty_critical "$cert_spki_info"; out ")" + json_rating="CRITICAL" + set_grade_cap "F" "RSA certificate uses exponent of 1" + else + out " (exponent is $cert_spki_info)" + fi json_msg+=" (exponent is $cert_spki_info)" - [[ $cert_spki_info -eq 1 ]] && set_grade_cap "F" "RSA certificate uses exponent of 1" fi ;; "EC") cert_spki_info="${cert_txt##*Subject Public Key Info:}" From ec4feb52e78081035be41a60e48e722888dae995 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 8 May 2020 16:13:30 -0400 Subject: [PATCH 091/211] Fix "local problem" output in ciher_pref_check() When cipher_pref_check() is called in "--ssl-native" mode and the specified protocol is not supported, the message indicating a "local problem" is not properly formatted. --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 91e1f3e..a4451de 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6825,11 +6825,11 @@ cipher_pref_check() { order=""; ciphers_found_with_sockets=false if [[ $proto == ssl3 ]] && ! "$HAS_SSL3" && ! "$using_sockets"; then - out "\n SSLv3: "; pr_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; + prln_local_problem "$OPENSSL doesn't support \"s_client -ssl3\""; return 0 fi if [[ $proto == tls1_3 ]] && ! "$HAS_TLS13" && ! "$using_sockets"; then - out "\n TLSv1.3 "; pr_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; + prln_local_problem "$OPENSSL doesn't support \"s_client -tls1_3\""; return 0 fi From 4f802502a0a985fb55a58edb5dbb10af9cec97f0 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Fri, 8 May 2020 16:15:32 -0400 Subject: [PATCH 092/211] Implement $HAS_SIGALGS The "-sigalgs" option is used in get_server_certificate() to obtain certificates the server uses with TLS 1.3. get_server_certificate() is currently designed to use $OPENSSL, if $OPENSSL supports TLS 1.3. LibreSSL 3.1.{0,1} has added client support for TLS 1.3, but does not support the "-sigalgs" option. So, this commit determines whether the "-sigalgs" option is supported, and if it isn't, then uses tls_sockets(). --- testssl.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 91e1f3e..1ed9088 100755 --- a/testssl.sh +++ b/testssl.sh @@ -307,6 +307,7 @@ HAS_SSL3=false HAS_TLS13=false HAS_X448=false HAS_X25519=false +HAS_SIGALGS=false HAS_PKUTIL=false HAS_PKEY=false HAS_NO_SSL2=false @@ -7542,7 +7543,7 @@ get_server_certificate() { CERTIFICATE_LIST_ORDERING_PROBLEM=false if [[ "$1" =~ "tls1_3" ]]; then [[ $(has_server_protocol "tls1_3") -eq 1 ]] && return 1 - if "$HAS_TLS13"; then + if "$HAS_TLS13" && "$HAS_SIGALGS"; then if [[ "$1" =~ "tls1_3_RSA" ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -showcerts -connect $NODEIP:$PORT $PROXY $SNI -tls1_3 -tlsextdebug -status -msg -sigalgs PSS+SHA256:PSS+SHA384") $ERRFILE >$TMPFILE elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then @@ -18264,6 +18265,7 @@ find_openssl_binary() { HAS_TLS13=false HAS_X448=false HAS_X25519=false + HAS_SIGALGS=false HAS_NO_SSL2=false HAS_NOSERVERNAME=false HAS_CIPHERSUITES=false @@ -18310,6 +18312,10 @@ find_openssl_binary() { $OPENSSL genpkey -algorithm X25519 2>&1 | grep -aq "not found" || \ HAS_X25519=true + if "$HAS_TLS13"; then + $OPENSSL s_client -tls1_3 -sigalgs PSS+SHA256:PSS+SHA384 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ + HAS_SIGALGS=true + fi $OPENSSL s_client -no_ssl2 -connect invalid. 2>&1 | grep -aiq "unknown option" || \ HAS_NO_SSL2=true @@ -18684,6 +18690,7 @@ HAS_SSL3: $HAS_SSL3 HAS_TLS13: $HAS_TLS13 HAS_X448: $HAS_X448 HAS_X25519: $HAS_X25519 +HAS_SIGALGS: $HAS_SIGALGS HAS_NO_SSL2: $HAS_NO_SSL2 HAS_SPDY: $HAS_SPDY HAS_ALPN: $HAS_ALPN From 871db32fb557ebf4fdec914d6b3fd787bff8bcf5 Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Fri, 8 May 2020 22:48:20 -0400 Subject: [PATCH 093/211] Fix a couple typos. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enviroment → environment ususally → usually --- doc/testssl.1 | 4 ++-- doc/testssl.1.html | 4 ++-- doc/testssl.1.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 6aea659..38bbfed 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -128,7 +128,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-warnings \fR\. The warnings parameter determines how testssl\.sh will deal with situations where user input normally will be necessary\. There are two options\. \fBbatch\fR doesn\'t wait for a confirming keypress when a client\- or server\-side probem is encountered\. As of 3\.0 it just then terminates the particular scan\. This is automatically chosen for mass testing (\fB\-\-file\fR)\. \fBoff\fR just skips the warning, the confirmation but continues the scan, independent whether it makes sense or not\. Please note that there are conflicts where testssl\.sh will still ask for confirmation which are the ones which otherwise would have a drastic impact on the results\. Almost any other decision will be made in the future as a best guess by testssl\.sh\. The same can be achieved by setting the environment variable \fBWARNINGS\fR\. . .P -\fB\-\-connect\-timeout \fR This is useful for socket TCP connections to a node\. If the node does not complete a TCP handshake (e\.g\. because it is down or behind a firewall or there\'s an IDS or a tarpit) testssl\.sh may ususally hang for around 2 minutes or even much more\. This parameter instructs testssl\.sh to wait at most \fBseconds\fR for the handshake to complete before giving up\. This option only works if your OS has a timeout binary installed\. CONNECT_TIMEOUT is the corresponding enviroment variable\. +\fB\-\-connect\-timeout \fR This is useful for socket TCP connections to a node\. If the node does not complete a TCP handshake (e\.g\. because it is down or behind a firewall or there\'s an IDS or a tarpit) testssl\.sh may usually hang for around 2 minutes or even much more\. This parameter instructs testssl\.sh to wait at most \fBseconds\fR for the handshake to complete before giving up\. This option only works if your OS has a timeout binary installed\. CONNECT_TIMEOUT is the corresponding environment variable\. . .P \fB\-\-openssl\-timeout \fR This is especially useful for all connects using openssl and practically useful for mass testing\. It avoids the openssl connect to hang for ~2 minutes\. The expected parameter \fBseconds\fR instructs testssl\.sh to wait before the openssl connect will be terminated\. The option is only available if your OS has a timeout binary installed\. As there are different implementations of \fBtimeout\fR: It automatically calls the binary with the right parameters\. OPENSSL_TIMEOUT is the equivalent environment variable\. @@ -167,7 +167,7 @@ Please note that \fBfname\fR has to be in Unix format\. DOS carriage returns won \fB\-\-assuming\-http\fR testssl\.sh normally does upfront an application protocol detection\. In cases where HTTP cannot be automatically detected you may want to use this option\. It enforces testssl\.sh not to skip HTTP specific tests (HTTP header) and to run a browser based client simulation\. Please note that sometimes also the severity depends on the application protocol, e\.g\. SHA1 signed certificates, the lack of any SAN matches and some vulnerabilities will be punished harder when checking a web server as opposed to a mail server\. . .P -\fB\-n, \-\-nodns \fR tells testssl\.sh which DNS lookups should be performed\. \fBmin\fR uses only forward DNS resolution (A and AAAA record or MX record) and skips CAA lookups and PTR records from the IP address back to a DNS name\. \fBnone\fR performs no DNS lookups at all\. For the latter you either have to supply the IP address as a target, to use \fB\-\-ip\fR or have the IP address in \fB/etc/hosts\fR\. The use of the switch is only useful if you either can\'t or are not willing to perform DNS lookups\. The latter can apply e\.g\. to some pentests\. In general this option could e\.g\. help you to avoid timeouts by DNS lookups\. \fBNODNS\fR is the enviroment variable for this\. +\fB\-n, \-\-nodns \fR tells testssl\.sh which DNS lookups should be performed\. \fBmin\fR uses only forward DNS resolution (A and AAAA record or MX record) and skips CAA lookups and PTR records from the IP address back to a DNS name\. \fBnone\fR performs no DNS lookups at all\. For the latter you either have to supply the IP address as a target, to use \fB\-\-ip\fR or have the IP address in \fB/etc/hosts\fR\. The use of the switch is only useful if you either can\'t or are not willing to perform DNS lookups\. The latter can apply e\.g\. to some pentests\. In general this option could e\.g\. help you to avoid timeouts by DNS lookups\. \fBNODNS\fR is the environment variable for this\. . .P \fB\-\-sneaky\fR For HTTP header checks testssl\.sh uses normally the server friendly HTTP user agent \fBTLS tester from ${URL}\fR\. With this option your traces are less verbose and a Firefox user agent is being used\. Be aware that it doesn\'t hide your activities\. That is just not possible (environment preset via \fBSNEAKY=true\fR)\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 8422223..8290958 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -181,7 +181,7 @@ host.example.com:631

    --warnings <batch|off>. The warnings parameter determines how testssl.sh will deal with situations where user input normally will be necessary. There are two options. batch doesn't wait for a confirming keypress when a client- or server-side probem is encountered. As of 3.0 it just then terminates the particular scan. This is automatically chosen for mass testing (--file). off just skips the warning, the confirmation but continues the scan, independent whether it makes sense or not. Please note that there are conflicts where testssl.sh will still ask for confirmation which are the ones which otherwise would have a drastic impact on the results. Almost any other decision will be made in the future as a best guess by testssl.sh. The same can be achieved by setting the environment variable WARNINGS.

    -

    --connect-timeout <seconds> This is useful for socket TCP connections to a node. If the node does not complete a TCP handshake (e.g. because it is down or behind a firewall or there's an IDS or a tarpit) testssl.sh may ususally hang for around 2 minutes or even much more. This parameter instructs testssl.sh to wait at most seconds for the handshake to complete before giving up. This option only works if your OS has a timeout binary installed. CONNECT_TIMEOUT is the corresponding enviroment variable.

    +

    --connect-timeout <seconds> This is useful for socket TCP connections to a node. If the node does not complete a TCP handshake (e.g. because it is down or behind a firewall or there's an IDS or a tarpit) testssl.sh may usually hang for around 2 minutes or even much more. This parameter instructs testssl.sh to wait at most seconds for the handshake to complete before giving up. This option only works if your OS has a timeout binary installed. CONNECT_TIMEOUT is the corresponding environment variable.

    --openssl-timeout <seconds> This is especially useful for all connects using openssl and practically useful for mass testing. It avoids the openssl connect to hang for ~2 minutes. The expected parameter seconds instructs testssl.sh to wait before the openssl connect will be terminated. The option is only available if your OS has a timeout binary installed. As there are different implementations of timeout: It automatically calls the binary with the right parameters. OPENSSL_TIMEOUT is the equivalent environment variable.

    @@ -212,7 +212,7 @@ The same can be achieved by setting the environment variable WARNINGS--assuming-http testssl.sh normally does upfront an application protocol detection. In cases where HTTP cannot be automatically detected you may want to use this option. It enforces testssl.sh not to skip HTTP specific tests (HTTP header) and to run a browser based client simulation. Please note that sometimes also the severity depends on the application protocol, e.g. SHA1 signed certificates, the lack of any SAN matches and some vulnerabilities will be punished harder when checking a web server as opposed to a mail server.

    -n, --nodns <min|none> tells testssl.sh which DNS lookups should be performed. min uses only forward DNS resolution (A and AAAA record or MX record) and skips CAA lookups and PTR records from the IP address back to a DNS name. none performs no DNS lookups at all. For the latter you either have to supply the IP address as a target, to use --ip or have the IP address -in /etc/hosts. The use of the switch is only useful if you either can't or are not willing to perform DNS lookups. The latter can apply e.g. to some pentests. In general this option could e.g. help you to avoid timeouts by DNS lookups. NODNS is the enviroment variable for this.

    +in /etc/hosts. The use of the switch is only useful if you either can't or are not willing to perform DNS lookups. The latter can apply e.g. to some pentests. In general this option could e.g. help you to avoid timeouts by DNS lookups. NODNS is the environment variable for this.

    --sneaky For HTTP header checks testssl.sh uses normally the server friendly HTTP user agent TLS tester from ${URL}. With this option your traces are less verbose and a Firefox user agent is being used. Be aware that it doesn't hide your activities. That is just not possible (environment preset via SNEAKY=true).

    diff --git a/doc/testssl.1.md b/doc/testssl.1.md index a63fb26..b695822 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -104,7 +104,7 @@ Please note that `fname` has to be in Unix format. DOS carriage returns won't be `--warnings `. The warnings parameter determines how testssl.sh will deal with situations where user input normally will be necessary. There are two options. `batch` doesn't wait for a confirming keypress when a client- or server-side probem is encountered. As of 3.0 it just then terminates the particular scan. This is automatically chosen for mass testing (`--file`). `off` just skips the warning, the confirmation but continues the scan, independent whether it makes sense or not. Please note that there are conflicts where testssl.sh will still ask for confirmation which are the ones which otherwise would have a drastic impact on the results. Almost any other decision will be made in the future as a best guess by testssl.sh. The same can be achieved by setting the environment variable `WARNINGS`. -`--connect-timeout ` This is useful for socket TCP connections to a node. If the node does not complete a TCP handshake (e.g. because it is down or behind a firewall or there's an IDS or a tarpit) testssl.sh may ususally hang for around 2 minutes or even much more. This parameter instructs testssl.sh to wait at most `seconds` for the handshake to complete before giving up. This option only works if your OS has a timeout binary installed. CONNECT_TIMEOUT is the corresponding enviroment variable. +`--connect-timeout ` This is useful for socket TCP connections to a node. If the node does not complete a TCP handshake (e.g. because it is down or behind a firewall or there's an IDS or a tarpit) testssl.sh may usually hang for around 2 minutes or even much more. This parameter instructs testssl.sh to wait at most `seconds` for the handshake to complete before giving up. This option only works if your OS has a timeout binary installed. CONNECT_TIMEOUT is the corresponding environment variable. `--openssl-timeout ` This is especially useful for all connects using openssl and practically useful for mass testing. It avoids the openssl connect to hang for ~2 minutes. The expected parameter `seconds` instructs testssl.sh to wait before the openssl connect will be terminated. The option is only available if your OS has a timeout binary installed. As there are different implementations of `timeout`: It automatically calls the binary with the right parameters. OPENSSL_TIMEOUT is the equivalent environment variable. @@ -137,7 +137,7 @@ The same can be achieved by setting the environment variable `WARNINGS`. `--assuming-http` testssl.sh normally does upfront an application protocol detection. In cases where HTTP cannot be automatically detected you may want to use this option. It enforces testssl.sh not to skip HTTP specific tests (HTTP header) and to run a browser based client simulation. Please note that sometimes also the severity depends on the application protocol, e.g. SHA1 signed certificates, the lack of any SAN matches and some vulnerabilities will be punished harder when checking a web server as opposed to a mail server. `-n, --nodns ` tells testssl.sh which DNS lookups should be performed. `min` uses only forward DNS resolution (A and AAAA record or MX record) and skips CAA lookups and PTR records from the IP address back to a DNS name. `none` performs no DNS lookups at all. For the latter you either have to supply the IP address as a target, to use `--ip` or have the IP address -in `/etc/hosts`. The use of the switch is only useful if you either can't or are not willing to perform DNS lookups. The latter can apply e.g. to some pentests. In general this option could e.g. help you to avoid timeouts by DNS lookups. `NODNS` is the enviroment variable for this. +in `/etc/hosts`. The use of the switch is only useful if you either can't or are not willing to perform DNS lookups. The latter can apply e.g. to some pentests. In general this option could e.g. help you to avoid timeouts by DNS lookups. `NODNS` is the environment variable for this. `--sneaky` For HTTP header checks testssl.sh uses normally the server friendly HTTP user agent `TLS tester from ${URL}`. With this option your traces are less verbose and a Firefox user agent is being used. Be aware that it doesn't hide your activities. That is just not possible (environment preset via `SNEAKY=true`). From d065bda3b8538ba8bc9cd4f077685536d2a95dd1 Mon Sep 17 00:00:00 2001 From: Dirk Date: Sat, 9 May 2020 13:17:02 +0200 Subject: [PATCH 094/211] Better Dockerfile: doesn't require "git clone" .. see also #1559. It "mkdirs" the needed etc and bin directories first, then copies stuff over. It also reduces a few layers. Also it corrects a mistake in the Readme.md (docker exec --> run) Thanks for the discussion @Alex131089 --- Dockerfile | 17 ++++++++++------- Readme.md | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11e2759..42a6941 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,19 @@ FROM alpine:3.11 -RUN apk update && apk upgrade && apk add --no-cache bash procps drill git coreutils libidn curl - -RUN addgroup testssl -RUN adduser -G testssl -g "testssl user" -s /bin/bash -D testssl - -RUN ln -s /home/testssl/testssl.sh /usr/local/bin/ +RUN apk update && \ + apk upgrade && \ + apk add --no-cache bash procps drill git coreutils libidn curl && \ + addgroup testssl && \ + adduser -G testssl -g "testssl user" -s /bin/bash -D testssl && \ + ln -s /home/testssl/testssl.sh /usr/local/bin/ && \ + mkdir -m 755 -p /home/testssl/etc /home/testssl/bin USER testssl WORKDIR /home/testssl/ -RUN git clone --depth=1 https://github.com/drwetter/testssl.sh.git . +COPY --chown=testssl:testssl etc/. /home/testssl/etc/ +COPY --chown=testssl:testssl bin/. /home/testssl/bin/ +COPY --chown=testssl:testssl testssl.sh /home/testssl/ ENTRYPOINT ["testssl.sh"] diff --git a/Readme.md b/Readme.md index fe16248..bc1dca3 100644 --- a/Readme.md +++ b/Readme.md @@ -68,7 +68,7 @@ Or if you have cloned this repo you also can just ``cd`` to the INSTALLDIR and r docker build . ``` -followed by ``docker exec -ti `` where ``ID`` is the identifier in the last line from the build command like +followed by ``docker run -ti `` where ``ID`` is the identifier in the last line from the build command like ``` ---> 889fa2f99933 From 825cbf7b1deafe89f6a05e80bad5b2d88db3bf0a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 11 May 2020 10:41:48 +0200 Subject: [PATCH 095/211] Improve compression detection for BREACH This commit tries to enummerate through all possible compressions instead of just raising the arm because of the first one detected. As far as the performance is concerned there's room for improvements which subsequent commits will address. --- testssl.sh | 67 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/testssl.sh b/testssl.sh index 39c67e3..6c61516 100755 --- a/testssl.sh +++ b/testssl.sh @@ -15887,6 +15887,7 @@ run_crime() { # BREACH is a HTTP-level compression & an attack which works against any cipher suite and is agnostic # to the version of TLS/SSL, more: http://www.breachattack.com/ . Foreign referrers are the important thing here! # Mitigation: see https://community.qualys.com/message/20360 +# Any URL can be vulnerable. Here only the given URL is tested. See also $when_makesense # run_breach() { local header @@ -15899,8 +15900,10 @@ run_breach() { local when_makesense=" Can be ignored for static pages or if no secrets in the page" local cve="CVE-2013-3587" local cwe="CWE-310" - local hint="" + local hint="" c="" local jsonID="BREACH" + local compressions="gzip deflate compress br" + local has_compression=() [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH" && return 7 @@ -15925,33 +15928,55 @@ run_breach() { [[ "$NODE" =~ google ]] && referer="https://yandex.ru/" # otherwise we have a false positive for google.com useragent="$UA_STD" $SNEAKY && useragent="$UA_SNEAKY" - tm_out "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: gzip,deflate,compress\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & - wait_kill $! $HEADER_MAXSLEEP - was_killed=$? # !=0 was killed - result=$(awk '/^Content-Encoding/ { print $2 }' $TMPFILE) - result=$(strip_lf "$result") - debugme grep '^Content-Encoding' $TMPFILE - if [[ ! -s $TMPFILE ]]; then - pr_warning "failed (HTTP header request stalled or empty return" - if [[ $was_killed -ne 0 ]]; then - pr_warning " and was terminated" - fileout "$jsonID" "WARN" "Test failed as HTTP request stalled and was terminated" "$cve" "$cwe" + for c in $compressions; do + tm_out "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: $c\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & + wait_kill $! $HEADER_MAXSLEEP + was_killed=$? # !=0 was killed + result=$(grep -ia ^Content-Encoding: $TMPFILE) + debugme echo "$result" + result="$(strip_lf "$result")" + result="${result#*:}" + result="$(strip_spaces "$result")" + if [[ ! -s $TMPFILE ]]; then + if [[ $was_killed -ne 0 ]]; then + has_compression+=("$c:warn_stalled") + else + has_compression+=("$c:warn_empty") + fi + ret+=1 + elif [[ -z $result ]]; then + has_compression+=("$c:no") else - fileout "$jsonID" "WARN" "Test failed as HTTP response was empty" "$cve" "$cwe" + has_compression+=("$c:yes") fi - prln_warning ") " - ret=1 - elif [[ -z $result ]]; then - pr_svrty_best "no HTTP compression (OK) " + done + + result="" + if [[ ! ${has_compression[@]} =~ yes ]] && [[ ! ${has_compression[@]} =~ warn ]]; then + pr_svrty_best "no gzip/deflate/compress/br HTTP compression (OK) " outln "$disclaimer" - fileout "$jsonID" "OK" "not vulnerable, no HTTP compression $disclaimer" "$cve" "$cwe" + fileout "$jsonID" "OK" "not vulnerable, no gzip/deflate/compress/br HTTP compression $disclaimer" "$cve" "$cwe" + elif [[ ${has_compression[@]} =~ warn_stalled ]]; then + pr_warning "At least 1/4 checks failed (HTTP header request stalled and was terminated" + out ", debug: ${has_compression[@]})" + fileout "$jsonID" "WARN" "Test failed as HTTP request stalled and was terminated" "$cve" "$cwe" + elif [[ ${has_compression[@]} =~ warn_empty ]]; then + pr_warning "At least 1/4 checks failed (HTTP header request was empty, debug: ${has_compression[@]}" + out ", debug: ${has_compression[@]})" + fileout "$jsonID" "WARN" "Test failed as HTTP response was empty, debug: ${has_compression[@]}" "$cve" "$cwe" else - pr_svrty_high "potentially NOT ok, uses $result HTTP compression." + for c in ${has_compression[@]}; do + if [[ $c =~ yes ]]; then + result+="${c%:*} " + fi + done + result="$(strip_trailing_space "$result")" + pr_svrty_high "potentially NOT ok, \"$result\" HTTP compression detected." outln "$disclaimer" outln "$spaces$when_makesense" - fileout "$jsonID" "HIGH" "potentially VULNERABLE, uses $result HTTP compression $disclaimer" "$cve" "$cwe" "$hint" + fileout "$jsonID" "HIGH" "potentially VULNERABLE, $result HTTP compression detected $disclaimer" "$cve" "$cwe" "$hint" fi - # Any URL can be vulnerable. I am testing now only the given URL! + debugme echo "has_compression: ${has_compression[@]}" tmpfile_handle ${FUNCNAME[0]}.txt return $ret From 25f87455a4bc4ce442396b6bdd2afd005dede07b Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 11 May 2020 13:57:50 +0200 Subject: [PATCH 096/211] Amendment for compression detection (BREACH) This commit saves more or less time for a detection of the compression. First it assembles the GET command with all available compressions and send them all. If the result is negative: we can just tell the finding and return. If it's positive: We already have identified 1x compression. Then we cycle through the remaining compressions with single GET requests. In order to not duplicate code we introduced a helper function sub_breach_helper() which takes care sending the request and analysis the result. We treat now failed requests differently: When the first fails we don't continue anymore. --- testssl.sh | 167 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 57 deletions(-) diff --git a/testssl.sh b/testssl.sh index 6c61516..13c633d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -15884,15 +15884,48 @@ run_crime() { } -# BREACH is a HTTP-level compression & an attack which works against any cipher suite and is agnostic -# to the version of TLS/SSL, more: http://www.breachattack.com/ . Foreign referrers are the important thing here! + +# As the name says. It expects as arg1 a GET command string. It returns 1 +# when GET command was stalled or killed (which is no not always used) +# and echos "warn_*". It return 0 when everything went ok and echos the +# compression if any. +sub_breach_helper() { + local get_command="$1" + local detected_compression="" + local -i was_killed=0 + + safe_echo "$get_command" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & + wait_kill $! $HEADER_MAXSLEEP + was_killed=$? # !=0 when it was killed + detected_compression=$(grep -ia ^Content-Encoding: $TMPFILE) + detected_compression="$(strip_lf "$detected_compression")" + detected_compression="${detected_compression#*:}" + detected_compression="$(strip_spaces "$detected_compression")" + if [[ ! -s $TMPFILE ]]; then + if [[ $was_killed -eq 0 ]]; then + echo "warn_stalled" + else + echo "warn_killed" + fi + return 1 + elif [[ -z $detected_compression ]]; then + echo "no_compression" + else + echo "$detected_compression" + fi + return 0 +} + + + +# BREACH is a HTTP-level compression & an attack which works against any cipher suite and is agnostic to the +# version of TLS/SSL, more: http://www.breachattack.com/ . External referrers are the important thing here! # Mitigation: see https://community.qualys.com/message/20360 # Any URL can be vulnerable. Here only the given URL is tested. See also $when_makesense # run_breach() { local header local -i ret=0 - local -i was_killed=0 local referer useragent local url="$1" local spaces=" " @@ -15904,6 +15937,8 @@ run_breach() { local jsonID="BREACH" local compressions="gzip deflate compress br" local has_compression=() + local detected_compression="" + local get_command="" [[ $SERVICE != HTTP ]] && ! "$CLIENT_AUTH" && return 7 @@ -15914,13 +15949,6 @@ run_breach() { fileout "$jsonID" "INFO" "was not tested, server side requires x509 authentication" "$cve" "$cwe" fi - # if [[ $NR_HEADER_FAIL -ge $MAX_HEADER_FAIL ]]; then - # pr_warning "Retrieving HTTP header failed before. Skipping." - # fileout "$jsonID" "WARN" "HTTP response was wampty before" "$cve" "$cwe" - # outln - # return 1 - # fi - [[ -z "$url" ]] && url="/" disclaimer=" - only supplied \"$url\" tested" @@ -15928,55 +15956,80 @@ run_breach() { [[ "$NODE" =~ google ]] && referer="https://yandex.ru/" # otherwise we have a false positive for google.com useragent="$UA_STD" $SNEAKY && useragent="$UA_SNEAKY" - for c in $compressions; do - tm_out "GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: $c\r\nAccept: text/*\r\n\r\n" | $OPENSSL s_client $(s_client_options "$OPTIMAL_PROTO $BUGS -quiet -ign_eof -connect $NODEIP:$PORT $PROXY $SNI") 1>$TMPFILE 2>$ERRFILE & - wait_kill $! $HEADER_MAXSLEEP - was_killed=$? # !=0 was killed - result=$(grep -ia ^Content-Encoding: $TMPFILE) - debugme echo "$result" - result="$(strip_lf "$result")" - result="${result#*:}" - result="$(strip_spaces "$result")" - if [[ ! -s $TMPFILE ]]; then - if [[ $was_killed -ne 0 ]]; then - has_compression+=("$c:warn_stalled") - else - has_compression+=("$c:warn_empty") - fi - ret+=1 - elif [[ -z $result ]]; then - has_compression+=("$c:no") - else - has_compression+=("$c:yes") - fi - done - result="" - if [[ ! ${has_compression[@]} =~ yes ]] && [[ ! ${has_compression[@]} =~ warn ]]; then - pr_svrty_best "no gzip/deflate/compress/br HTTP compression (OK) " - outln "$disclaimer" - fileout "$jsonID" "OK" "not vulnerable, no gzip/deflate/compress/br HTTP compression $disclaimer" "$cve" "$cwe" - elif [[ ${has_compression[@]} =~ warn_stalled ]]; then - pr_warning "At least 1/4 checks failed (HTTP header request stalled and was terminated" - out ", debug: ${has_compression[@]})" - fileout "$jsonID" "WARN" "Test failed as HTTP request stalled and was terminated" "$cve" "$cwe" - elif [[ ${has_compression[@]} =~ warn_empty ]]; then - pr_warning "At least 1/4 checks failed (HTTP header request was empty, debug: ${has_compression[@]}" - out ", debug: ${has_compression[@]})" - fileout "$jsonID" "WARN" "Test failed as HTTP response was empty, debug: ${has_compression[@]}" "$cve" "$cwe" - else - for c in ${has_compression[@]}; do - if [[ $c =~ yes ]]; then - result+="${c%:*} " + # Assemble the GET command with all available compressions and send them all, initially. + # If the result is negative: we can just tell the finding and return. If it's + # positive: We already have identified 1x compression + get_command="GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: ${compressions// /,}\r\nAccept: text/*\r\n\r\n" + detected_compression=$(sub_breach_helper "$get_command") + case "$detected_compression" in + warn_stalled) + pr_warning "First request failed (HTTP header request stalled and was terminated)" + fileout "$jsonID" "WARN" "Test failed as first HTTP request stalled and was terminated" "$cve" "$cwe" + ret=1 + ;; + warn_failed) + pr_warning "First request failed (HTTP header request was empty)" + fileout "$jsonID" "WARN" "Test failed as first HTTP response was empty" "$cve" "$cwe" + ret=1 + ;; + no_compression) + pr_svrty_best "no gzip/deflate/compress/br HTTP compression (OK) " + outln "$disclaimer" + fileout "$jsonID" "OK" "not vulnerable, no gzip/deflate/compress/br HTTP compression $disclaimer" "$cve" "$cwe" + ret=0 + ;; + *) # Now assemble the remaining compressions in $compressions and loop through them + has_compression+=("$detected_compression:yes") + compressions="${compressions//$detected_compression/}" + for c in $compressions; do + get_command="GET $url HTTP/1.1\r\nHost: $NODE\r\nUser-Agent: $useragent\r\nReferer: $referer\r\nConnection: Close\r\nAccept-encoding: ${c}\r\nAccept: text/*\r\n\r\n" + detected_compression=$(sub_breach_helper "$get_command") + if [[ $? -ne 0 ]]; then + # This failure unlikely here. The initial request must have succeeded and this one then + # failed but we'd rather treat this correctly (e.d. IDS which triggers later). Not also + # we exit on the first stalled request. So if the first one with all compressions failed, + # we don't get here. It seems very unlikely the first failed and subsequent will succeed. + has_compression+=("$c:$compressions") + elif [[ "$detected_compression" =~ no_compression ]]; then + has_compression+=("$c:no") + debugme echo "has_compression: $c: no" + elif [[ -n "detected_compression" ]]; then + has_compression+=("$c:yes") + debugme echo "has_compression: $c: yes" + else + prln_fixme "strange reply around line $((LINENO)) from sub_breach_helper()" + fi + done + + # Final verdict (if not happened preemptively before). We reuse $detected_compression here + detected_compression="" + if [[ ${has_compression[@]} =~ warn ]]; then + # warn_empty / warn_stalled + if [[ ${has_compression[@]} =~ warn_empty ]]; then + pr_warning "At least 1/4 checks failed (HTTP header request was empty, debug: ${has_compression[@]}" + out ", debug: ${has_compression[@]})" + fileout "$jsonID" "WARN" "Test failed as HTTP response was empty, debug: ${has_compression[@]}" "$cve" "$cwe" + else # warn_stalled + pr_warning "At least 1/4 checks failed (HTTP header request stalled and was terminated" + out ", debug: ${has_compression[@]})" + fileout "$jsonID" "WARN" "Test failed as HTTP request stalled and was terminated" "$cve" "$cwe" + fi + else + for c in ${has_compression[@]}; do + if [[ $c =~ yes ]]; then + detected_compression+="${c%:*} " + fi + done + detected_compression="$(strip_trailing_space "$detected_compression")" + pr_svrty_high "potentially NOT ok, \"$detected_compression\" HTTP compression detected." + outln "$disclaimer" + outln "${spaces}${when_makesense}" + fileout "$jsonID" "HIGH" "potentially VULNERABLE, $detected_compression HTTP compression detected $disclaimer" "$cve" "$cwe" "$hint" fi - done - result="$(strip_trailing_space "$result")" - pr_svrty_high "potentially NOT ok, \"$result\" HTTP compression detected." - outln "$disclaimer" - outln "$spaces$when_makesense" - fileout "$jsonID" "HIGH" "potentially VULNERABLE, $result HTTP compression detected $disclaimer" "$cve" "$cwe" "$hint" - fi - debugme echo "has_compression: ${has_compression[@]}" + debugme outln "${spaces}has_compression: ${has_compression[@]}" + ;; + esac tmpfile_handle ${FUNCNAME[0]}.txt return $ret From 78a95d73c8608cd2db559d45955b5e9b912e4c70 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Mon, 11 May 2020 14:07:02 +0200 Subject: [PATCH 097/211] Add BREACH extension --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d0236..6641957 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Added several ciphers to colored ciphers * Percent output char problem fixed * Several display/output fixes +* BREACH check: list all compression methods and add brotli * Security fix: DNS input * Don't use external pwd anymore * STARTTLS: XMPP server support From 6119d8538e5e486acb3681c3ba793cce6f04a814 Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Mon, 11 May 2020 15:20:16 +0200 Subject: [PATCH 098/211] proper rating of dh group length --- doc/testssl.1.md | 3 +-- testssl.sh | 22 +++++++--------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index b695822..efbd11e 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -145,7 +145,7 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. -`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively +`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively ADDTL_CA_FILES is the environment variable for this. @@ -404,7 +404,6 @@ As of writing, these checks are missing: * Zombie POODLE - should be graded **F** if vulnerable * All remaining old Symantec PKI certificates are distrusted - should be graded **T** * Symantec certificates issued before June 2016 are distrusted - should be graded **T** -* ! A reading of DH params - should give correct points in `set_key_str_score()` * Anonymous key exchange - should give **0** points in `set_key_str_score()` * Exportable key exchange - should give **40** points in `set_key_str_score()` * Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()` diff --git a/testssl.sh b/testssl.sh index 39c67e3..8ef3c5d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1030,7 +1030,7 @@ set_grade_warning() { } # Sets the score for Category 2 (Key Exchange Strength) -# arg1: Short key algorithm ("EC", "DH", "RSA", ...) # Can die, when we get DH_PARAMs +# arg1: Short key algorithm ("EC", "DH", "RSA", ...), or "DHE" for ephemeral key size # arg2: key size (number of bits) set_key_str_score() { local type=$1 @@ -1038,13 +1038,8 @@ set_key_str_score() { "$do_rating" || return 0 - # TODO: We need to get the size of DH params (follows the same table as the "else" clause) - # For now, verifying the key size will do... if [[ $type == EC ]]; then - if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then - let KEY_EXCH_SCORE=20 - set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then + if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 set_grade_cap "F" "Using an insecure key" elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then @@ -1054,15 +1049,12 @@ set_key_str_score() { let KEY_EXCH_SCORE=90 fi else - if [[ $size -lt 512 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then - let KEY_EXCH_SCORE=20 - set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then + if [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using an insecure key" + set_grade_cap "F" "Using an insecure key / DH key exchange parameters" elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then let KEY_EXCH_SCORE=80 - set_grade_cap "B" "Using a weak key" + set_grade_cap "B" "Using a weak key / DH key exchange parameters" elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then let KEY_EXCH_SCORE=90 fi @@ -16677,7 +16669,6 @@ run_logjam() { if "$vuln_exportdh_ciphers"; then pr_svrty_high "VULNERABLE (NOT ok):"; out " uses DH EXPORT ciphers" fileout "$jsonID" "HIGH" "VULNERABLE, uses DH EXPORT ciphers" "$cve" "$cwe" "$hint" - set_grade_cap "B" "Uses weak DH key exchange parameters (vulnerable to LOGJAM)" if [[ $subret -eq 3 ]]; then out ", no DH key detected with <= TLS 1.2" fileout "$jsonID2" "OK" "no DH key detected with <= TLS 1.2" @@ -16693,7 +16684,6 @@ run_logjam() { else if [[ $subret -eq 1 ]]; then out_common_prime "$jsonID2" "$cve" "$cwe" - set_grade_cap "A" "Uses known DH key exchange parameters" if ! "$openssl_no_expdhciphers"; then outln "," out "${spaces}but no DH EXPORT ciphers${addtl_warning}" @@ -16726,6 +16716,8 @@ run_logjam() { fi fi + [[ $DH_GROUP_LEN_P -gt 0 ]] && set_key_str_score "DHE" $DH_GROUP_LEN_P + outln tmpfile_handle ${FUNCNAME[0]}.txt return $ret From 8b74d41487ceb4d39d27b870e60382654490bc25 Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Mon, 11 May 2020 15:22:51 +0200 Subject: [PATCH 099/211] unintended linebreak --- doc/testssl.1.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index efbd11e..5198def 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -145,8 +145,7 @@ in `/etc/hosts`. The use of the switch is only useful if you either can't or ar `--phone-out` Checking for revoked certificates via CRL and OCSP is not done per default. This switch instructs testssl.sh to query external -- in a sense of the current run -- URIs. By using this switch you acknowledge that the check might have privacy issues, a download of several megabytes (CRL file) may happen and there may be network connectivity problems while contacting the endpoint which testssl.sh doesn't handle. PHONE_OUT is the environment variable for this which needs to be set to true if you want this. -`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively -ADDTL_CA_FILES is the environment variable for this. +`--add-ca ` enables you to add your own CA(s) for trust chain checks. `cafile` can be a single path or multiple paths as a comma separated list of root CA files. Internally they will be added during runtime to all CA stores. This is (only) useful for internal hosts whose certificates is issued by internal CAs. Alternatively ADDTL_CA_FILES is the environment variable for this. ### SINGLE CHECK OPTIONS From d5671a0352d15ed20e09dcba52fcdc75f44d6014 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 11 May 2020 10:09:02 -0400 Subject: [PATCH 100/211] Fix $SHOW_SIGALGO This commit fixes two issues related to $SHOW_SIGALGO. First, cipher_pref_check() does not show the signature algorithm if any of the ciphers were found using tls_sockets(), since the call to tls_sockets() does not specify that the server's certificate should be extracted. Second, in run_beast() the call to tls_sockets() indicates that the server's certificate should be extracted if "$SHOW_SIGALGO" is true, even if "$WIDE" is false. While this does not cause any problems, extracting the certificate is a waste of effort if "$WIDE" is false, since the signature algorithm is not shown in that case. --- testssl.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index 39c67e3..3570521 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6993,7 +6993,11 @@ cipher_pref_check() { ! "${ciphers_found2[i]}" && ciphers_to_test+=", ${hexcode[i]}" done [[ -z "$ciphers_to_test" ]] && break - tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey" + if "$wide" && "$SHOW_SIGALGO"; then + tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "all" + else + tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey" + fi [[ $? -ne 0 ]] && break cipher=$(get_cipher "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt") for (( i=0; i < nr_ciphers; i++ )); do @@ -17006,7 +17010,7 @@ run_beast(){ ! "${ciphers_found[i]}" && ciphers_to_test+=", ${hexcode[i]}" done [[ -z "$ciphers_to_test" ]] && break - if "$SHOW_SIGALGO"; then + if "$WIDE" && "$SHOW_SIGALGO"; then tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "all" else tls_sockets "$proto_hex" "${ciphers_to_test:2}, 00,ff" "ephemeralkey" From df42eeb8b4b1510a8550b55702f25ef4d1af6ff6 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 14 May 2020 14:42:08 -0400 Subject: [PATCH 101/211] Extract server's signature algorithm PR #1519 requested that testssl.sh show the signature algorithm that the server uses during the TLS handshake. In TLS 1.3, this appears in the CertificateVerify message. In TLS 1.2 it appears in the ServerKeyExchange message when the chosen cipher suite uses an ephemeral (DH or ECDH) key, except in the case of cipher suites that provide no authentication. This information is not present in TLS 1.1 and earlier, as the hash algorithm to use in these earlier versions of the protocol is hard coded into the specification. This commit takes a first step towards being able to show the signature algorithm by extending parse_tls_serverhello() to extract the signature algorithm when it is present. Matching the output produced by OpenSSL, it output two separate lines, the "Peer signature type" (RSA, RSA-PSS, DSA, ECDSA, Ed25519, or Ed448) and the "Peer signing digest" (MD5, SHA1, SHA224, SHA256, SHA384, or SHA512). This will allow the same function to extract the signature algorithm and digest, whether the handshake was performed using "$OPENSSL s_client" or tls_sockets(). --- testssl.sh | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index ebf8389..e29bda3 100755 --- a/testssl.sh +++ b/testssl.sh @@ -12800,6 +12800,7 @@ parse_tls_serverhello() { local len1 len2 len3 key_bitstring="" pem_certificate local dh_p dh_param ephemeral_param rfc7919_param local -i dh_p_len dh_param_len + local peering_signing_digest=0 peer_signature_type=0 DETECTED_TLS_VERSION="" [[ $DEBUG -ge 1 ]] && echo > $TMPFILE @@ -13033,6 +13034,14 @@ parse_tls_serverhello() { fi tls_serverkeyexchange_ascii="${tls_handshake_ascii:i:msg_len}" tls_serverkeyexchange_ascii_len=$msg_len + elif [[ "$tls_msg_type" == 0F ]]; then + if [[ $msg_len -lt 4 ]]; then + debugme tmln_warning "Response contained malformed certificate_verify message." + return 1 + fi + # Extract just the SignatureAndHashAlgorithm from the CertificateVerify message. + peering_signing_digest="${tls_handshake_ascii:i:2}" + peer_signature_type="${tls_handshake_ascii:$((i+2)):2}" elif [[ "$process_full" =~ all ]] && [[ "$tls_msg_type" == 16 ]]; then if [[ -n "$tls_certificate_status_ascii" ]]; then debugme tmln_warning "Response contained more than one certificate_status handshake message." @@ -13811,12 +13820,24 @@ parse_tls_serverhello() { 29) dh_bits=253 ; named_curve_str="X25519" ;; 30) dh_bits=448 ; named_curve_str="X448" ;; esac + if [[ "$DETECTED_TLS_VERSION" == 0303 ]]; then + # Skip over the public key to get to the SignatureAndHashAlgorithm + # This is TLS 1.2-only, as this field does not appear in earlier versions. + len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:6:2}") + offset=$((len1+8)) + if [[ $tls_serverkeyexchange_ascii_len -ge $((offset+4)) ]]; then + # The SignatureAndHashAlgorithm won't be present in an anonymous + # key exhange. + peering_signing_digest="${tls_serverkeyexchange_ascii:offset:2}" + peer_signature_type="${tls_serverkeyexchange_ascii:$((offset+2)):2}" + fi + fi fi if [[ $dh_bits -ne 0 ]] && [[ $named_curve -ne 29 ]] && [[ $named_curve -ne 30 ]]; then - [[ $DEBUG -ge 3 ]] && echo -e " dh_bits: ECDH, $named_curve_str, $dh_bits bits\n" + [[ $DEBUG -ge 3 ]] && echo -e " dh_bits: ECDH, $named_curve_str, $dh_bits bits" echo "Server Temp Key: ECDH, $named_curve_str, $dh_bits bits" >> $TMPFILE elif [[ $dh_bits -ne 0 ]]; then - [[ $DEBUG -ge 3 ]] && echo -e " dh_bits: $named_curve_str, $dh_bits bits\n" + [[ $DEBUG -ge 3 ]] && echo -e " dh_bits: $named_curve_str, $dh_bits bits" echo "Server Temp Key: $named_curve_str, $dh_bits bits" >> $TMPFILE fi elif [[ $rfc_cipher_suite =~ TLS_DHE_ ]] || [[ $rfc_cipher_suite =~ TLS_DH_anon ]] || \ @@ -13875,10 +13896,73 @@ parse_tls_serverhello() { [[ "$ephemeral_param" != "$rfc7919_param" ]] && named_curve_str="" fi - [[ $DEBUG -ge 3 ]] && [[ $dh_bits -ne 0 ]] && echo -e " dh_bits: DH,$named_curve_str $dh_bits bits\n" + [[ $DEBUG -ge 3 ]] && [[ $dh_bits -ne 0 ]] && echo -e " dh_bits: DH,$named_curve_str $dh_bits bits" [[ $dh_bits -ne 0 ]] && echo "Server Temp Key: DH,$named_curve_str $dh_bits bits" >> $TMPFILE + if [[ "$DETECTED_TLS_VERSION" == 0303 ]]; then + # Skip over the public key (P, G, Y) to get to the SignatureAndHashAlgorithm + # This is TLS 1.2-only, as this field does not appear in earlier versions. + offset=$((dh_p_len+4)) + if [[ $tls_serverkeyexchange_ascii_len -lt $((offset+4)) ]]; then + debugme echo "Malformed ServerKeyExchange Handshake message in ServerHello." + tmpfile_handle ${FUNCNAME[0]}.txt + return 1 + fi + len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:offset:4}") + offset+=$((len1+4)) + if [[ $tls_serverkeyexchange_ascii_len -lt $((offset+4)) ]]; then + debugme echo "Malformed ServerKeyExchange Handshake message in ServerHello." + tmpfile_handle ${FUNCNAME[0]}.txt + return 1 + fi + len1=2*$(hex2dec "${tls_serverkeyexchange_ascii:offset:4}") + offset+=$((len1+4)) + if [[ $tls_serverkeyexchange_ascii_len -ge $((offset+4)) ]]; then + # The SignatureAndHashAlgorithm won't be present in an anonymous + # key exhange. + peering_signing_digest="${tls_serverkeyexchange_ascii:offset:2}" + peer_signature_type="${tls_serverkeyexchange_ascii:$((offset+2)):2}" + fi + fi fi fi + if [[ 0x$peering_signing_digest -eq 8 ]] && \ + [[ 0x$peer_signature_type -ge 4 ]] && [[ 0x$peer_signature_type -le 11 ]]; then + case $peer_signature_type in + 04) peering_signing_digest="SHA256"; peer_signature_type="RSA-PSS" ;; + 05) peering_signing_digest="SHA384"; peer_signature_type="RSA-PSS" ;; + 06) peering_signing_digest="SHA512"; peer_signature_type="RSA-PSS" ;; + 07) peering_signing_digest=""; peer_signature_type="Ed25519" ;; + 08) peering_signing_digest=""; peer_signature_type="Ed448" ;; + 09) peering_signing_digest="SHA256"; peer_signature_type="RSA-PSS" ;; + 0A) peering_signing_digest="SHA384"; peer_signature_type="RSA-PSS" ;; + 0B) peering_signing_digest="SHA512"; peer_signature_type="RSA-PSS" ;; + esac + if [[ -n "$peering_signing_digest" ]]; then + echo "Peer signing digest: $peering_signing_digest" >> $TMPFILE + [[ $DEBUG -ge 3 ]] && echo -e " Peer signing digest: $peering_signing_digest" + fi + echo "Peer signature type: $peer_signature_type" >> $TMPFILE + [[ $DEBUG -ge 3 ]] && echo -e " Peer signature type: $peer_signature_type\n" + elif [[ 0x$peering_signing_digest -ge 1 ]] && [[ 0x$peering_signing_digest -le 6 ]] && \ + [[ 0x$peer_signature_type -ge 1 ]] && [[ 0x$peer_signature_type -le 3 ]]; then + case $peering_signing_digest in + 01) peering_signing_digest="MD5" ;; + 02) peering_signing_digest="SHA1" ;; + 03) peering_signing_digest="SHA224" ;; + 04) peering_signing_digest="SHA256" ;; + 05) peering_signing_digest="SHA384" ;; + 06) peering_signing_digest="SHA512" ;; + esac + case $peer_signature_type in + 01) peer_signature_type="RSA" ;; + 02) peer_signature_type="DSA" ;; + 03) peer_signature_type="ECDSA" ;; + esac + echo "Peer signing digest: $peering_signing_digest" >> $TMPFILE + [[ $DEBUG -ge 3 ]] && echo -e " Peer signing digest: $peering_signing_digest" + echo "Peer signature type: $peer_signature_type" >> $TMPFILE + [[ $DEBUG -ge 3 ]] && echo -e " Peer signature type: $peer_signature_type\n" + fi tmpfile_handle ${FUNCNAME[0]}.txt TLS_SERVER_HELLO="02$(printf "%06x" $(( tls_serverhello_ascii_len/2)) )${tls_serverhello_ascii}" From 3ae48931fb0aebc9132dd3fc88d142b09a84b7f0 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 14 May 2020 14:55:48 -0400 Subject: [PATCH 102/211] Support EdDSA This commit adds support for EdDSA (Ed25519 and Ed448). In particular: * It modifies prepare_tls_clienthello() to include Ed25519 and Ed448 in the signature_algorithms extension of the TLS 1.2 and earlier ClientHello (RFC 8422). * It modifies run_server_defaults() and get_server_certificate() to check whether the server offers EdDSA certificates with TLS 1.3. * It modifies certificate_info() to handle certificates signed with EdDSA or with EdDSA public keys, even if $OPENSSL does not support pretty printing such keys and signatures. * It modifies read_sigalg_from_file() to recognize EdDSA signatures even if $OPENSSL does not. --- CHANGELOG.md | 1 + testssl.sh | 70 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6641957..7ada38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Don't use external pwd anymore * STARTTLS: XMPP server support * Rating (SSL Labs, not complete) +* Added support for certificates with EdDSA signatures and pubilc keys ### Features implemented / improvements in 3.0 diff --git a/testssl.sh b/testssl.sh index e29bda3..3f410d5 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1040,7 +1040,7 @@ set_key_str_score() { # TODO: We need to get the size of DH params (follows the same table as the "else" clause) # For now, verifying the key size will do... - if [[ $type == EC ]]; then + if [[ $type == EC || $type == EdDSA ]]; then if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then let KEY_EXCH_SCORE=20 set_grade_cap "F" "Using an insecure key" @@ -6249,7 +6249,15 @@ read_dhtype_from_file() { # arg1: certificate file read_sigalg_from_file() { - $OPENSSL x509 -noout -text -in "$1" 2>/dev/null | awk -F':' '/Signature Algorithm/ { print $2; exit; }' + local sig_alg + + sig_alg="$(strip_leading_space "$($OPENSSL x509 -noout -text -in "$1" 2>/dev/null | awk -F':' '/Signature Algorithm/ { print $2; exit; }')")" + case "$sig_alg" in + 1.3.101.112|ED25519) tm_out "Ed25519" ;; + 1.3.101.113|ED448) tm_out "Ed448" ;; + *) tm_out "$sig_alg" ;; + esac + } @@ -7547,7 +7555,7 @@ get_server_certificate() { CERTIFICATE_LIST_ORDERING_PROBLEM=false if [[ "$1" =~ "tls1_3" ]]; then [[ $(has_server_protocol "tls1_3") -eq 1 ]] && return 1 - if "$HAS_TLS13" && "$HAS_SIGALGS"; then + if "$HAS_TLS13" && "$HAS_SIGALGS" && [[ ! "$1" =~ "tls1_3_EdDSA" ]]; then if [[ "$1" =~ "tls1_3_RSA" ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -showcerts -connect $NODEIP:$PORT $PROXY $SNI -tls1_3 -tlsextdebug -status -msg -sigalgs PSS+SHA256:PSS+SHA384") $ERRFILE >$TMPFILE elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then @@ -7568,6 +7576,8 @@ get_server_certificate() { tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,10,00,0e,08,04,08,05,08,06,04,01,05,01,06,01,02,01" elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,0a,00,08,04,03,05,03,06,03,02,03" + elif [[ "$1" =~ "tls1_3_EdDSA" ]]; then + tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,06,00,04,08,07,08,08" else return 1 fi @@ -8332,8 +8342,16 @@ certificate_info() { GOOD_CA_BUNDLE="" cert_sig_algo="$(awk -F':' '/Signature Algorithm/ { print $2; if (++Match >= 1) exit; }' <<< "$cert_txt")" cert_sig_algo="${cert_sig_algo// /}" + case "$cert_sig_algo" in + 1.3.101.112|ED25519) cert_sig_algo="Ed25519" ;; + 1.3.101.113|ED448) cert_sig_algo="Ed448" ;; + esac cert_key_algo="$(awk -F':' '/Public Key Algorithm:/ { print $2; if (++Match >= 1) exit; }' <<< "$cert_txt")" cert_key_algo="${cert_key_algo// /}" + case "$cert_key_algo" in + 1.3.101.112|E[Dd]25519) cert_key_algo="Ed25519"; cert_keysize=253 ;; + 1.3.101.113|E[Dd]448) cert_key_algo="Ed448"; cert_keysize=456 ;; + esac out "$indent" ; pr_bold " Signature Algorithm " jsonID="cert_signatureAlgorithm" @@ -8441,6 +8459,10 @@ certificate_info() { fileout "${jsonID}${json_postfix}" "CRITICAL" "MD5" set_grade_cap "F" "Supports a insecure signature (MD5)" ;; + Ed25519|Ed448) + prln_svrty_good "$cert_sig_algo" + fileout "${jsonID}${json_postfix}" "OK" "$cert_sig_algo" + ;; *) out "$cert_sig_algo (" pr_warning "FIXME: can't tell whether this is good or not" @@ -8461,6 +8483,7 @@ certificate_info() { case $cert_key_algo in *RSA*|*rsa*) short_keyAlgo="RSA";; *ecdsa*|*ecPublicKey) short_keyAlgo="EC";; + *Ed25519*|*Ed448*) short_keyAlgo="EdDSA";; *DSA*|*dsa*) short_keyAlgo="DSA";; *GOST*|*gost*) short_keyAlgo="GOST";; *dh*|*DH*) short_keyAlgo="DH" ;; @@ -8523,6 +8546,10 @@ certificate_info() { ((ret++)) fi + set_key_str_score "$short_keyAlgo" "$cert_keysize" + elif [[ $cert_key_algo == Ed* ]]; then + pr_svrty_good "$cert_key_algo" + json_rating="OK"; json_msg="$short_keyAlgo $cert_key_algo" set_key_str_score "$short_keyAlgo" "$cert_keysize" else out "$cert_key_algo + $cert_keysize bits (" @@ -8586,7 +8613,7 @@ certificate_info() { cert_keyusage="$(strip_leading_space "$(awk '/X509v3 Key Usage:/ { getline; print $0 }' <<< "$cert_txt")")" if [[ -n "$cert_keyusage" ]]; then outln "$cert_keyusage" - if ( [[ " $cert_type " =~ " RSASig " ]] || [[ " $cert_type " =~ " DSA " ]] || [[ " $cert_type " =~ " ECDSA " ]] ) && \ + if ( [[ " $cert_type " =~ " RSASig " ]] || [[ " $cert_type " =~ " DSA " ]] || [[ " $cert_type " =~ " ECDSA " ]] || [[ " $cert_type " =~ " EdDSA " ]] ) && \ [[ ! "$cert_keyusage" =~ "Digital Signature" ]]; then prln_svrty_high "$indent Certificate incorrectly used for digital signatures" fileout "${jsonID}${json_postfix}" "HIGH" "Certificate incorrectly used for digital signatures: \"$cert_keyusage\"" @@ -9257,27 +9284,28 @@ run_server_defaults() { ciphers_to_test[7]="" ciphers_to_test[8]="tls1_3_RSA" ciphers_to_test[9]="tls1_3_ECDSA" + ciphers_to_test[10]="tls1_3_EdDSA" certificate_type[1]="" ; certificate_type[2]="" certificate_type[3]=""; certificate_type[4]="" certificate_type[5]="" ; certificate_type[6]="" certificate_type[7]="" ; certificate_type[8]="RSASig" - certificate_type[9]="ECDSA" + certificate_type[9]="ECDSA" ; certificate_type[10]="EdDSA" - for (( n=1; n <= 16 ; n++ )); do + for (( n=1; n <= 17 ; n++ )); do # Some servers use a different certificate if the ClientHello # specifies TLSv1.1 and doesn't include a server name extension. # So, for each public key type for which a certificate was found, # try again, but only with TLSv1.1 and without SNI. if [[ $n -ne 1 ]] && [[ "$OPTIMAL_PROTO" == -ssl2 ]]; then ciphers_to_test[n]="" - elif [[ $n -ge 10 ]]; then + elif [[ $n -ge 11 ]]; then ciphers_to_test[n]="" - [[ ${success[n-9]} -eq 0 ]] && [[ $(has_server_protocol "tls1_1") -ne 1 ]] && \ - ciphers_to_test[n]="${ciphers_to_test[n-9]}" && certificate_type[n]="${certificate_type[n-9]}" + [[ ${success[n-10]} -eq 0 ]] && [[ $(has_server_protocol "tls1_1") -ne 1 ]] && \ + ciphers_to_test[n]="${ciphers_to_test[n-10]}" && certificate_type[n]="${certificate_type[n-10]}" fi if [[ -n "${ciphers_to_test[n]}" ]]; then - if [[ $n -ge 10 ]]; then + if [[ $n -ge 11 ]]; then sni="$SNI" SNI="" get_server_certificate "${ciphers_to_test[n]}" "tls1_1" @@ -9288,7 +9316,7 @@ run_server_defaults() { success[n]=$? fi if [[ ${success[n]} -eq 0 ]] && [[ -s "$HOSTCERT" ]]; then - [[ $n -ge 10 ]] && [[ ! -e $HOSTCERT.nosni ]] && cp $HOSTCERT $HOSTCERT.nosni + [[ $n -ge 11 ]] && [[ ! -e $HOSTCERT.nosni ]] && cp $HOSTCERT $HOSTCERT.nosni cp "$TEMPDIR/$NODEIP.get_server_certificate.txt" $TMPFILE >$ERRFILE if [[ -z "$sessticket_lifetime_hint" ]]; then @@ -9370,7 +9398,7 @@ run_server_defaults() { fi i=$((i + 1)) done - if ! "$match_found" && [[ $n -ge 10 ]] && [[ $certs_found -ne 0 ]]; then + if ! "$match_found" && [[ $n -ge 11 ]] && [[ $certs_found -ne 0 ]]; then # A new certificate was found using TLSv1.1 without SNI. # Check to see if the new certificate should be displayed. # It should be displayed if it is either a match for the @@ -9427,7 +9455,7 @@ run_server_defaults() { [[ -n "${previous_intermediates[certs_found]}" ]] && [[ -r $TEMPDIR/hostcert_issuer.pem ]] && \ previous_hostcert_issuer[certs_found]=$(cat $TEMPDIR/hostcert_issuer.pem) previous_ordering_problem[certs_found]=$CERTIFICATE_LIST_ORDERING_PROBLEM - [[ $n -ge 10 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI" + [[ $n -ge 11 ]] && sni_used[certs_found]="" || sni_used[certs_found]="$SNI" tls_version[certs_found]="$DETECTED_TLS_VERSION" previous_hostcert_type[certs_found]=" ${certificate_type[n]}" if [[ $DEBUG -ge 1 ]]; then @@ -10738,7 +10766,15 @@ get_pub_key_size() { "$HAS_PKEY" || return 1 # OpenSSL displays the number of bits for RSA and ECC - pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text 2>>$ERRFILE | awk -F'(' '/Public-Key/ { print $2 }') + pubkeybits=$($OPENSSL x509 -noout -pubkey -in $HOSTCERT 2>>$ERRFILE | $OPENSSL pkey -pubin -text 2>>$ERRFILE) + if [[ "$pubkeybits" =~ E[Dd]25519 ]]; then + echo "Server public key is 253 bit" >> $TMPFILE + return 0 + elif [[ "$pubkeybits" =~ E[Dd]448 ]]; then + echo "Server public key is 456 bit" >> $TMPFILE + return 0 + fi + pubkeybits=$(awk -F'(' '/Public-Key/ { print $2 }' <<< "$pubkeybits") if [[ -n $pubkeybits ]]; then # remainder e.g. "256 bit)" pubkeybits="${pubkeybits//\)/}" @@ -14279,10 +14315,10 @@ prepare_tls_clienthello() { if [[ 0x$tls_low_byte -le 0x03 ]]; then extension_signature_algorithms=" - 00, 0d, # Type: signature_algorithms , see RFC 5246 - 00, 20, 00,1e, # lengths + 00, 0d, # Type: signature_algorithms , see RFC 5246 and RFC 8422 + 00, 24, 00,22, # lengths 06,01, 06,02, 06,03, 05,01, 05,02, 05,03, 04,01, 04,02, 04,03, - 03,01, 03,02, 03,03, 02,01, 02,02, 02,03" + 03,01, 03,02, 03,03, 02,01, 02,02, 02,03, 08,07, 08,08" else extension_signature_algorithms=" 00, 0d, # Type: signature_algorithms , see RFC 8446 From 37ffabf8d52a77cbb6da03ade93b53d063392d4a Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 18 May 2020 07:38:49 -0400 Subject: [PATCH 103/211] Remove some unnecessary quotation marks --- testssl.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/testssl.sh b/testssl.sh index 3f410d5..c0ed0b4 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7553,12 +7553,12 @@ get_server_certificate() { "$SSL_NATIVE" && using_sockets=false CERTIFICATE_LIST_ORDERING_PROBLEM=false - if [[ "$1" =~ "tls1_3" ]]; then + if [[ "$1" =~ tls1_3 ]]; then [[ $(has_server_protocol "tls1_3") -eq 1 ]] && return 1 - if "$HAS_TLS13" && "$HAS_SIGALGS" && [[ ! "$1" =~ "tls1_3_EdDSA" ]]; then - if [[ "$1" =~ "tls1_3_RSA" ]]; then + if "$HAS_TLS13" && "$HAS_SIGALGS" && [[ ! "$1" =~ tls1_3_EdDSA ]]; then + if [[ "$1" =~ tls1_3_RSA ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -showcerts -connect $NODEIP:$PORT $PROXY $SNI -tls1_3 -tlsextdebug -status -msg -sigalgs PSS+SHA256:PSS+SHA384") $ERRFILE >$TMPFILE - elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then + elif [[ "$1" =~ tls1_3_ECDSA ]]; then $OPENSSL s_client $(s_client_options "$STARTTLS $BUGS -showcerts -connect $NODEIP:$PORT $PROXY $SNI -tls1_3 -tlsextdebug -status -msg -sigalgs ECDSA+SHA256:ECDSA+SHA384") $ERRFILE >$TMPFILE else return 1 @@ -7572,11 +7572,11 @@ get_server_certificate() { # For STARTTLS protcols not being implemented yet via sockets this is a bypass otherwise it won't be usable at all (e.g. LDAP) if ( [[ "$STARTTLS" =~ ldap ]] || [[ "$STARTTLS" =~ irc ]] ); then return 1 - elif [[ "$1" =~ "tls1_3_RSA" ]]; then + elif [[ "$1" =~ tls1_3_RSA ]]; then tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,10,00,0e,08,04,08,05,08,06,04,01,05,01,06,01,02,01" - elif [[ "$1" =~ "tls1_3_ECDSA" ]]; then + elif [[ "$1" =~ tls1_3_ECDSA ]]; then tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,0a,00,08,04,03,05,03,06,03,02,03" - elif [[ "$1" =~ "tls1_3_EdDSA" ]]; then + elif [[ "$1" =~ tls1_3_EdDSA ]]; then tls_sockets "04" "$TLS13_CIPHER" "all" "00,12,00,00, 00,05,00,05,01,00,00,00,00, 00,0d,00,06,00,04,08,07,08,08" else return 1 @@ -8613,19 +8613,19 @@ certificate_info() { cert_keyusage="$(strip_leading_space "$(awk '/X509v3 Key Usage:/ { getline; print $0 }' <<< "$cert_txt")")" if [[ -n "$cert_keyusage" ]]; then outln "$cert_keyusage" - if ( [[ " $cert_type " =~ " RSASig " ]] || [[ " $cert_type " =~ " DSA " ]] || [[ " $cert_type " =~ " ECDSA " ]] || [[ " $cert_type " =~ " EdDSA " ]] ) && \ - [[ ! "$cert_keyusage" =~ "Digital Signature" ]]; then + if ( [[ " $cert_type " =~ \ RSASig\ ]] || [[ " $cert_type " =~ \ DSA\ ]] || [[ " $cert_type " =~ \ ECDSA\ ]] || [[ " $cert_type " =~ \ EdDSA\ ]] ) && \ + [[ ! "$cert_keyusage" =~ Digital\ Signature ]]; then prln_svrty_high "$indent Certificate incorrectly used for digital signatures" fileout "${jsonID}${json_postfix}" "HIGH" "Certificate incorrectly used for digital signatures: \"$cert_keyusage\"" outok=false fi - if [[ " $cert_type " =~ " RSAKMK " ]] && [[ ! "$cert_keyusage" =~ "Key Encipherment" ]]; then + if [[ " $cert_type " =~ \ RSAKMK\ ]] && [[ ! "$cert_keyusage" =~ Key\ Encipherment ]]; then prln_svrty_high "$indent Certificate incorrectly used for key encipherment" fileout "${jsonID}${json_postfix}" "HIGH" "Certificate incorrectly used for key encipherment: \"$cert_keyusage\"" outok=false fi - if ( [[ " $cert_type " =~ " DH " ]] || [[ " $cert_type " =~ " ECDH " ]] ) && \ - [[ ! "$cert_keyusage" =~ "Key Agreement" ]]; then + if ( [[ " $cert_type " =~ \ DH\ ]] || [[ " $cert_type " =~ \ ECDH\ ]] ) && \ + [[ ! "$cert_keyusage" =~ Key\ Agreement ]]; then prln_svrty_high "$indent Certificate incorrectly used for key agreement" fileout "${jsonID}${json_postfix}" "HIGH" "Certificate incorrectly used for key agreement: \"$cert_keyusage\"" outok=false From a4ae05c90c70063cd2eb9d4a3e25b60a4ec764b5 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 25 May 2020 13:23:49 +0200 Subject: [PATCH 104/211] Add get_txt_record(), fix variable declaration in get_mx_record() This commit adds a function for querying the TXT DNS record, so that subsequently we'll can build on top of that a function for checking MTA-STS, see #1073. Also it modifies a local variable mxs in get_mx_record() which was declared as mx but mxs was used. (That is pending an backport to 3.0.) --- testssl.sh | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index c0ed0b4..2b34e85 100755 --- a/testssl.sh +++ b/testssl.sh @@ -19557,7 +19557,7 @@ get_caa_rr_record() { return 0 } -# watch out: $1 can also be a cname! --> all checked +# arg1: domain to check for. Returned will be the MX record as a string get_mx_record() { local mx="" local saved_openssl_conf="$OPENSSL_CONF" @@ -19567,21 +19567,49 @@ get_mx_record() { OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134 # we need the last two columns here if "$HAS_HOST"; then - mxs="$(host -t MX "$1" 2>/dev/null | awk '/is handled by/ { print $(NF-1), $NF }')" + mx="$(host -t MX "$1" 2>/dev/null | awk '/is handled by/ { print $(NF-1), $NF }')" elif "$HAS_DIG"; then - mxs="$(dig +short $noidnout -t MX "$1" 2>/dev/null | awk '/^[0-9]/ { print $1" "$2 }')" + mx="$(dig +short $noidnout -t MX "$1" 2>/dev/null | awk '/^[0-9]/ { print $1" "$2 }')" elif "$HAS_DRILL"; then - mxs="$(drill mx $1 | awk '/IN[ \t]MX[ \t]+/ { print $(NF-1), $NF }')" + mx="$(drill mx $1 | awk '/IN[ \t]MX[ \t]+/ { print $(NF-1), $NF }')" elif "$HAS_NSLOOKUP"; then - mxs="$(strip_lf "$(nslookup -type=MX "$1" 2>/dev/null | awk '/mail exchanger/ { print $(NF-1), $NF }')")" + mx="$(strip_lf "$(nslookup -type=MX "$1" 2>/dev/null | awk '/mail exchanger/ { print $(NF-1), $NF }')")" else # shouldn't reach this, as we checked in the top fatal "No dig, host, drill or nslookup" $ERR_DNSBIN fi OPENSSL_CONF="$saved_openssl_conf" - echo "$mxs" + echo "$mx" } +# arg1: domain / hostname. Returned will be the TXT record as a string which can be multilined +# (one entry per line), for e.g. non-MTA-STS records. +# Is supposed to be used by MTA STS in the future like get_txt_record _mta-sts.DOMAIN.TLD +get_txt_record() { + local record="" + local saved_openssl_conf="$OPENSSL_CONF" + local noidnout="" + + "$HAS_DIG_NOIDNOUT" && noidnout="+noidnout" + OPENSSL_CONF="" # see https://github.com/drwetter/testssl.sh/issues/134 + # we need the last two columns here and strip any remaining double quotes later + if "$HAS_HOST"; then + record="$(host -t TXT "$1" 2>/dev/null | awk -F\" '/descriptive text/ { print $(NF-1) }')" + elif "$HAS_DIG"; then + record="$(dig +short $noidnout -t TXT "$1" 2>/dev/null)" + elif "$HAS_DRILL"; then + record="$(drill txt $1 | awk -F\" '/^[a-z0-9].*TXT/ { print $(NF-1) }')" + elif "$HAS_NSLOOKUP"; then + record="$(strip_lf "$(nslookup -type=MX "$1" 2>/dev/null | awk -F= '/text/ { print $(NF-1), $NF }')")" + else + # shouldn't reach this, as we checked in the top + fatal "No dig, host, drill or nslookup" $ERR_DNSBIN + fi + OPENSSL_CONF="$saved_openssl_conf" + echo "${record//\"/}" +} + + # set IPADDRs and IP46ADDRs # From e6150a2348141687eb3aea46c94c3b03ba9686f3 Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Thu, 28 May 2020 20:33:17 +0200 Subject: [PATCH 105/211] Missed todo comment fix; cleanup output --- testssl.sh | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/testssl.sh b/testssl.sh index ff6c232..28ec6fb 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1035,36 +1035,29 @@ set_grade_warning() { set_key_str_score() { local type=$1 local size=$2 + local type_output "$do_rating" || return 0 -<<<<<<< HEAD - if [[ $type == EC ]]; then - if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then -======= - # TODO: We need to get the size of DH params (follows the same table as the "else" clause) - # For now, verifying the key size will do... + [[ $type == DHE ]] && type_output="DH key exchange parameters" || type_output="key" + if [[ $type == EC || $type == EdDSA ]]; then - if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then - let KEY_EXCH_SCORE=20 - set_grade_cap "F" "Using an insecure key" - elif [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then ->>>>>>> upstream/3.1dev + if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using an insecure key" + set_grade_cap "F" "Using an insecure $type_output" elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then let KEY_EXCH_SCORE=80 - set_grade_cap "B" "Using a weak key" + set_grade_cap "B" "Using a weak $type_output" elif [[ $size -lt 225 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then let KEY_EXCH_SCORE=90 fi else if [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using an insecure key / DH key exchange parameters" + set_grade_cap "F" "Using an insecure $type_output" elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then let KEY_EXCH_SCORE=80 - set_grade_cap "B" "Using a weak key / DH key exchange parameters" + set_grade_cap "B" "Using a weak $type_output" elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then let KEY_EXCH_SCORE=90 fi @@ -8520,7 +8513,7 @@ certificate_info() { fi out " bits" - set_key_str_score "$short_keyAlgo" "$cert_keysize" # TODO: should be $dh_param_size + set_key_str_score "$short_keyAlgo" "$cert_keysize" elif [[ $cert_key_algo =~ RSA ]] || [[ $cert_key_algo =~ rsa ]] || [[ $cert_key_algo =~ dsa ]] || \ [[ $cert_key_algo =~ dhKeyAgreement ]] || [[ $cert_key_algo == X9.42\ DH ]]; then if [[ "$cert_keysize" -le 512 ]]; then From dca50fc49ab8aaafd8973a41f71a477f0baa1b4d Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Thu, 28 May 2020 21:00:45 +0200 Subject: [PATCH 106/211] allow multiple equal key sizes --- testssl.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/testssl.sh b/testssl.sh index 28ec6fb..94b6f9f 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1039,26 +1039,26 @@ set_key_str_score() { "$do_rating" || return 0 - [[ $type == DHE ]] && type_output="DH key exchange parameters" || type_output="key" + [[ $type == DHE ]] && type_output="temporal DH key (DH parameters)" || type_output="key" if [[ $type == EC || $type == EdDSA ]]; then - if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then + if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then let KEY_EXCH_SCORE=40 set_grade_cap "F" "Using an insecure $type_output" - elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then + elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -ge 80 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak $type_output" - elif [[ $size -lt 225 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then + elif [[ $size -lt 225 ]] && [[ $KEY_EXCH_SCORE -ge 90 ]]; then let KEY_EXCH_SCORE=90 fi else - if [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -gt 40 ]]; then + if [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then let KEY_EXCH_SCORE=40 set_grade_cap "F" "Using an insecure $type_output" - elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -gt 80 ]]; then + elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -ge 80 ]]; then let KEY_EXCH_SCORE=80 set_grade_cap "B" "Using a weak $type_output" - elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -gt 90 ]]; then + elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -ge 90 ]]; then let KEY_EXCH_SCORE=90 fi fi From 30d571076808eaa2244a701aab7a680e7815b311 Mon Sep 17 00:00:00 2001 From: Magnus Larsen <[]> Date: Thu, 28 May 2020 21:12:14 +0200 Subject: [PATCH 107/211] ephemeral is the word --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 94b6f9f..02bb7e5 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1039,7 +1039,7 @@ set_key_str_score() { "$do_rating" || return 0 - [[ $type == DHE ]] && type_output="temporal DH key (DH parameters)" || type_output="key" + [[ $type == DHE ]] && type_output="ephemeral DH key (DH parameters)" || type_output="key" if [[ $type == EC || $type == EdDSA ]]; then if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then From edefce5998181cbbfe073b7238ceaeedc087dc75 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 1 Jun 2020 15:31:01 -0400 Subject: [PATCH 108/211] Fix Shellcheck SC2034 issues This commit fixes several issues related to Shellcheck issue SC2034: unused variables. In most cases variables are declared in a function, but are referenced later. The exceptions are: * SESS_RESUMPTION is declared and values are assigned to it, but it us never used. (Same applies for not_new_reused in sub_seession_resumption().) * In run_cipherlists(), there is a typo in the declaration of sslv2_tdes_ciphers. * In get_caa_rr_record(), "hash", "len", and "line" are used but not declared. --- testssl.sh | 51 +++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/testssl.sh b/testssl.sh index 2b34e85..ca5ae4a 100755 --- a/testssl.sh +++ b/testssl.sh @@ -266,7 +266,6 @@ V2_HELLO_CIPHERSPEC_LENGTH=0 declare -r NPN_PROTOs="spdy/4a2,spdy/3,spdy/3.1,spdy/2,spdy/1,http/1.1" # alpn_protos needs to be space-separated, not comma-seperated, including odd ones observed @ facebook and others, old ones like h2-17 omitted as they could not be found declare -r ALPN_PROTOs="h2 spdy/3.1 http/1.1 grpc-exp h2-fb spdy/1 spdy/2 spdy/3 stun.turn stun.nat-discovery webrtc c-webrtc ftp" -declare -a SESS_RESUMPTION TEMPDIR="" TMPFILE="" ERRFILE="" @@ -1687,7 +1686,7 @@ http_get() { # There the environment variable is used automatically # Currently it is being used by check_pwnedkeys() only. http_get_header() { - local proto z + local proto local node="" query="" local dl="$2" local useragent="$UA_STD" @@ -5932,7 +5931,7 @@ run_cipherlists() { local ossl_anon_ciphers anon_ciphers sslv2_anon_ciphers local ossl_exp_ciphers exp_ciphers sslv2_exp_ciphers local ossl_low_ciphers low_ciphers sslv2_low_ciphers - local ossl_tdes_ciphers tdes_ciphers sslv2_tdes_cipher + local ossl_tdes_ciphers tdes_ciphers sslv2_tdes_ciphers local ossl_obsoleted_ciphers obsoleted_ciphers local strong_ciphers local cwe="CWE-327" @@ -6341,7 +6340,6 @@ sub_session_resumption() { local tmpfile=$(mktemp $TEMPDIR/session_resumption.$NODEIP.XXXXXX) local sess_data=$(mktemp $TEMPDIR/sub_session_data_resumption.$NODEIP.XXXXXX) local -a rw_line - local not_new_reused=false local protocol="$1" if [[ "$2" == ID ]]; then @@ -6401,7 +6399,6 @@ sub_session_resumption() { new_sid=true else debugme echo -n "Problem with 2nd ServerHello " - not_new_reused=true fi # Now get the line and compare the numbers "read" and "written" as a second criteria. # If the "read" number is bigger: a new session ID was probably used @@ -6823,7 +6820,6 @@ cipher_pref_check() { local using_sockets="$4" local wide="$5" # at the moment always = true local tested_cipher cipher order rfc_cipher rfc_order - local overflow_probe_cipherlist="ALL:-ECDHE-RSA-AES256-GCM-SHA384:-AES128-SHA:-DES-CBC3-SHA" local -i i nr_ciphers nr_nonossl_ciphers num_bundles bundle_size bundle end_of_bundle success local -i nr_ciphers_found local hexc ciphers_to_test @@ -8198,7 +8194,6 @@ certificate_transparency() { local tls_version="$6" local sni="" local ciphers="" - local hexc n ciph sslver kx auth enc mac export local extra_extns="" local -i success # Cipher suites that use a certifiate with an RSA (signature) public key @@ -9562,25 +9557,20 @@ run_server_defaults() { jsonID="sessionresumption_ticket" sub_session_resumption "$sessticket_proto" case $? in - 0) SESS_RESUMPTION[2]="ticket=yes" - out "Tickets: yes, " + 0) out "Tickets: yes, " fileout "$jsonID" "INFO" "supported" ;; - 1) SESS_RESUMPTION[2]="ticket=no" - out "Tickets no, " + 1) out "Tickets no, " fileout "$jsonID" "INFO" "not supported" ;; - 5) SESS_RESUMPTION[2]="ticket=noclue" - pr_warning "Ticket resumption test failed, pls report / " + 5) pr_warning "Ticket resumption test failed, pls report / " fileout "$jsonID" "WARN" "check failed, pls report" ((ret++)) ;; - 6) SESS_RESUMPTION[2]="ticket=clientauth" - pr_warning "Client Auth: Ticket resumption test not supported / " + 6) pr_warning "Client Auth: Ticket resumption test not supported / " fileout "$jsonID" "WARN" "check couldn't be performed because of client authentication" ;; - 7) SESS_RESUMPTION[2]="ticket=unsuccessful" - pr_warning "Connect problem: Ticket resumption test not possible / " + 7) pr_warning "Connect problem: Ticket resumption test not possible / " fileout "$jsonID" "WARN" "check failed because of connect problem" ((ret++)) ;; @@ -9588,32 +9578,25 @@ run_server_defaults() { jsonID="sessionresumption_ID" if "$NO_SSL_SESSIONID"; then - SESS_RESUMPTION[1]="ID=no" outln "ID: no" fileout "$jsonID" "INFO" "No Session ID, no resumption" else sub_session_resumption "$sessticket_proto" ID case $? in - 0) SESS_RESUMPTION[1]="ID=yes" - outln "ID: yes" + 0) outln "ID: yes" fileout "$jsonID" "INFO" "supported" ;; - 1|2) SESS_RESUMPTION[1]="ID=no" - outln "ID: no" + 1|2) outln "ID: no" fileout "$jsonID" "INFO" "not supported" ;; - 5) SESS_RESUMPTION[1]="ID=noclue" - prln_warning "ID resumption test failed, pls report" + 5) prln_warning "ID resumption test failed, pls report" fileout "$jsonID" "WARN" "check failed, pls report" ((ret++)) ;; - 6) SESS_RESUMPTION[1]="ID=clientauth" - # [[ ${SESS_RESUMPTION[2]} =~ clientauth ]] || pr_warning "Client Auth: " - prln_warning "Client Auth: ID resumption test not supported" + 6) prln_warning "Client Auth: ID resumption test not supported" fileout "$jsonID" "WARN" "check couldn't be performed because of client authentication" ;; - 7) SESS_RESUMPTION[1]="ID=unsuccessful" - prln_warning "ID resumption test failed" + 7) prln_warning "ID resumption test failed" fileout "$jsonID" "WARN" "check failed because of connect problem" ((ret++)) ;; @@ -10565,7 +10548,6 @@ starttls_mysql_dialog() { # returns 6 if opening the socket caused a problem, 1 if STARTTLS handshake failed, 0: all ok # fd_socket() { - local jabber="" local proyxline="" local nodeip="$(tr -d '[]' <<< $NODEIP)" # sockets do not need the square brackets we have of IPv6 addresses # we just need do it here, that's all! @@ -14238,7 +14220,7 @@ prepare_tls_clienthello() { local len_client_hello_word len_all_word local ecc_cipher_suite_found=false local extension_signature_algorithms extension_heartbeat session_id - local extension_session_ticket extension_next_protocol extension_padding + local extension_session_ticket extension_next_protocol local extension_supported_groups="" extension_supported_point_formats="" local extensions_key_share="" extn_type supported_groups_c2n="" extn_psk_mode="" local extra_extensions extra_extensions_list="" extension_supported_versions="" @@ -14656,11 +14638,11 @@ prepare_tls_clienthello() { resend_if_hello_retry_request() { local original_clienthello="$1" local tls_hello_ascii="$2" - local msg_type tls_low_byte server_version cipher_suite rfc_cipher_suite + local msg_type server_version cipher_suite rfc_cipher_suite local key_share="" new_key_share="" cookie="" second_clienthello data="" local -i i j msg_len tls_hello_ascii_len sid_len - local -i extns_offset hrr_extns_len extra_extensions_len len_extn - local extra_extensions extn_type part2 new_extra_extns="" + local -i extns_offset hrr_extns_len len_extn + local extn_type local sha256_hrr="CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C" tls_hello_ascii_len=${#tls_hello_ascii} @@ -19480,6 +19462,7 @@ get_aaaa_record() { # arg1: domain to check for get_caa_rr_record() { local raw_caa="" + local hash len line local -i len_caa_property local caa_property_name local caa_property_value From cce7566dc8eac7ba459f3609feb5cad96cb27ce2 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Tue, 2 Jun 2020 16:26:55 +0200 Subject: [PATCH 109/211] Moved grade_caps to run_rating() function; added KEY_EXCH_SCORE=20 back again --- testssl.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/testssl.sh b/testssl.sh index 02bb7e5..37c329b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1039,25 +1039,23 @@ set_key_str_score() { "$do_rating" || return 0 - [[ $type == DHE ]] && type_output="ephemeral DH key (DH parameters)" || type_output="key" - if [[ $type == EC || $type == EdDSA ]]; then - if [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then + if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then + let KEY_EXCH_SCORE=20 + elif [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using an insecure $type_output" elif [[ $size -lt 163 ]] && [[ $KEY_EXCH_SCORE -ge 80 ]]; then let KEY_EXCH_SCORE=80 - set_grade_cap "B" "Using a weak $type_output" elif [[ $size -lt 225 ]] && [[ $KEY_EXCH_SCORE -ge 90 ]]; then let KEY_EXCH_SCORE=90 fi else - if [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then + if [[ $size -lt 512 ]] && [[ $KEY_EXCH_SCORE -ge 20 ]]; then + let KEY_EXCH_SCORE=20 + elif [[ $size -lt 1024 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then let KEY_EXCH_SCORE=40 - set_grade_cap "F" "Using an insecure $type_output" elif [[ $size -lt 2048 ]] && [[ $KEY_EXCH_SCORE -ge 80 ]]; then let KEY_EXCH_SCORE=80 - set_grade_cap "B" "Using a weak $type_output" elif [[ $size -lt 4096 ]] && [[ $KEY_EXCH_SCORE -ge 90 ]]; then let KEY_EXCH_SCORE=90 fi @@ -20880,6 +20878,12 @@ run_rating() { pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)" ## Category 2 + if [[ $KEY_EXCH_SCORE -le 40 ]]; then + set_grade_cap "F" "Using an insecure public key and/or ephemeral key" + elif [[ $KEY_EXCH_SCORE -le 80 ]]; then + set_grade_cap "B" "Using a weak public key and/or ephemeral key" + fi + let c2_score=$KEY_EXCH_SCORE let c2_wscore=$c2_score*30/100 From 55bbb98a024b818ac2a3a38f055b1078825947b9 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Tue, 2 Jun 2020 16:28:24 +0200 Subject: [PATCH 110/211] small fixes --- testssl.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index 37c329b..a4a401f 100755 --- a/testssl.sh +++ b/testssl.sh @@ -1035,12 +1035,11 @@ set_grade_warning() { set_key_str_score() { local type=$1 local size=$2 - local type_output "$do_rating" || return 0 if [[ $type == EC || $type == EdDSA ]]; then - if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -gt 20 ]]; then + if [[ $size -lt 110 ]] && [[ $KEY_EXCH_SCORE -ge 20 ]]; then let KEY_EXCH_SCORE=20 elif [[ $size -lt 123 ]] && [[ $KEY_EXCH_SCORE -ge 40 ]]; then let KEY_EXCH_SCORE=40 @@ -1064,7 +1063,7 @@ set_key_str_score() { } # Sets the best and worst bit size key, used to grade Category 3 (Cipher Strength) -# This function itself doesn't actually set a score; its just in the name to keep it logical (score == grading function) +# This function itself doesn't actually set a score; its just in the name to keep it logical (score == rating function) # arg1: a bit size set_ciph_str_score() { local size=$1 From fe87192a80ac356e4affa39ba83ccc16db0b576d Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 8 Jun 2020 13:54:36 -0400 Subject: [PATCH 111/211] Fix missing spaces In some cases when the Trust finding is printed, there is no space between the results when SNI is used and the results without SNI (which appear in paraenthesis). This commit adds the missing space. --- testssl.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/testssl.sh b/testssl.sh index bd53b08..0a3056d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8862,24 +8862,24 @@ certificate_info() { fi elif [[ $trust_nosni -eq 4 ]] || [[ $trust_nosni -eq 8 ]] || [[ $trust_sni -eq 4 ]] || [[ $trust_sni -eq 8 ]]; then case $trust_nosni in - 1) trustfinding_nosni="(w/o SNI: Ok via SAN)" ;; - 2) trustfinding_nosni="(w/o SNI: Ok via SAN wildcard)" ;; + 1) trustfinding_nosni=" (w/o SNI: Ok via SAN)" ;; + 2) trustfinding_nosni=" (w/o SNI: Ok via SAN wildcard)" ;; 4) if "$has_dns_sans_nosni"; then - trustfinding_nosni="(w/o SNI: via CN, but not SAN)" + trustfinding_nosni=" (w/o SNI: via CN, but not SAN)" else - trustfinding_nosni="(w/o SNI: via CN only)" + trustfinding_nosni=" (w/o SNI: via CN only)" fi ;; - 5) trustfinding_nosni="(w/o SNI: Ok via SAN and CN)" ;; - 6) trustfinding_nosni="(w/o SNI: Ok via SAN wildcard and CN)" ;; + 5) trustfinding_nosni=" (w/o SNI: Ok via SAN and CN)" ;; + 6) trustfinding_nosni=" (w/o SNI: Ok via SAN wildcard and CN)" ;; 8) if "$has_dns_sans_nosni"; then - trustfinding_nosni="(w/o SNI: via CN wildcard, but not SAN)" + trustfinding_nosni=" (w/o SNI: via CN wildcard, but not SAN)" else - trustfinding_nosni="(w/o SNI: via CN (wildcard) only)" + trustfinding_nosni=" (w/o SNI: via CN (wildcard) only)" fi ;; - 9) trustfinding_nosni="(w/o SNI: Ok via CN wildcard and SAN)" ;; - 10) trustfinding_nosni="(w/o SNI: Ok via SAN wildcard and CN wildcard)" ;; + 9) trustfinding_nosni=" (w/o SNI: Ok via CN wildcard and SAN)" ;; + 10) trustfinding_nosni=" (w/o SNI: Ok via SAN wildcard and CN wildcard)" ;; esac elif [[ $trust_sni -ne 0 ]]; then trustfinding_nosni=" (works w/o SNI)" From a6c2168cd9d3ea166761cc4265aefa9c661cd497 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 8 Jun 2020 13:57:00 -0400 Subject: [PATCH 112/211] Fix and enhance CN matching PR #1373 changed get_cn_from_cert() to handle certificate subject names that include more than one CN attribute. It did this by converting newline characters to spaces. It seems that this resulted in a space character being added to the end of the string returned by get_cn_from_cert() even in the case that the subject name only included one CN attribute. The presence of the space character in returned value caused compare_server_name_to_cert() to determine that the CN attribute did not contain a DNS name (since DNS names cannot include spaces), and so compare_server_name_to_cert() reports that the server name does not match against the CN in the subject. This may be the reason for the problem noted in #1555. This commit fixes the above problem and also enhances the matching of the CN in the subject name against the server's name. Currently, compare_server_name_to_cert() assumes that the subject field contains at most one CN attribute. However, as noted in #1373, some certificates include subject names with more than one CN attribute, and RFC 6125 (Section 6.2.2) indicates that the certificate subject name include more than one CN, with each specifying a different DNS name. So, in addition to fixing the problem with the space character, this commit also enhances the CN matching to work even if the certificate includes more than one CN attribute in the subject name. --- testssl.sh | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/testssl.sh b/testssl.sh index bd53b08..584955c 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7804,9 +7804,9 @@ wildcard_match() compare_server_name_to_cert() { local cert="$1" - local servername cn dns_sans ip_sans san dercert tag + local servername cns cn dns_sans ip_sans san dercert tag local srv_id="" xmppaddr="" - local -i i len len1 + local -i i len len1 cn_match=0 local -i subret=0 # no error condition, passing results HAS_DNS_SANS=false @@ -7960,19 +7960,23 @@ compare_server_name_to_cert() { done <<< "$dns_sans" fi - cn="$(get_cn_from_cert "$cert")" + # Get every CN from the subject field and compare against the server name. + cns="$($OPENSSL x509 -in $1 -noout -subject -nameopt multiline,-align,sname,-esc_msb,utf8,-space_eq 2>>$ERRFILE | awk -F'=' '/CN=/ { print $2 }')" + while read cn; do + # If the CN contains any characters that are not valid for a DNS name, + # then assume it does not contain a DNS name. + [[ -n $(sed 's/^[_\.a-zA-Z0-9*\-]*//' <<< "$cn") ]] && continue - # If the CN contains any characters that are not valid for a DNS name, - # then assume it does not contain a DNS name. - [[ -n $(sed 's/^[_\.a-zA-Z0-9*\-]*//' <<< "$cn") ]] && return $subret + # Check whether the CN matches the servername + [[ $(toupper "$cn") == "$servername" ]] && cn_match=4 && break - # Check whether the CN in the certificate matches the servername - [[ $(toupper "$cn") == "$servername" ]] && subret+=4 && return $subret - - # Check whether the CN in the certificate is a wildcard name that matches - # the servername - wildcard_match "$servername" "$cn" - [[ $? -eq 0 ]] && subret+=8 + # Check whether the CN is a wildcard name that matches the servername + # NOTE: Don't stop loop on a wildcard match in case there is another CN + # that is an exact match. + wildcard_match "$servername" "$cn" + [[ $? -eq 0 ]] && cn_match=8 + done <<< "$cns" + subret+=$cn_match return $subret } From 21208f46cd683966f50ce89861d7096441f93b31 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Thu, 18 Jun 2020 21:15:28 +0200 Subject: [PATCH 113/211] Clearer grade cap reason regarding certificate errors --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index a4a401f..5bcd5a1 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7204,7 +7204,7 @@ determine_trust() { out "$code" fi fileout "${jsonID}${json_postfix}" "CRITICAL" "failed $code. $addtl_warning" - set_grade_cap "T" "Issues with certificate $code" + set_grade_cap "T" "Issues with the chain of trust $code" else # is one ok and the others not ==> display the culprit store if "$some_ok"; then @@ -7223,7 +7223,7 @@ determine_trust() { out "$code" fi notok_was="${certificate_file[i]} $code $notok_was" - set_grade_cap "T" "Issues with certificate $code" + set_grade_cap "T" "Issues with chain of trust $code" fi done #pr_svrty_high "$notok_was " From de14ec9f8111fa905467b965b7026e8817ffabb3 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Fri, 19 Jun 2020 21:21:43 +0200 Subject: [PATCH 114/211] STARTTLS rating styling --- testssl.sh | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/testssl.sh b/testssl.sh index 926e282..9c64829 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20802,14 +20802,7 @@ run_rating() { pr_headlineln " Rating (experimental) " outln - if [[ -n "$STARTTLS_PROTOCOL" ]]; then - pr_bold " Grade "; pr_svrty_critical "T" - outln " - STARTTLS encryption is opportunistic" - outln " (Further details would lead to a false sense of security)" - fileout "grade" "CRITICAL" "T" - fileout "grade_cap_reasons" "INFO" "No more details shown as it would lead to a false sense of security" - return 0 - fi + [[ -n "$STARTTLS_PROTOCOL" ]] && set_grade_warning "STARTTLS encryption is opportunistic. The grade is very insignificant" # Sort the reasons. This is just nicer to read in genereal IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) @@ -20912,7 +20905,7 @@ run_rating() { pr_bold " Final Score "; outln $final_score - # get score, and somehow do something about the GRADE_CAP + # Calculate the pre-cap grade if [[ $final_score -ge 80 ]]; then pre_cap_grade="A" elif [[ $final_score -ge 65 ]]; then From 2bff63b7db8a289acca2d88e1669ed3e85b9ef17 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Mon, 22 Jun 2020 19:14:25 +0200 Subject: [PATCH 115/211] Add a comment about STARTTLS connections in the docs --- doc/testssl.1.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 5198def..227ffbd 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -407,6 +407,10 @@ As of writing, these checks are missing: * Exportable key exchange - should give **40** points in `set_key_str_score()` * Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()` +#### STARTTLS +This program rates STARTTLS connections, exactly according to the specification. However, this program adds a grade warning about STARTTLS is being used. This is not apart of the rating specification, and limits the grade a STARTTLS connection can have, to a maximum of `A-`. + + #### Implementing new grades caps or -warnings To implement a new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: ```bash From 069c5ae917709f2fd1540cc4ce6bd4334e0db47b Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Mon, 22 Jun 2020 19:16:20 +0200 Subject: [PATCH 116/211] Spelling --- doc/testssl.1.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 227ffbd..c15f3ee 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -410,7 +410,6 @@ As of writing, these checks are missing: #### STARTTLS This program rates STARTTLS connections, exactly according to the specification. However, this program adds a grade warning about STARTTLS is being used. This is not apart of the rating specification, and limits the grade a STARTTLS connection can have, to a maximum of `A-`. - #### Implementing new grades caps or -warnings To implement a new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: ```bash @@ -423,14 +422,14 @@ set_grade_warning "Documentation is always right" #### Implementing a new check which contains grade caps When implementing a new check (be it vulnerability or not) that sets grade caps, the `set_rating_state()` has to be updated (i.e. the `$do_mycheck` variable-name has to be added to the loop, and `$nr_enabled` if-statement has to be incremented) -The `set_rating_state()` automatically disables ratinng, if all the required checks are *not* enabled. +The `set_rating_state()` automatically disables rating, if all the required checks are *not* enabled. This is to prevent giving out a misleading or wrong grade. #### Implementing a new revision When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1. Added to the script wherever relevant, or - 2. Added to the above list of missing checks (if *i.* is not possible) + 2. Added to the above list of missing checks (if above is not possible) * New grade warnings has to be added wherever relevant * The revision output in `run_rating()` function has to updated From 82e939f2bdd1ea977224ecd742ce514578a6fbd7 Mon Sep 17 00:00:00 2001 From: Christoph Settgast Date: Tue, 23 Jun 2020 15:26:31 +0200 Subject: [PATCH 117/211] Add wiresharked Android 7.0 (native) After being bitten by https://stackoverflow.com/questions/39133437/sslhandshakeexception-handshake-failed-on-android-n-7-0 I add a wiresharked Android 7.0 to reflect that bug in Android 7.0. --- etc/client-simulation.txt | 9 +++++---- etc/client-simulation.wiresharked.txt | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/etc/client-simulation.txt b/etc/client-simulation.txt index ed2c3bc..b81a934 100644 --- a/etc/client-simulation.txt +++ b/etc/client-simulation.txt @@ -174,24 +174,25 @@ requiresSha2+=(false) current+=(true) - names+=("Android 7.0") + names+=("Android 7.0 (native)") short+=("android_70") - ch_ciphers+=("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305-OLD:ECDHE-RSA-CHACHA20-POLY1305-OLD:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:DES-CBC3-SHA") + ch_ciphers+=("ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA") ciphersuites+=("") ch_sni+=("$SNI") warning+=("") - handshakebytes+=("16030100e4010000e0030366285fd01ec41e6b9c032a373d4607a6349c509d8a1b142cecc6820364d6eab42024c69f1c56165106d550c4c72135be8c3fe21f72843d19e663602d6476babc090022cca9cca8cc14cc13c02bc02fc02cc030c009c013c00ac014009c009d002f0035000a01000075ff0100010000000014001200000f6465762e73736c6c6162732e636f6d0017000000230000000d00120010060106030501050304010403020102030005000501000000000012000000100017001502683208737064792f332e3108687474702f312e31000b00020100000a00080006001d00170018") + handshakebytes+=("160301009d0100009903036cea0f867ae9fdd087adedaa810119e62971b36c0486d44fb3099e51403c8a1e000018c02bc02ccca9c02fc030cca8c013c014009c009d002f003501000058ff010001000000000d000b00000873796e6f642e696d0017000000230000000d0016001406010603050105030401040303010303020102030010000e000c02683208687474702f312e31000b00020100000a000400020017") protos+=("-no_ssl3 -no_ssl2") tlsvers+=("-tls1_2 -tls1_1 -tls1") lowest_protocol+=("0x0301") highest_protocol+=("0x0303") + alpn+=("h2,http/1.1") service+=("HTTP,FTP") minDhBits+=(-1) maxDhBits+=(-1) minRsaBits+=(-1) maxRsaBits+=(-1) minEcdsaBits+=(-1) - curves+=("X25519:prime256v1:secp384r1") + curves+=("prime256v1") requiresSha2+=(false) current+=(true) diff --git a/etc/client-simulation.wiresharked.txt b/etc/client-simulation.wiresharked.txt index e399707..255c724 100644 --- a/etc/client-simulation.wiresharked.txt +++ b/etc/client-simulation.wiresharked.txt @@ -5,6 +5,28 @@ # # Instructions how to add a client simulation see file "client-simulation.wiresharked.md". + names+=("Android 7.0 (native)") + short+=("android_70") + ch_ciphers+=("ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA") + ciphersuites+=("") + ch_sni+=("$SNI") + warning+=("") + handshakebytes+=("160301009d0100009903036cea0f867ae9fdd087adedaa810119e62971b36c0486d44fb3099e51403c8a1e000018c02bc02ccca9c02fc030cca8c013c014009c009d002f003501000058ff010001000000000d000b00000873796e6f642e696d0017000000230000000d0016001406010603050105030401040303010303020102030010000e000c02683208687474702f312e31000b00020100000a000400020017") + protos+=("-no_ssl3 -no_ssl2") + tlsvers+=("-tls1_2 -tls1_1 -tls1") + lowest_protocol+=("0x0301") + highest_protocol+=("0x0303") + alpn+=("h2,http/1.1") + service+=("HTTP,FTP") + minDhBits+=(-1) + maxDhBits+=(-1) + minRsaBits+=(-1) + maxRsaBits+=(-1) + minEcdsaBits+=(-1) + curves+=("prime256v1") + requiresSha2+=(false) + current+=(true) + names+=("Android 8.1 (native)") short+=("android_81") ch_ciphers+=("ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA") From f647ae8264d274067706b2b3272e7b930cbf16c0 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Tue, 23 Jun 2020 19:24:24 +0200 Subject: [PATCH 118/211] Change to grade cap --- doc/testssl.1.md | 3 --- testssl.sh | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index c15f3ee..a866739 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -407,9 +407,6 @@ As of writing, these checks are missing: * Exportable key exchange - should give **40** points in `set_key_str_score()` * Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()` -#### STARTTLS -This program rates STARTTLS connections, exactly according to the specification. However, this program adds a grade warning about STARTTLS is being used. This is not apart of the rating specification, and limits the grade a STARTTLS connection can have, to a maximum of `A-`. - #### Implementing new grades caps or -warnings To implement a new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason: ```bash diff --git a/testssl.sh b/testssl.sh index 9c64829..d5b3c3b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20802,7 +20802,7 @@ run_rating() { pr_headlineln " Rating (experimental) " outln - [[ -n "$STARTTLS_PROTOCOL" ]] && set_grade_warning "STARTTLS encryption is opportunistic. The grade is very insignificant" + [[ -n "$STARTTLS_PROTOCOL" ]] && set_grade_cap "T" "Encryption via STARTTLS is not mandatory (opportunistic). This leads to a false sense of security" # Sort the reasons. This is just nicer to read in genereal IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) From b2d41330e0f0636eb35ebfc6fc0eb747519514dc Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 25 Jun 2020 13:05:47 +0200 Subject: [PATCH 119/211] port typo fixes to html and roff doc --- doc/testssl.1 | 4 ++-- doc/testssl.1.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 38bbfed..1b7a9f3 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -650,10 +650,10 @@ To implement a new grading cap, simply call the \fBset_grade_cap()\fR function, When implementing a new check (be it vulnerability or not) that sets grade caps, the \fBset_rating_state()\fR has to be updated (i\.e\. the \fB$do_mycheck\fR variable\-name has to be added to the loop, and \fB$nr_enabled\fR if\-statement has to be incremented) . .P -The \fBset_rating_state()\fR automatically disables ratinng, if all the required checks are \fInot\fR enabled\. This is to prevent giving out a misleading or wrong grade\. +The \fBset_rating_state()\fR automatically disables rating, if all the required checks are \fInot\fR enabled\. This is to prevent giving out a misleading or wrong grade\. . .P -When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1\. Added to the script wherever relevant, or 2\. Added to the above list of missing checks (if \fIi\.\fR is not possible) * New grade warnings has to be added wherever relevant * The revision output in \fBrun_rating()\fR function has to updated +When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1\. Added to the script wherever relevant, or 2\. Added to the above list of missing checks (if above is not possible) * New grade warnings has to be added wherever relevant * The revision output in \fBrun_rating()\fR function has to updated . .SH "EXAMPLES" . diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 8290958..38067d1 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -515,7 +515,7 @@ set_grade_warning "Documentation is always right"

    When implementing a new check (be it vulnerability or not) that sets grade caps, the set_rating_state() has to be updated (i.e. the $do_mycheck variable-name has to be added to the loop, and $nr_enabled if-statement has to be incremented)

    -

    The set_rating_state() automatically disables ratinng, if all the required checks are not enabled. +

    The set_rating_state() automatically disables rating, if all the required checks are not enabled. This is to prevent giving out a misleading or wrong grade.

    Implementing a new revision

    @@ -523,7 +523,7 @@ This is to prevent giving out a misleading or wrong grade.

    When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1. Added to the script wherever relevant, or - 2. Added to the above list of missing checks (if i. is not possible) + 2. Added to the above list of missing checks (if above is not possible) * New grade warnings has to be added wherever relevant * The revision output in run_rating() function has to updated

    From 91ceaca1e94df11578d0f637bb46cc9e99b81f38 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 25 Jun 2020 07:31:50 -0400 Subject: [PATCH 120/211] Fix #1662 This commit fixes #1662 by changing the fileout to use the value of $cert_ext_keyusage rather than the string "cert_ext_keyusage". --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index faebe83..86a9ce5 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8647,7 +8647,7 @@ certificate_info() { outok=false fi if "$outok"; then - fileout "${jsonID}${json_postfix}" "INFO" "cert_ext_keyusage" + fileout "${jsonID}${json_postfix}" "INFO" "$cert_ext_keyusage" fi out "$indent"; pr_bold " Serial / Fingerprints " From 1f2b4a3f40fe68f401ae9f93dd8079501a9f9344 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 25 Jun 2020 13:18:28 -0400 Subject: [PATCH 121/211] Fix #1665 This commit fixes #1665 by adding the certificate number to the JSON identifier for cert_eTLS. --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 7a161b0..b750190 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8942,7 +8942,7 @@ certificate_info() { out "$indent"; pr_bold " ETS/\"eTLS\"" out ", visibility info " jsonID="cert_eTLS" - etsi_etls_visibility_info "$jsonID" "$spaces" "$HOSTCERT" "$cert_txt" + etsi_etls_visibility_info "${jsonID}${json_postfix}" "$spaces" "$HOSTCERT" "$cert_txt" # *Currently* this is even listed as a vulnerability (CWE-310, CVE-2019-919), see # https://nvd.nist.gov/vuln/detail/CVE-2019-9191, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9191 # For now we leave this here. We may want to change that later or add infos to other sections (FS & vulnerability) From 288223c70760a534712d2cc68a57486f281820fe Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 25 Jun 2020 20:47:51 +0200 Subject: [PATCH 122/211] Polish STARTTLS rating output Moved the sentence ~i "A grade better than T would lead to a false sense of security" to the documentation. No reason for excuses in the output. ;-) Explanation fits better in the doc. See also #1657 --- doc/testssl.1 | 2 +- doc/testssl.1.html | 2 +- doc/testssl.1.md | 3 ++- testssl.sh | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 1b7a9f3..834d339 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -638,7 +638,7 @@ This program has a near\-complete implementation of SSL Labs\'s \'SSL Server Rat This is \fInot\fR a 100% reimplementation of the SSL Lab\'s SSL Server Test \fIhttps://www\.ssllabs\.com/ssltest/analyze\.html\fR, but an implementation of the above rating specification, slight discrepancies may occur\. Please note that for now we stick to the SSL Labs rating as good as possible\. We are not responsible for their rating\. Before filing issues please inspect their Rating Guide\. . .P -Disclaimer: Having a good grade is \fBNOT\fR necessarily equal to having good security! Don\'t start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it\. +Disclaimer: Having a good grade is \fBNOT\fR necessarily equal to having good security! Don\'t start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it\. Please note STARTTLS always results in a grade cap to T\. Anything else would lead to a false sense of security \- at least until we test for DANE or MTA-STS\. . .P As of writing, these checks are missing: * GOLDENDOODLE \- should be graded \fBF\fR if vulnerable * Insecure renegotiation \- should be graded \fBF\fR if vulnerable * Padding oracle in AES\-NI CBC MAC check (CVE\-2016\-2107) \- should be graded \fBF\fR if vulnerable * Sleeping POODLE \- should be graded \fBF\fR if vulnerable * Zero Length Padding Oracle (CVE\-2019\-1559) \- should be graded \fBF\fR if vulnerable * Zombie POODLE \- should be graded \fBF\fR if vulnerable * All remaining old Symantec PKI certificates are distrusted \- should be graded \fBT\fR * Symantec certificates issued before June 2016 are distrusted \- should be graded \fBT\fR * ! A reading of DH params \- should give correct points in \fBset_key_str_score()\fR * Anonymous key exchange \- should give \fB0\fR points in \fBset_key_str_score()\fR * Exportable key exchange \- should give \fB40\fR points in \fBset_key_str_score()\fR * Weak key (Debian OpenSSL Flaw) \- should give \fB0\fR points in \fBset_key_str_score()\fR diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 38067d1..af08013 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -484,7 +484,7 @@ Rating automatically gets disabled, to not give a wrong or misleading grade, whe

    This is not a 100% reimplementation of the SSL Lab's SSL Server Test, but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide.

    -

    Disclaimer: Having a good grade is NOT necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it.

    +

    Disclaimer: Having a good grade is NOT necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it. Please note STARTTLS always results in a grade cap to T. Anything else would lead to a false sense of security - at least until we test for DANE or MTA-STS.

    As of writing, these checks are missing: * GOLDENDOODLE - should be graded F if vulnerable diff --git a/doc/testssl.1.md b/doc/testssl.1.md index a866739..6755235 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -392,7 +392,8 @@ This program has a near-complete implementation of SSL Labs's '[SSL Server Ratin This is *not* a 100% reimplementation of the [SSL Lab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide. -Disclaimer: Having a good grade is **NOT** necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it. +Disclaimer: Having a good grade is **NOT** necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it. Please note STARTTLS always results in a grade cap to T. Anything else +would lead to a false sense of security - at least until we test for DANE or MTA-STS. As of writing, these checks are missing: * GOLDENDOODLE - should be graded **F** if vulnerable diff --git a/testssl.sh b/testssl.sh index d5b3c3b..fe021e6 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20797,12 +20797,13 @@ run_rating() { local c1_worst c1_best local c3_worst c3_best c3_worst_cb c3_best_cb local old_ifs=$IFS sorted_reasons sorted_warnings reason_nr=0 warning_nr=0 + local spaces=" " outln "\n"; pr_headlineln " Rating (experimental) " outln - [[ -n "$STARTTLS_PROTOCOL" ]] && set_grade_cap "T" "Encryption via STARTTLS is not mandatory (opportunistic). This leads to a false sense of security" + [[ -n "$STARTTLS_PROTOCOL" ]] && set_grade_cap "T" "Encryption via STARTTLS is not mandatory (opportunistic)." # Sort the reasons. This is just nicer to read in genereal IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}")) From 7c759937469892e5eaf2621acfbf33c7e5f38fef Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 25 Jun 2020 20:54:43 +0200 Subject: [PATCH 123/211] remove unused spaces var --- testssl.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index fe021e6..716da8f 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20797,7 +20797,6 @@ run_rating() { local c1_worst c1_best local c3_worst c3_best c3_worst_cb c3_best_cb local old_ifs=$IFS sorted_reasons sorted_warnings reason_nr=0 warning_nr=0 - local spaces=" " outln "\n"; pr_headlineln " Rating (experimental) " From 919064095f7115a67795c70306452c8d03b51c36 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 6 Jul 2020 15:45:36 -0400 Subject: [PATCH 124/211] Separate pr_cipher_quality() into two functions This commit separates pr_cipher_quality() into two functions, one that returns the quality of a cipher as a numeric rating (get_cipher_quality()) and one that prints a cipher based on its quality (pr_cipher_quality()). This separation allows get_cipher_quality() to be used to determine how good a cipher is without having to print anything. Having this ability would be helpful in implementing the changes suggested in #1311. --- testssl.sh | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/testssl.sh b/testssl.sh index 135613f..416080d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -6108,8 +6108,7 @@ pr_ecdh_curve_quality() { pr_ecdh_quality "$bits" "$curve" } -# Print $2 based on the quality of the cipher in $1. If $2 is empty, just print $1. -# The return value is an indicator of the quality of the cipher in $1: +# Return a value that is an indicator of the quality of the cipher in $1: # 0 = $1 is empty # 1 = pr_svrty_critical, 2 = pr_svrty_high, 3 = pr_svrty_medium, 4 = pr_svrty_low # 5 = neither good nor bad, 6 = pr_svrty_good, 7 = pr_svrty_best @@ -6119,13 +6118,11 @@ pr_ecdh_curve_quality() { # Hint: find out by "grep etc/cipher-mapping.txt" but it' might be be easier # to look out Enc= and Au= or Mac= # -pr_cipher_quality() { +get_cipher_quality() { local cipher="$1" - local text="$2" local ossl_cipher [[ -z "$1" ]] && return 0 - [[ -z "$text" ]] && text="$cipher" if [[ "$cipher" != TLS_* ]] && [[ "$cipher" != SSL_* ]]; then # This must be the OpenSSL name for a cipher or for TLS 1.3 ($TLS13_OSSL_CIPHERS) @@ -6135,46 +6132,36 @@ pr_cipher_quality() { # the case, see "prepare_arrays()" and "./etc/cipher-mapping.txt" case "$cipher" in *NULL*|EXP*|ADH*|AECDH*|*anon*) - pr_svrty_critical "$text" return 1 ;; *RC4*|*RC2*|*MD5|*M1) - pr_svrty_high "$text" return 2 ;; AES256-GCM-SHA384|AES128-GCM-SHA256|AES256-CCM*|AES128-CCM*|ARIA256-GCM-SHA384|ARIA128-GCM-SHA256) # RSA kx and e.g. GCM isn't certainly the best - pr_svrty_good "$text" return 6 ;; *CBC3*|*3DES*|*IDEA*) - pr_svrty_medium "$text" return 3 ;; *DES*) - pr_svrty_high "$text" return 2 ;; PSK-*GCM*|PSK-*CCM*|RSA-PSK-*GCM*|RSA-PSK-CHACHA20-POLY1305|PSK-CHACHA20-POLY1305) # PSK kx and e.g. GCM isn't certainly the best - pr_svrty_good "$text" return 6 ;; DH-*GCM*|ECDH-*GCM*) # static DH or ECDH kx and GCM isn't certainly the best - pr_svrty_good "$text" return 6 ;; *GCM*|*CCM*|*CHACHA20*) - pr_svrty_best "$text" return 7 ;; #best ones *AES*SHA*|*CAMELLIA*SHA*|*SEED*SHA*|*CBC*|*GOST*) - pr_svrty_low "$text" return 4 ;; *) - out "$text" return 5 ;; esac @@ -6187,45 +6174,64 @@ pr_cipher_quality() { # Now we look at the RFC cipher names. The sequence matters - as above. case "$cipher" in *NULL*|*EXP*|*_DES40_*|*anon*) - pr_svrty_critical "$text" return 1 ;; *RC4*|*RC2*|*MD5|*MD5_1) - pr_svrty_high "$text" return 2 ;; *_DES_*) if [[ "$cipher" =~ EDE3 ]]; then - pr_svrty_medium "$text" # 3DES return 3 fi - pr_svrty_high "$text" return 2 ;; *CBC3*|*3DES*|*IDEA*) - pr_svrty_medium "$text" return 3 ;; *CBC*|*GOST*) - pr_svrty_low "$text" return 4 ;; TLS_RSA_*|TLS_DH_*|TLS_ECDH_*|TLS_PSK_WITH_*) - pr_svrty_good "$text" # RSA, or static DH, ECDH, or PSK kx and e.g. GCM isn't certainly the best return 6 ;; *GCM*|*CCM*|*CHACHA20*) - pr_svrty_best "$text" return 7 ;; *) - out "$text" return 5 ;; esac } +# Print $2 based on the quality of the cipher in $1. If $2 is empty, just print $1. +# The return value is an indicator of the quality of the cipher in $1: +# 0 = $1 is empty +# 1 = pr_svrty_critical, 2 = pr_svrty_high, 3 = pr_svrty_medium, 4 = pr_svrty_low +# 5 = neither good nor bad, 6 = pr_svrty_good, 7 = pr_svrty_best +# +pr_cipher_quality() { + local cipher="$1" + local text="$2" + local -i quality + + [[ -z "$1" ]] && return 0 + [[ -z "$text" ]] && text="$cipher" + + get_cipher_quality "$cipher" + quality=$? + case $quality in + 1) pr_svrty_critical "$text" ;; + 2) pr_svrty_high "$text" ;; + 3) pr_svrty_medium "$text" ;; + 4) pr_svrty_low "$text" ;; + 5) out "$text" ;; + 6) pr_svrty_good "$text" ;; + 7) pr_svrty_best "$text" ;; + esac + return $quality +} + # arg1: file with input for grepping the type of ephemeral DH key (DH ECDH) read_dhtype_from_file() { local temp kx From 45eafd239f5ecc96a9735833362d452662797a25 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 7 Jul 2020 07:30:48 -0400 Subject: [PATCH 125/211] Fix printing of unrecognized option When testssl.sh is called with an unknown option it prints something like: 0: unrecognized option "--option" It should be printing the name of the program rather than "0". This commit fixes that. --- testssl.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testssl.sh b/testssl.sh index 416080d..fe3ef22 100755 --- a/testssl.sh +++ b/testssl.sh @@ -21693,7 +21693,7 @@ parse_cmd_line() { (--) shift break ;; - (-*) tmln_warning "0: unrecognized option \"$1\"" 1>&2; + (-*) tmln_warning "$0: unrecognized option \"$1\"" 1>&2; help 1 ;; (*) break From 6c8df4529cc0bc672a076bf7b082e2bef5fd264e Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 7 Jul 2020 12:01:23 -0400 Subject: [PATCH 126/211] Include cipher quality in JSON and CSV run_cipherlists() checks for support for different groups of ciphers, but does not indicate which ciphers in each group are supported. So, for example, if the JSON file indicates that there is a problem with severity level "HIGH" because the "LOW" ciphers are available, there is no clear indication of which of these ciphers are supported by the server. If run_server_preference() is run with "--color 3", then there will be a visual indication (via color) of the ciphers the server supports that are considered bad, but this information does not appear in the JSON (or CSV) output. The JSON (or CSV) output will include information about every cipher that is supported, but the severity level is always "INFO". This commit addresses this problem by changing the fileout() calls in ciphers_by_strength() and cipher_pref_check() that output each supported cipher individually so that the "severity" argument is an indication of the quality of the cipher. With this, information about which bad ciphers are supported can easily be found in the JSON/CSV output. --- testssl.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index fe3ef22..c18299c 100755 --- a/testssl.sh +++ b/testssl.sh @@ -4378,7 +4378,7 @@ ciphers_by_strength() { outln "${sigalg[i]}" id="cipher$proto" id+="_${normalized_hexcode[i]}" - fileout "$id" "INFO" "$proto_text $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}") $available" + fileout "$id" "$(get_cipher_quality_severity "${ciph[i]}")" "$proto_text $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}") $available" fi done @@ -6204,6 +6204,26 @@ get_cipher_quality() { esac } +# Output the severity level associated with the cipher in $1. +get_cipher_quality_severity() { + local cipher="$1" + local -i quality + + [[ -z "$1" ]] && return 0 + + get_cipher_quality "$cipher" + quality=$? + case $quality in + 1) tm_out "CRITICAL" ;; + 2) tm_out "HIGH" ;; + 3) tm_out "MEDIUM" ;; + 4) tm_out "LOW" ;; + 5) tm_out "INFO" ;; + 6|7) tm_out "OK" ;; + esac + return $quality +} + # Print $2 based on the quality of the cipher in $1. If $2 is empty, just print $1. # The return value is an indicator of the quality of the cipher in $1: # 0 = $1 is empty @@ -7051,7 +7071,7 @@ cipher_pref_check() { neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}" "true" outln "${sigalg[i]}" id="cipher-${proto}_${normalized_hexcode[i]}" - fileout "$id" "INFO" "$proto_text $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}")" + fileout "$id" "$(get_cipher_quality_severity "${ciph[i]}")" "$proto_text $(neat_list "${normalized_hexcode[i]}" "${ciph[i]}" "${kx[i]}" "${enc[i]}" "${export2[i]}")" done else outln From cec5726f30da8c315c2402750f1c5bf4bc40c2ce Mon Sep 17 00:00:00 2001 From: Dirk Date: Fri, 10 Jul 2020 19:52:47 +0200 Subject: [PATCH 127/211] Revised risk for BREACH --> medium --- testssl.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testssl.sh b/testssl.sh index c18299c..425f17b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -16102,7 +16102,7 @@ run_breach() { ret=1 ;; no_compression) - pr_svrty_best "no gzip/deflate/compress/br HTTP compression (OK) " + pr_svrty_good "no gzip/deflate/compress/br HTTP compression (OK) " outln "$disclaimer" fileout "$jsonID" "OK" "not vulnerable, no gzip/deflate/compress/br HTTP compression $disclaimer" "$cve" "$cwe" ret=0 @@ -16150,10 +16150,10 @@ run_breach() { fi done detected_compression="$(strip_trailing_space "$detected_compression")" - pr_svrty_high "potentially NOT ok, \"$detected_compression\" HTTP compression detected." + pr_svrty_medium "potentially NOT ok, \"$detected_compression\" HTTP compression detected." outln "$disclaimer" outln "${spaces}${when_makesense}" - fileout "$jsonID" "HIGH" "potentially VULNERABLE, $detected_compression HTTP compression detected $disclaimer" "$cve" "$cwe" "$hint" + fileout "$jsonID" "MEDIUM" "potentially VULNERABLE, $detected_compression HTTP compression detected $disclaimer" "$cve" "$cwe" "$hint" fi debugme outln "${spaces}has_compression: ${has_compression[@]}" ;; From 903eeec97b3c6e0fcc85a83957c4996dc773df38 Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 14 Jul 2020 22:23:11 +0200 Subject: [PATCH 128/211] Start of implementing of hanno's bad OCSP intermediate CA detector see https://github.com/hannob/badocspcert --- testssl.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/testssl.sh b/testssl.sh index 425f17b..dbfd4ef 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8965,6 +8965,30 @@ certificate_info() { # https://certs.opera.com/03/ev-oids.xml # see #967 + # courtesy Hanno Boeck (see https://github.com/hannob/badocspcert) + out "$indent"; pr_bold " Bad OCSP intermediate" + out " (exp.) " + jsonID="cert_bad_ocsp" + badocspcerts="${TESTSSL_INSTALL_DIR}/etc/bad_ocsp_certs.txt" + +#FIXME: there might be >1 certificate. We parse the file intermediatecerts.pem +# but just raise the flag saying the chain is bad w/o naming the intermediate +# cert to blame. We should have split intermediatecerts.pem e.g. into +# intermediatecert1.pem, intermediatecert2.pem before + badocsp=1 + for pem in "$TEMPDIR/intermediatecerts.pem"; do + hash=$($OPENSSL x509 -in "$pem" -outform der 2>/dev/null | $OPENSSL dgst -sha256 -binary | $OPENSSL base64) + grep -q "$hash" "$badocspcerts" + badocsp=$? + [[ $badocsp -eq 0 ]] && break + done + if [[ $badocsp -eq 0 ]]; then + prln_svrty_medium "NOT ok" + fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is/are intermediate certificate(s)" + else + fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok" + fi + out "$indent"; pr_bold " ETS/\"eTLS\"" out ", visibility info " jsonID="cert_eTLS" From eb7b0c96444d1197a4d36cde68c4eeac8d95a71c Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 14 Jul 2020 22:26:23 +0200 Subject: [PATCH 129/211] add hash file --- etc/bad_ocsp_certs.txt | 293 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 etc/bad_ocsp_certs.txt diff --git a/etc/bad_ocsp_certs.txt b/etc/bad_ocsp_certs.txt new file mode 100644 index 0000000..e3b84f5 --- /dev/null +++ b/etc/bad_ocsp_certs.txt @@ -0,0 +1,293 @@ +wXYR23sxE2HiDXuCMarfR4vLf/oL5YXAbeIwkiPC74o= +/bNF0mk3ftnTQwi7eIVjKzDmH46vt+tedfgCTV/U4Yc= +16BtRir8gNcRg9lKOYstzIJYNefGbaXkVAVXLwSwWl0= +gptouX8R3uNEhXrBLrQM5QLuUmARIwFv3r8UIAkaQEg= +VEULJ4BS+pGRFgDLmPMQCg+1gVKNj+q5i2/++KSMmY4= +c50MOnG2w3fL0cQIrnzBlzLCy++GINY7Fsn9AYXfmnE= +VQdJ/141xAfVFdmLMnUEvEhP2q4opxOv5OIoKYa+BOg= +Qavo+alRqJkPvc0+FM9HnUJkAbEV8BcBibZl6UDDYyw= +iKuWH2T5No9MTRMH9Z1OWcXlcPhSpsUROo/yjgQxUVc= +zXQZjUwj5HAd6leYkjIbnk9HoIvYN0cQuJmq0UlaSzU= +vEicbcK94lP1hto0E01mQLi1g6MSCJzhCTPEvAP8XzM= +89kW0bcyAfBJ7afll/4KXM1XEaaVj7wLem9/pk96/kc= +h6rZeKX+LulB5AaPmWPqKb2qWIeNJBIkmoLNIvgSbYI= +REzv7dg/YxXF7Iay8c1jsdvyOHz6K+sPtAAVPyr5QI8= +6keT4uuBTUMmH4Borv3ng7g358AhrBnI6bCFUfLWyD8= +DLZoT2AcQW+ZKolAmRSjKgwgmXFodfX3jsDEaxtXCZw= +fTW/BMhXKCtCLoUeqKYit10uzLq2XVumJZjB2koR1QI= +TefMrLUJmUJkSVDyzIYTQBJWXGFNUap9MIf6wZaVihQ= +kO5UjrrKyrQCB6YaN4zhhrlNJK58Vb/IMGXqlgcuKzg= +EaJ2cYciZURctyWOsoRO5hTRR3e59vc76VMhIvIfrQ0= +HLRwcoz1bzAgA7sOTrBiQU+hHU+X4/BhFwyWyIBx1xE= +ysBfTwUIEJkybc8nxNlwqKPqkU5nkq2swQSWEcntQ7o= +7AICWhoVeywCrEn1Wai2zwchJvg3lJn658Uu0YPakBI= +VvMhZ/AD2PLLc4qzhsNcoVJlzCmpjuEGbNPhrFVsoVw= +7r7fhG8dug6Iqba5qw/2Xwl/KEwGpkqTnujFyebzE/Q= +U1cNBeQNxi+d+R0Vr7AVtWqqaAwgzBaObVfZRsnOJoQ= +9kDlZDxAwfMp4QBDjijJV2ka+opT5AWjJvev63DCO8E= +GwRlNTeOB9EKzaok7vziBCC7mllhFOtHXKaWNXdT6SU= +fTOuYYzWJVM3fSU9LryihdhOmKkk2J+Y1L5P7jH5Kqg= +wC2KMO1psvhk7Y+xpjo+clUoiSDKKUvcow9jiY+5GVw= +BOy6j5K/90WMSl58aSYfx+LvUtWvVPvdkrFxQbvgZR8= +48UkTRX44LA09QCQO32hHFfBZWF1uGYIx/zcVh0IG/Y= +lA0vISoqOcyEvULQ9txPe6TEd+elqZIslrn17BTkpsg= +WXK5vUcQF9XzJwW+6RVwNiZXX4snCjnBwxLH+SRsENQ= +q/OAPNKTniaAPlIoCoH2fEbD4O51/Nux4w+wOjIaz60= +N9IgpsdSJ5kCEZE0nBg/kXvhvoYmz5JrD9bgqGge4DE= +QqtNnxgJRU6+wkXY2wb/YaqCibBaJj3+6WYtrJFmYEM= +Bb+2YF1IUWpXG6+af/dTdhMEcNpe5/9oTCZy6qDAyK0= +/UwrmT4DVukNT5+9I2He8aSYN4zuzrkt12/grX4sexY= +k5BxSx2Qn6PXDdx2gbOPB+1OY1bLXHGRXRvc1I/jNfg= +MafFefJNVWLNsgP6Fanyw/9dwfLmv3wL2V+8sBFMjSE= +jlzqHRASUhwnpOEScHArx8mhFW8TUHPWyk5C7ugmJJ4= +Irbr3umwptpcn6ztJ7+dzgmAPCr8EfdrXAvPR7f31WA= +gP3kKCEq8MoKxTHu5u0t89PCpFV9/OhXBw/JR5IumyQ= +P+i+OSoIaEuZ9JfmGMfd9aAqQom/nQjllQRZMb+6gU8= +3Ist7lDdR4qxNcrCac6miFFVepEpq82Y31ITsj2/s80= +UCx6hwNB175n2ybb+s+WR6/YmoVL+IEvpO9ww1bsE+U= +vzmkJB9C1SI2iUSz3FPtnqpax3NeJC4GJ8DdW7pxRIQ= +OuaZ2U6P69rLhtT5DUCQMzNHjmXgZVxDJFEZfjP6B/I= +oloZVGgZ0EgADvnGV3xLzY0hVbHkNGpFmdbIt5eZ1KE= +8YRCvt9wtNFSETVscrZZMyvtA//Tu6evqqvm3p1yMAI= +2IiPSoT3TJdN/7Vzob9bu6zRcTuQUJb46wFQYr85bE0= +BkNqyNDI4SCltfqMK83LLx+AWF55x3t+D40PYWjZxF4= +Uk5bOlWNX/AafEYCahS1jHfPne+6aR6sjkoX+sKjo7Y= +1B0jxRVZ9l5cIlA9enq/XNll7DnGHGmKfZjXl69we0g= +XIDlafzuk/FaK8BDUQKyagT1qh7JkSwToFiBwarVAtI= +3gqS5UNbYTII3ENezHFYvyj0IKk+CpHVlllyBT9SNUk= +IcrbfNgi6hO0luJau4FRx/sA77se5FijtKTzwWvDwW8= +7T23SNbXju/wuUUrxqAuA5A5xVB1pDkyO4uoX7Ibndo= +SyTFIcR2AOg4AKP/DC1Y3ALIF3732ml8WoDB1IZ6Zt0= +XbHSLXBmhPBxnokC+4azRrP8nk0aH5IoOkiLw9GgSEs= +P3hQJbzPrKRwGhc0zg97MM5BBpQsjZL0G3CwZ+1l01Q= +FQc8a73HRpmohRjCelfJVuXiPWypYZ5SGkaMeHPeT4o= +qbVpjFJjvv89YHINwYRMuV0W8G4EJovOO+TWAoKwHvk= +o38cCp3HIpi2H5M8tUxlQ4Y3sm2+29HmuAgnnz2tMOU= +Pq1PcvBvEFSIHSco3gM6jhP63mvRZQhAGOuUPBc3jao= +2FU6KIDpa3qkx0E92QOv09WAUEaV3SahaP1IzOexR0o= +UH22DSY9PQnSg94uOqQ139h3XlK8M1cC44Mru1fsHL0= +Uz/pfrRfztJASeQe/p2yVKXdnZDf1TyVEsYgftsh2Cw= +0o20NeMSEqO9zPh2IPZUS5mpwCMov5g+iC/QYnodEw8= +X2yqRKRmPEQd2SyLZlX7+Xz24daTTbj1+Nl633Az+qw= +X79u1PD4etpl+ueqq0rZ4bWlhpOa7fs5pJJr5/ZcZQw= +hTY6JMsbZubPYkTofSQ9u4MG9gc1fGFMucTCJKDgQ1g= +fggrvFaXaxWdRpZUCpa2AUhhS6m14psgNfeJvs+/Blc= +vs/eEkzt00TZJctV7dpmLZqcBoj6mghwzj27baQxPk4= +T/QE8C4s0AGI8V0cAPS20eOLWjlc+FMU6uuoVbamS3U= +8i22V6GpKYQavKxSZxpc7op9BpWGr4XOFt4rBd2iIlI= +ON7T/2gnV5AIr0iH65aYo8+pJ/qO1Z8GugkPuaY+LXc= +cFYQcjyWE/ZBMYHK33PVEHG9dh/aSRQj4avQAVAbZPM= +lylXMEAxI07Rdnn9y5dVbWFz1fK/Dm5m1hJoDKbndoU= +VnmkMeedTrnulnxg2HA8fHj0Q/cduXFX5DBZ3kLYUN8= +ziMyOQIIdCoaymUTl0xMnbJpHq9FaLUz5KF+1d2pc+Y= +x0It4hzryS7K5r6dzX5xHuZQ0wrtcR+7Dfayx4S2xPs= +OUzLvAs1lcoN7UAHL6+YY27QIWma0olFZIeRZbW11Mk= +xhkwd8YYnR37v4E7h9x8vwSYrPcniHvH7FQyCQbem8g= +ApN5EY5XdSJsVNcYKjZ6JAtRdw9QEbs1F3z9F9myRFo= +ThB8mBtCrL5BwBBn4W1E22SBTUGT5XIxfqBLh8ecR18= +1Ia/o/ANFl7iz2Jw/afQCBfljNot9Polbg8usSLPjwI= +6+h7tBiFAnCfREBVJZq7IrxRuIyQhBmhNVnfyO9mMNE= +rxiY1/Bjh1HAddAULU4qDqcx/GIjJPFT/hvztq/ZrxM= +LgGRdRygy6gcOmM43uGgK41rzE8fgmG4CbzOerrxpD0= +TCQc/j0/+2DKiNawalUqsc8O99jS4I2hUoK1UZLrvSk= +Yy/Wl7rK8e0jJRfsm3Yit8JeFEiwzGJrMyhnGeNRzoo= +OALkJFFveO6sMpqumx9gpBLb4dWwldesncDc3ePB9fs= +8DdiFAXg81ZQfiOfrdZHhC07UIV8PP+ECFkXT3L2/Rg= +OTuLFcq8OIb7LkFkldY8i63Y3K+HVSB2yKCpY3wk3kc= +d+rEdkU8tzIlf/FmpevRZWyx9nO2jijfQXdBM5efoqQ= +X/rEPg3cW0rytpb2vE236R3zFLuP4NBxOgsaetKmj6w= +FHxEf+64YgK1AzFPyvADa+qu9DfDm1azWOxEap0gOH8= +Qs/dpvZguOW0wcQRllpFGTElWeMmL422nS2uF7JrO6M= +e0ZNw4T9saUlwswnntDHz60kvs9yxGp9cJPRV8IXYH4= +rgO5rRcQaih4WDCx3NY2eXxMZNgcuNFhWV26+DQz5kw= +rkGVTarL/T5bn9cHj3tvquYbVZ7Oee+O2FitjAIqejw= +Fmz4lA9HJR1VUo7Z5Z4melkqToo43T1on43LpknpS3M= +0Dnu/3EIjMDxagWo/zxhYQ4UHR6FCsfhH3cT7uiMuVE= +gxB49+Ifpx/iT5HXGOSlcynv5vSHGxJhFdXSc7+tn3Y= +YmQD4Ho5PnB8iKRwRQSRcEsSK2TRGDI5F91NQfwGPDg= +V0k5vjI5lVaapElYqzCJ9jmpyG45ezg+9g6ZRGSbQwo= +8O5ZFO2UxyUtBYtOOYCK7m+o9izwl0+31tKp3xbjqH8= +2/EmjBPmRa31jRYmxDNr0/qFoaTRAyDDHqDn0KZgiJY= +zYmOTBarGJLzY6Rh4RAzPHTp3Us+Z+IjZMzAMOu1718= +5UEx8Tn8I8Kcm9MiLhrnEVYZTK3SgXIt3mEwOXl46GE= +EcYS0zK0wSJCUqb+L4o5bbr0A9+cy2IFbeOZWIBKuC8= +nHyngZxD4CplRlj7NmUL2KPyKpuFNe81fiu8i5IakOI= +v1SpOD9EZi517KIusOR/LCNeXRGKfoluyLZFkWy6aOk= +AaBwYTqusQLERGiiMVWeO2bRmC7GQ1iRkJqeazZkI9I= +prt/LZfwpe6H5EaWrtD6IQWjOw73Dc9ocygcbroIE84= +lu8zwkqLHxbPFw9DIh4X5ir/aQqLAU8kUpv+s49AoNo= +YOh1YzkH5QH7JqEK+9lzAhxTri9gGHFFhxYOg9UuGaI= +3YcG9HnxHbxsapDBI1j/oAo3MfC+0rG2UpBIMI3MEuM= +nk5sCNP756rPK3o/wrrD1swblHU/PJYVIj1qj2txcj4= +JTTsG9dMPBt2YaDnzCjxvfKoLM05Q+6q3Nj+2VBV6tE= +z2TrVpvtIKtWfKZcicZOoeeIxmBHoO8exeS9LlbKwnE= +rUcj6rVaV0U6X1W/W/dvygi1p/I5tCabOqm44Uk1MB0= +nM3gEf9hnDId/o1kkM2N4Thn0ZOpte5qb1rED2HXEcU= +gFp7gGAab/tKveY170dwXq4XYg3vnPr2FGK2LXxLiGo= +cWCg2EGxxcEgoIyS3iMmSD2Q2b/OGYS91v9KtrfSc8M= +qQ+X1rXHYS4CDLvPB0YgDJZ24YKMWoUL5ryIjDRfpLU= +nACcovl+osvygXNgDe9velTFZkWZuKtQDuGIWlbk8nA= +rJWg/X4L9eG+bwKgQvDwmmV6euEnLt7FBatrmmEWeCw= +ZAF9eLXwwExBlzXWpKbVLsHSARUkOw18dFB+JX/6SkA= +OGTWwQAfAMSqgdzbN14qC0348ooaqZ+h84t0vOgtWxg= +Yv3R3U29JpQAZqoDD82kUbK8IUP+zmWoqgP8C9MR8P0= +vL0E1K7ZYsnSWv4M+vhjjOFDFlKYjsUhcynnVZrDxnE= +z4mkHf7l9xdA3vYCc13b8d6+DLgW1zmA2aWDxYgc53g= +OoglMMA+phXl702tvXyGYJEvqT+vUIhxb7Rqjh/6khg= +HJJmkCoxw5QbUG1E0NTQbsnbdlXmX5VXZZ+rdospCxs= +fLiI73QNy/wMIL2kTywmGfbQ1FmPuTLQN9ryeAd3c6U= +WONo7k1hW4iOEcVSsss7Rp8wrEv0jYs3m1EAnAgmQ+w= +yXgWNowrekYIszRNvmhI2L0SEmDC952siskMrhfI5Xw= +O/HkFQPH8CPQ1Mr/vo5RJiwscxC8bZbozI0UOmAK7oA= +PoS6Q0KQhRbndXPAmS8JecoITkaFaB/xlcy6iiKbinY= +jZz9mPUrHCxvTpztPR2ZWSfvsNVjj73wiDTQ9yyikRg= +s5YnQBRBzrQv9sf5g+DyqSMNGHdAPJ9AllyHV86/19Y= +ePE8SR4TAgaDfDqu2lXFby1dYN0NGXkQhD9G1freNek= +4qtqY+hkbg8jKCDtBH29RICWdFYhFJHJGvonr1qVVbk= +AFHJiSTyOb1Jq4yPaAL4uc9K1B6zJy8aNMDNbWHcs9Q= +j0FFvUjMm/ciklkj59LQnMy+mXOlODjKNvlWH3TownE= +LAlx85e6hzewM1hFVpBllJNqzhdTpaS+Xi8RtCukt1k= +UVh8hnv2bzXs5VSgjgpBwTuLvZtZ0mLSBKcDCaZyvuY= +VbDNLMrRjiWXfZBtrJ+ouGQ9eiWOezg38RV6E6wZ0Yc= +/T5EKAyNy+jPjPVVEeBmnIU3lF2h+nRowOpStobdW2g= +dNmS05ELz340uLXNKPkerrT0Hz2mOU14uMQ2ctQ/Tw8= +CrEV3p0Saj1OoQ3fCGPMnYlWdE63tMzat+V9agblhRg= +e4X20IWbJAozYzdKEYHgEgIQR9ztI1KoQfblW10HfqU= +QlIC1Lv9qbeZ0Peqkn2UT0LMpCN8L8GYdbkHXes1piE= +D3UQNcGOHTkunMVXxX6UpV0S+7CG8mpFKeJhNiW/0Tw= +yErgHs0gLa++7h8OZ5ZG3ozNZT14RnGKO19OEpMkKYo= +UrY9G9COg73HI9ex/pYs7BgG5/U/dvLHCFjKNSk6HcE= +4KZw9PEFfpF56dtF4zPON+PuMcNJnxxYSlh72aX1NkA= +oBmBHkNpykxiqqgKFUlhPmD2xc7Tg6+ded+Pjxk/Hf4= +Af1hSHk5SzrhSSUvwVaYZ0FZzlVQZp/5EokuGYNaHkw= +gyJm1rqMv8vyjgYUoB2fTDm45B98h9IHfbtsA4QMqcI= +5z8fGaRFmmBnpF6E21hdbB348SpznXM/WyiZZUbxh1o= +jAFyH2+p0h38KDZvUkNOqLRL+IQ/HEM7llv2WTNE6zY= ++Hiz3yE7CBe/8eXvTozXybV8gP/J+KcwnqRqr1QLrhg= +tA1oOJ/QQcVuKT+j1X19t5LdALYuvf8ZCw4mRAcsk0o= +Q9tljdTkAg+LXGvXEH4V4jNFmiJs0Nd++PcrKxzCmv4= +oK8UGM0U9rJBXsYxbH1Yi7JdQwo9bQJvV94g+WXqvdw= +weRTSNcUy0fbssC5uvm6HyfkIAR82nqLaTVkMATKq5c= +jmkw14oTnzgnFGpZRu+f46dzmbL9DOuwsu0I7hih11g= +2FLeXQmAht/ppvPXKNUmGGVYfEid5nV1PScjdKXW6fw= +6Mpy7LmIXCTqKd4OrJdwQnjSoeWbZmZtMn+wzGvdkS8= +SHR1jWVj4EM7HtzufMxdnCqtjroSvLBwRFS7Tvjq95k= +tc/msLKqhhoLNnwMBTlaU4rUk6nfARVEqO/EaH/bLMg= +uWj7/D7KKFqtw40jZ3JyQf99NzPxJ4+KT1DsyMDvHoA= +/x3SHxpdC0Us2WnPSqVTg1yr4Ck8bHsAnxRaogLALIs= +gGoqp37b08dtj9Bm37XMMxDzWbAQLOksD67BaqQ//wo= +M81TfgCducdYpWhDXAZwb58BE4BU4gud3JPgOXE4qms= +jZkhf/gtYOTfWb6KESFiXN/8TyLyHhJjod8G0twLVA4= +xrclJq9F1orhZnHp8cJFMECNk4+PBEfhEG4vKnDTrSE= +YPBm3Hik4ukpocjtEC7bcH3wMYH4L99Q1TpS2sNVxls= +dkoNhNVVLNWHLHNGTzfwIXXNpwWIECsTraKgGZ/EA+k= +Ko2+vTr9uXK8H3fP576FBoiM/tNe38VMuqY446WrPv8= +W9DA1XmAFWfzOI/mRO95CrMeLbIkbq9MiB5aV9mgpYI= +xbZ5EGlYFS+D+1iG3cQfB4UZPvZ8aXW+PlCfF/KbeoY= +kFjVBl+PPppjqv5c6Jp2RHCw3vnc87nsfQWH+riP66A= +1WAk8MXZI31+CyOFBn2O+I7dro7BmZafXKoszClp2YI= +rCiRmOxpliiAP4hD3WPHZOhWuCdM957bjtjpmXlpf5U= +x0eO33MQG6Eu7N3zSMd0f/94nvtAHMjcrfrglClUPlQ= +4P+T6QVsSO3ca2jpTzFcOiIp6c4krzNjTTTV7wMjMok= +Qf2VNoVpPOZ9DSPMkcoXkmHWuZSLIdO75fet++U5gt4= +3oPj/CRfawSJMeHIE06qypb6p7HvHROV86Ys4BSwUYI= +BmTx7u7RpzVXACAdL0Qx3EOwvOqxKajKv9Tth9BDQe8= +cbPtjO7MwGOM0gIrQm6SF2YnHeYjZ8fNhS7C8eERHnY= +zgJQ8qnxj2jV/5RKTGWfZpL3PgNpzswhX18lfjIlmHg= +t3h0inkrj5HwSwG6/DGjHtfvanEq/4C2YQ2are4get8= +iGWwLhQDkWdWEyLIwewud8R89763MdL+HLusv3LRdyw= +6Va4oVaHH3HUNMrHjr0j1Q79GrkHYc5DbnnQOu0TZkw= +wXOXph2a+dKiKczyYqsdMrSph6E0rlvZ3k/BVfAN0W4= +TzBNg2b/kJHt121TO+1Vko5zt6qtUuNbhudip38EaU4= +DzMgaSsXqRbOVPfk2nv+Ui+zGxrCHgiHdrN8B6NO4qg= +4MJ6zLmueGBbYeeCbAIdkczS92mljEvW2lRgC5njVDg= +FslKjMwVyYOCW1vT8txo1pTIoyDyMo7NjJylfeUdoOU= +AfiXESH0ED0wvkI1zX3A7ubGrhL8p3UISOoOLhP8JCg= +JbrMQKU5K4Kq3qBJA5BaRnEh8oIg5vL34P6YKq/BT6Y= ++5U8T8AEWEbQJJHI7M84e6NDR8F6uw6m1Z9t5NLx6gQ= ++1Tuqbzo6eqXghVPPUFCd/twn0m5R9c5eKwnhUbCzgM= +yKk01qjoRfemX1yPcf5E9Si4XjJldaDWeTnHTYDNF9g= +HJQqIqAWoeVVna537FzoZx+YrgukrC3CWUGOjh6flK0= +z3O1LQQbcwm0OdFiR0FLkMnSbkTjh0ijZQDVgptRh/k= +v17fvuuFmZxRacvz9NtjtnmtLh4icvw3lfn5kh5tBIc= +oBM75bFOAjEKLUvqtgEJTxGU7ovW/Snd/nuTR0Z8Luw= +ygBap14zWUvR3txYTh505RmOux3oiSntTz4un/zjhzs= +I6dHBNd6A8/T/xnmLFAISCFObGD9Kq733Oeo+e6fkjI= +O5Zo9Z9V+jg4/Co7gLf5tbE9Gkbx6qbgvP8ExUGYBWw= +WkBVNcESoKga8NKsyjw/m8Gmd1hs28Yzy09fd44aNVA= +kVPkQg3cfrTm6GSqA3fa30CC7NNQUhE2OOBdPClrwAY= +3QOOh+C00sNpaA0954Y4qzn8HX5QYymWkhEBdo241Ng= +TmPxQkAahPikc9bd7jQaFh+tqG00MMjCxTRTZBPZ25c= +cW824XTQWVrowV39agxJjpe2plWz8g/lSIyo+exgktY= +mk45jSqmA/7RCGPANi4TkNl+wS+gJ4/XgqqzzdV9wh0= +O7YMQ5NGQUpA4twtHW9WYiV40rHygyR2fBSQFaY/80o= +9dLSumgXp6mqDiE1S78Ob5XF4ofuiM8vJ58P/sTtrBU= +cBtDKsDN1NnPlbS4hMMr9cypDUTgFhq9E7k01o44BHI= +xMfENr2I6OaNsAKX34OsyBnhmGOboAUiyOMkWHaJhSM= +XN2AnPRPX4Zl6sFQVVBMWwa3h6wYKUUFvbq0p35Q13Y= +RgOPYyYijNtWYZxSJmYT2gTIykmeDQOw7c/8EQ1c/HA= +7cc0xQFQHceidEj6AsdJMfhXi/KXsXPzS4QegsZpGSY= +J9b9r4ApeEbf7/guf1i5pIrJ4+6ToRKxu+JD7hqXRHw= +yBksMve0nH8yocoAFZWn+eNsnnIFjW6qG6t3UqjBZxg= +vkCBOGmrJ6Bx0SrWqIMFg+vDthjj8jRjWfSxGhyUNO4= +HoZCeMIIgbZxwMbS4UthFQrR8Tz5LG7BS1UNy8R+FUE= +VMN6joU/0dY3jTeLk5MH7DIaMcwaWonnGAYzvBPxh2I= +nomO0D+kaWlpDa1zxylmdQRf+bWgEAo5m+uENamPUYU= +Sw0TktORVzUyB6ZMyxRoPd6dLO0ftYsW4Di+VwfCeBM= +eqRdb1sU2rHGhEwZwoBOFLWBHm7eHwKwrvBlp7NZxo8= +lMZj6epcJ+5PZBJ/m0JYY+mRqeFWwH3xoAgDrjF2QWI= +Logg3A6vrj1tKFwFfs4URws3dDiwAs7dTHK080OlT0M= +TnB4Z5RqwFNDxrqP8SHqZqdYA3kTJXqO5JdDUNOaEDQ= +wlxO28NuP7fD2Te+6fLSnjavsHz6MYgmLg1f3JGeDXc= +Xn/LnJe9pWmTsWWNEgIydh1mWjZEU0MA+mpb7F4NV5U= +ETE43XshZyWEAjji1+7ss3ONsTkGSyTLhT/CcKSeYFc= +vjPRxX693ZJ7V722BL5Fe1Uv5Wjn89y6CTw57Rwwojk= +g/yJGzUNng1+vm3Spr/j0LD0ZT/KBIYVpd7rvAOaP2Y= +VyZLgqhk26HBHvP4CruUysNmBmKwwi9XH/mTs/vPdvs= +dKvl5czrdUkf9yxM8yVAXYrb/jkOGJz0MLpg5ieYh44= +wv6s1nSHjHsMIyWi7O0KMz23eAqG3+w3WBAO/AEBxmU= +ndwuDVW0YeDHMigoLfVrK+8iTKI4VoHRe26MB3hSVzw= +MG6XOeNFj/RUaHe3BLLjkF5YsjXWTjL08CaskbcpXRU= +/ToPPdRIAJK21FBHPeuSAaCzCKiAeDOjxzj4oH64HtM= +Cqny59lccYt9HrfM29AWToYFeunWaSK8YPmQP5Sg8O8= +9XCaLS9otTv29kW7F4rflTRvif2lxjv94IBComSSqrI= +66NMexCWcWFMNn4d4HUSTDlUzhn4X6z2EJDsMZ9/Gn8= +Eo3tGorWDCS0JU4x25T8Q5K/k+1UNEcqpDoLmFYQYGg= +txawif5OU9Gi73ulesheaOxyLPYQUsJaWWJq07FcX0A= +al9MFnjKZeWfBg1Xzf9mUGUxSGHVOo59FFDKktlsoQI= +q8hnBsmNa/ZzcvkI7AGt9jGxkdczron4ND6wR7EIFEs= +QEfJ1pJgwHITvLhgin7F4oOKVrefZ4R4EurAd40NJ/E= +kl7n1aIq1/vpurVNfI0LmnT341qK9q9kXi6MNRmnCS8= +8GjeqhjMAtWovjXLgzgyeRApH25i5yFqk0dkoaukqAA= +sf466/ljp4gOdLCwVWaB6osczOPmmn07EKaKy+huSKE= +//4HdQP9cvDlM4sKe04hjn0f+C5JPn6FKuUaocdYXRc= +lcanR90Lx1WhlBgn6JS4CDWSJBt5JUHi6xsw+5sT9X8= +DTF2xY8yGqNMV8jffBfR9OdseX7BFsnx1pd0jtH859k= +DW5GeE87aU6cdQZ4ZBe8b4f50vc9GbXoCBYSshE3t2Y= +l4vPOcPDqs7+EEj6A2AoPcLr+lFQAkxeN4UU0+decpU= +LYGa85fiidxBcJxKr22jsZBHScN6mVhgdFgDcW19VpA= +oaBOTPxW/RkX60gfxGYpouRmVnVvoI4iSH9Utk4co+c= +Zj/elPiDah/r2DvoMQl5MS1l/4wbcWOU5o9B2COWwfg= +SDlfccwm9kJ0vQbH6xWR+dTsYrZPpsFlMfPLcsJGnYI= ++AhlYjZyW1ZvByHFGBWKZ1bJaQPLcetI3dqO3f6g8mw= +TngnF2hJQIjtH5uycSUxq10TjGxZtGZQ0oWzWFjmexM= +QqIfNyhYBPLnNgdctUh0Nozw4hEAiLPxOEEu7IhZCVQ= +RnFuv2oirqHfH5fmKl73CVEnzkVQtiQuumkHr8XEgs8= +iH1/Het/BxLIWvq/UMrfib8C6xPlUsv1LXtd8/IdsNo= +VbgN72B/iOMcWEBn0J1DNE2UEXuvZINlhHsc5bZ2d8k= +/DqYk44u07024NQfGFF2STIpBrLmszaQiRMlquzRlkQ= +UjV1BHNxu6iCY+WrXe2PawaVxdKhkqbdEUBsR2ujxy8= +KlCKX+YXPE6PASZpShpqcdkABFH6MdsDO4cFiQRB7MU= +/S8zJI85ZFZGh5gCNaXwvLOrVi5H9AT3QuUzQsU3UoA= +U4pFqvkAPU05iLv8+fhZsyXOX3ddwGASBQWFF/m8FeU= +50rUSw1W7r45uxhcUqub7+T3WJm2qBQW6RA9XCm/J/s= +53rALbbqoOmmtXOpmHDG3029uDOd2jvEB4b3DivX9/Y= +HpU+FRI6leRwRL+vBrGXT7mXa4T0Nx69d/itge/QXAA= +5Xu/UHn5gpO0FC80iXLmjjUeUrkDc/BfRvCcyzCvW/A= +vUQgxZKm9/JSromBJOAC8bnP4fJTiGTVazSntN+zH8g= +CE7KcKm0Dk4VY+NXdrcW5BCpcrsn7VHCSMSNMkL6ja8= +bsjTVRvQRUsMxLnbh2Nmrgh2TF/PFbAPlBG94w1TNq4= +qONS2wyOJ7rlg9AVFG6Led7gkdVjj1g6DEQvO/Vd7rM= +LRskT6zq+tlHVbXpmqv3rJHFmdpzv6Rh7uuvtLlptck= +6tyA999jBWDMGzU/aOpmAVnOlm4i1ARYh4B4fAR20OQ= +IuXm21yuQo7alq7QVk4rl3DsoswCUVjjep3e7eMfReU= From d07d1f102ed233e76e697beba4d4114f3e1b234c Mon Sep 17 00:00:00 2001 From: Dirk Date: Tue, 14 Jul 2020 23:42:06 +0200 Subject: [PATCH 130/211] Works now * open: generation of intermediate certificate files. We do that at several places. But for some reasons I do not understand currently we remove those files. * we don't name the offending certificate --- testssl.sh | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/testssl.sh b/testssl.sh index dbfd4ef..6bd0294 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8314,13 +8314,14 @@ certificate_info() { local certificate_list_ordering_problem="${12}" local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt" + local badocspcerts="${TESTSSL_INSTALL_DIR}/etc/bad_ocsp_certs.txt" local -i lineno_matched=0 local cert_keyusage cert_ext_keyusage short_keyAlgo local outok=true local expire days2expire secs2warn ocsp_uri crl local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn local issuer_DC issuerfinding cn_nosni="" - local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial + local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial cert local policy_oid local spaces="" local -i trust_sni=0 trust_nosni=0 diffseconds=0 @@ -8342,6 +8343,7 @@ certificate_info() { local response="" local yearstart yearend clockstart clockend y m d local gt_825=false gt_825warn=false + local badocsp=1 if [[ $number_of_certificates -gt 1 ]]; then [[ $certificate_number -eq 1 ]] && outln @@ -8969,15 +8971,22 @@ certificate_info() { out "$indent"; pr_bold " Bad OCSP intermediate" out " (exp.) " jsonID="cert_bad_ocsp" - badocspcerts="${TESTSSL_INSTALL_DIR}/etc/bad_ocsp_certs.txt" -#FIXME: there might be >1 certificate. We parse the file intermediatecerts.pem -# but just raise the flag saying the chain is bad w/o naming the intermediate -# cert to blame. We should have split intermediatecerts.pem e.g. into -# intermediatecert1.pem, intermediatecert2.pem before - badocsp=1 - for pem in "$TEMPDIR/intermediatecerts.pem"; do - hash=$($OPENSSL x509 -in "$pem" -outform der 2>/dev/null | $OPENSSL dgst -sha256 -binary | $OPENSSL base64) +# There might be >1 certificate, so we split intermediatecerts.pem e.g. into +# intermediatecert1.crt, intermediatecert2.cert. +#FIXME: This is redundant code. We do that elsewhere, e.g. before in extract_certificates() +# and run_hpkp() at least but didn't keep the result +# +#FIXME: We just raise the flag saying the chain is bad w/o naming the intermediate +# cert to blame. + + awk -v n=-1 "{start=1} + /-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} } + inc { print > (\"$TEMPDIR/intermediatecert\" n \".crt\") } + /---END CERTIFICATE-----/{ inc=0 }" "$TEMPDIR/intermediatecerts.pem" + + for cert in $TEMPDIR/intermediatecert?.crt; do + hash=$($OPENSSL x509 -in "$cert" -outform der 2>/dev/null | $OPENSSL dgst -sha256 -binary | $OPENSSL base64) grep -q "$hash" "$badocspcerts" badocsp=$? [[ $badocsp -eq 0 ]] && break @@ -8986,6 +8995,7 @@ certificate_info() { prln_svrty_medium "NOT ok" fileout "${jsonID}${json_postfix}" "MEDIUM" "NOT ok is/are intermediate certificate(s)" else + prln_svrty_good "Ok" fileout "${jsonID}${json_postfix}" "OK" "intermediate certificate(s) is/are ok" fi From 851cd564e625c595412c34fdf3c7c7489962aa6f Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 15 Jul 2020 11:52:19 -0400 Subject: [PATCH 131/211] Check for bad OCSP intermediate certificates This commit checks whether any intermediate certificates provided by the server include an extended key usage extension that asserts the OCSP Signing key purpose. This commit replaces #1680, which checks for such certificates by comparing the server's intermediate certificates against a fixed list of known bad certificates. --- etc/bad_ocsp_certs.txt | 293 ----------------------------------------- testssl.sh | 7 +- 2 files changed, 2 insertions(+), 298 deletions(-) delete mode 100644 etc/bad_ocsp_certs.txt diff --git a/etc/bad_ocsp_certs.txt b/etc/bad_ocsp_certs.txt deleted file mode 100644 index e3b84f5..0000000 --- a/etc/bad_ocsp_certs.txt +++ /dev/null @@ -1,293 +0,0 @@ -wXYR23sxE2HiDXuCMarfR4vLf/oL5YXAbeIwkiPC74o= -/bNF0mk3ftnTQwi7eIVjKzDmH46vt+tedfgCTV/U4Yc= -16BtRir8gNcRg9lKOYstzIJYNefGbaXkVAVXLwSwWl0= -gptouX8R3uNEhXrBLrQM5QLuUmARIwFv3r8UIAkaQEg= -VEULJ4BS+pGRFgDLmPMQCg+1gVKNj+q5i2/++KSMmY4= -c50MOnG2w3fL0cQIrnzBlzLCy++GINY7Fsn9AYXfmnE= -VQdJ/141xAfVFdmLMnUEvEhP2q4opxOv5OIoKYa+BOg= -Qavo+alRqJkPvc0+FM9HnUJkAbEV8BcBibZl6UDDYyw= -iKuWH2T5No9MTRMH9Z1OWcXlcPhSpsUROo/yjgQxUVc= -zXQZjUwj5HAd6leYkjIbnk9HoIvYN0cQuJmq0UlaSzU= -vEicbcK94lP1hto0E01mQLi1g6MSCJzhCTPEvAP8XzM= -89kW0bcyAfBJ7afll/4KXM1XEaaVj7wLem9/pk96/kc= -h6rZeKX+LulB5AaPmWPqKb2qWIeNJBIkmoLNIvgSbYI= -REzv7dg/YxXF7Iay8c1jsdvyOHz6K+sPtAAVPyr5QI8= -6keT4uuBTUMmH4Borv3ng7g358AhrBnI6bCFUfLWyD8= -DLZoT2AcQW+ZKolAmRSjKgwgmXFodfX3jsDEaxtXCZw= -fTW/BMhXKCtCLoUeqKYit10uzLq2XVumJZjB2koR1QI= -TefMrLUJmUJkSVDyzIYTQBJWXGFNUap9MIf6wZaVihQ= -kO5UjrrKyrQCB6YaN4zhhrlNJK58Vb/IMGXqlgcuKzg= -EaJ2cYciZURctyWOsoRO5hTRR3e59vc76VMhIvIfrQ0= -HLRwcoz1bzAgA7sOTrBiQU+hHU+X4/BhFwyWyIBx1xE= -ysBfTwUIEJkybc8nxNlwqKPqkU5nkq2swQSWEcntQ7o= -7AICWhoVeywCrEn1Wai2zwchJvg3lJn658Uu0YPakBI= -VvMhZ/AD2PLLc4qzhsNcoVJlzCmpjuEGbNPhrFVsoVw= -7r7fhG8dug6Iqba5qw/2Xwl/KEwGpkqTnujFyebzE/Q= -U1cNBeQNxi+d+R0Vr7AVtWqqaAwgzBaObVfZRsnOJoQ= -9kDlZDxAwfMp4QBDjijJV2ka+opT5AWjJvev63DCO8E= -GwRlNTeOB9EKzaok7vziBCC7mllhFOtHXKaWNXdT6SU= -fTOuYYzWJVM3fSU9LryihdhOmKkk2J+Y1L5P7jH5Kqg= -wC2KMO1psvhk7Y+xpjo+clUoiSDKKUvcow9jiY+5GVw= -BOy6j5K/90WMSl58aSYfx+LvUtWvVPvdkrFxQbvgZR8= -48UkTRX44LA09QCQO32hHFfBZWF1uGYIx/zcVh0IG/Y= -lA0vISoqOcyEvULQ9txPe6TEd+elqZIslrn17BTkpsg= -WXK5vUcQF9XzJwW+6RVwNiZXX4snCjnBwxLH+SRsENQ= -q/OAPNKTniaAPlIoCoH2fEbD4O51/Nux4w+wOjIaz60= -N9IgpsdSJ5kCEZE0nBg/kXvhvoYmz5JrD9bgqGge4DE= -QqtNnxgJRU6+wkXY2wb/YaqCibBaJj3+6WYtrJFmYEM= -Bb+2YF1IUWpXG6+af/dTdhMEcNpe5/9oTCZy6qDAyK0= -/UwrmT4DVukNT5+9I2He8aSYN4zuzrkt12/grX4sexY= -k5BxSx2Qn6PXDdx2gbOPB+1OY1bLXHGRXRvc1I/jNfg= -MafFefJNVWLNsgP6Fanyw/9dwfLmv3wL2V+8sBFMjSE= -jlzqHRASUhwnpOEScHArx8mhFW8TUHPWyk5C7ugmJJ4= -Irbr3umwptpcn6ztJ7+dzgmAPCr8EfdrXAvPR7f31WA= -gP3kKCEq8MoKxTHu5u0t89PCpFV9/OhXBw/JR5IumyQ= -P+i+OSoIaEuZ9JfmGMfd9aAqQom/nQjllQRZMb+6gU8= -3Ist7lDdR4qxNcrCac6miFFVepEpq82Y31ITsj2/s80= -UCx6hwNB175n2ybb+s+WR6/YmoVL+IEvpO9ww1bsE+U= -vzmkJB9C1SI2iUSz3FPtnqpax3NeJC4GJ8DdW7pxRIQ= -OuaZ2U6P69rLhtT5DUCQMzNHjmXgZVxDJFEZfjP6B/I= -oloZVGgZ0EgADvnGV3xLzY0hVbHkNGpFmdbIt5eZ1KE= -8YRCvt9wtNFSETVscrZZMyvtA//Tu6evqqvm3p1yMAI= -2IiPSoT3TJdN/7Vzob9bu6zRcTuQUJb46wFQYr85bE0= -BkNqyNDI4SCltfqMK83LLx+AWF55x3t+D40PYWjZxF4= -Uk5bOlWNX/AafEYCahS1jHfPne+6aR6sjkoX+sKjo7Y= -1B0jxRVZ9l5cIlA9enq/XNll7DnGHGmKfZjXl69we0g= -XIDlafzuk/FaK8BDUQKyagT1qh7JkSwToFiBwarVAtI= -3gqS5UNbYTII3ENezHFYvyj0IKk+CpHVlllyBT9SNUk= -IcrbfNgi6hO0luJau4FRx/sA77se5FijtKTzwWvDwW8= -7T23SNbXju/wuUUrxqAuA5A5xVB1pDkyO4uoX7Ibndo= -SyTFIcR2AOg4AKP/DC1Y3ALIF3732ml8WoDB1IZ6Zt0= -XbHSLXBmhPBxnokC+4azRrP8nk0aH5IoOkiLw9GgSEs= -P3hQJbzPrKRwGhc0zg97MM5BBpQsjZL0G3CwZ+1l01Q= -FQc8a73HRpmohRjCelfJVuXiPWypYZ5SGkaMeHPeT4o= -qbVpjFJjvv89YHINwYRMuV0W8G4EJovOO+TWAoKwHvk= -o38cCp3HIpi2H5M8tUxlQ4Y3sm2+29HmuAgnnz2tMOU= -Pq1PcvBvEFSIHSco3gM6jhP63mvRZQhAGOuUPBc3jao= -2FU6KIDpa3qkx0E92QOv09WAUEaV3SahaP1IzOexR0o= -UH22DSY9PQnSg94uOqQ139h3XlK8M1cC44Mru1fsHL0= -Uz/pfrRfztJASeQe/p2yVKXdnZDf1TyVEsYgftsh2Cw= -0o20NeMSEqO9zPh2IPZUS5mpwCMov5g+iC/QYnodEw8= -X2yqRKRmPEQd2SyLZlX7+Xz24daTTbj1+Nl633Az+qw= -X79u1PD4etpl+ueqq0rZ4bWlhpOa7fs5pJJr5/ZcZQw= -hTY6JMsbZubPYkTofSQ9u4MG9gc1fGFMucTCJKDgQ1g= -fggrvFaXaxWdRpZUCpa2AUhhS6m14psgNfeJvs+/Blc= -vs/eEkzt00TZJctV7dpmLZqcBoj6mghwzj27baQxPk4= -T/QE8C4s0AGI8V0cAPS20eOLWjlc+FMU6uuoVbamS3U= -8i22V6GpKYQavKxSZxpc7op9BpWGr4XOFt4rBd2iIlI= -ON7T/2gnV5AIr0iH65aYo8+pJ/qO1Z8GugkPuaY+LXc= -cFYQcjyWE/ZBMYHK33PVEHG9dh/aSRQj4avQAVAbZPM= -lylXMEAxI07Rdnn9y5dVbWFz1fK/Dm5m1hJoDKbndoU= -VnmkMeedTrnulnxg2HA8fHj0Q/cduXFX5DBZ3kLYUN8= -ziMyOQIIdCoaymUTl0xMnbJpHq9FaLUz5KF+1d2pc+Y= -x0It4hzryS7K5r6dzX5xHuZQ0wrtcR+7Dfayx4S2xPs= -OUzLvAs1lcoN7UAHL6+YY27QIWma0olFZIeRZbW11Mk= -xhkwd8YYnR37v4E7h9x8vwSYrPcniHvH7FQyCQbem8g= -ApN5EY5XdSJsVNcYKjZ6JAtRdw9QEbs1F3z9F9myRFo= -ThB8mBtCrL5BwBBn4W1E22SBTUGT5XIxfqBLh8ecR18= -1Ia/o/ANFl7iz2Jw/afQCBfljNot9Polbg8usSLPjwI= -6+h7tBiFAnCfREBVJZq7IrxRuIyQhBmhNVnfyO9mMNE= -rxiY1/Bjh1HAddAULU4qDqcx/GIjJPFT/hvztq/ZrxM= -LgGRdRygy6gcOmM43uGgK41rzE8fgmG4CbzOerrxpD0= -TCQc/j0/+2DKiNawalUqsc8O99jS4I2hUoK1UZLrvSk= -Yy/Wl7rK8e0jJRfsm3Yit8JeFEiwzGJrMyhnGeNRzoo= -OALkJFFveO6sMpqumx9gpBLb4dWwldesncDc3ePB9fs= -8DdiFAXg81ZQfiOfrdZHhC07UIV8PP+ECFkXT3L2/Rg= -OTuLFcq8OIb7LkFkldY8i63Y3K+HVSB2yKCpY3wk3kc= -d+rEdkU8tzIlf/FmpevRZWyx9nO2jijfQXdBM5efoqQ= -X/rEPg3cW0rytpb2vE236R3zFLuP4NBxOgsaetKmj6w= -FHxEf+64YgK1AzFPyvADa+qu9DfDm1azWOxEap0gOH8= -Qs/dpvZguOW0wcQRllpFGTElWeMmL422nS2uF7JrO6M= -e0ZNw4T9saUlwswnntDHz60kvs9yxGp9cJPRV8IXYH4= -rgO5rRcQaih4WDCx3NY2eXxMZNgcuNFhWV26+DQz5kw= -rkGVTarL/T5bn9cHj3tvquYbVZ7Oee+O2FitjAIqejw= -Fmz4lA9HJR1VUo7Z5Z4melkqToo43T1on43LpknpS3M= -0Dnu/3EIjMDxagWo/zxhYQ4UHR6FCsfhH3cT7uiMuVE= -gxB49+Ifpx/iT5HXGOSlcynv5vSHGxJhFdXSc7+tn3Y= -YmQD4Ho5PnB8iKRwRQSRcEsSK2TRGDI5F91NQfwGPDg= -V0k5vjI5lVaapElYqzCJ9jmpyG45ezg+9g6ZRGSbQwo= -8O5ZFO2UxyUtBYtOOYCK7m+o9izwl0+31tKp3xbjqH8= -2/EmjBPmRa31jRYmxDNr0/qFoaTRAyDDHqDn0KZgiJY= -zYmOTBarGJLzY6Rh4RAzPHTp3Us+Z+IjZMzAMOu1718= -5UEx8Tn8I8Kcm9MiLhrnEVYZTK3SgXIt3mEwOXl46GE= -EcYS0zK0wSJCUqb+L4o5bbr0A9+cy2IFbeOZWIBKuC8= -nHyngZxD4CplRlj7NmUL2KPyKpuFNe81fiu8i5IakOI= -v1SpOD9EZi517KIusOR/LCNeXRGKfoluyLZFkWy6aOk= -AaBwYTqusQLERGiiMVWeO2bRmC7GQ1iRkJqeazZkI9I= -prt/LZfwpe6H5EaWrtD6IQWjOw73Dc9ocygcbroIE84= -lu8zwkqLHxbPFw9DIh4X5ir/aQqLAU8kUpv+s49AoNo= -YOh1YzkH5QH7JqEK+9lzAhxTri9gGHFFhxYOg9UuGaI= -3YcG9HnxHbxsapDBI1j/oAo3MfC+0rG2UpBIMI3MEuM= -nk5sCNP756rPK3o/wrrD1swblHU/PJYVIj1qj2txcj4= -JTTsG9dMPBt2YaDnzCjxvfKoLM05Q+6q3Nj+2VBV6tE= -z2TrVpvtIKtWfKZcicZOoeeIxmBHoO8exeS9LlbKwnE= -rUcj6rVaV0U6X1W/W/dvygi1p/I5tCabOqm44Uk1MB0= -nM3gEf9hnDId/o1kkM2N4Thn0ZOpte5qb1rED2HXEcU= -gFp7gGAab/tKveY170dwXq4XYg3vnPr2FGK2LXxLiGo= -cWCg2EGxxcEgoIyS3iMmSD2Q2b/OGYS91v9KtrfSc8M= -qQ+X1rXHYS4CDLvPB0YgDJZ24YKMWoUL5ryIjDRfpLU= -nACcovl+osvygXNgDe9velTFZkWZuKtQDuGIWlbk8nA= -rJWg/X4L9eG+bwKgQvDwmmV6euEnLt7FBatrmmEWeCw= -ZAF9eLXwwExBlzXWpKbVLsHSARUkOw18dFB+JX/6SkA= -OGTWwQAfAMSqgdzbN14qC0348ooaqZ+h84t0vOgtWxg= -Yv3R3U29JpQAZqoDD82kUbK8IUP+zmWoqgP8C9MR8P0= -vL0E1K7ZYsnSWv4M+vhjjOFDFlKYjsUhcynnVZrDxnE= -z4mkHf7l9xdA3vYCc13b8d6+DLgW1zmA2aWDxYgc53g= -OoglMMA+phXl702tvXyGYJEvqT+vUIhxb7Rqjh/6khg= -HJJmkCoxw5QbUG1E0NTQbsnbdlXmX5VXZZ+rdospCxs= -fLiI73QNy/wMIL2kTywmGfbQ1FmPuTLQN9ryeAd3c6U= -WONo7k1hW4iOEcVSsss7Rp8wrEv0jYs3m1EAnAgmQ+w= -yXgWNowrekYIszRNvmhI2L0SEmDC952siskMrhfI5Xw= -O/HkFQPH8CPQ1Mr/vo5RJiwscxC8bZbozI0UOmAK7oA= -PoS6Q0KQhRbndXPAmS8JecoITkaFaB/xlcy6iiKbinY= -jZz9mPUrHCxvTpztPR2ZWSfvsNVjj73wiDTQ9yyikRg= -s5YnQBRBzrQv9sf5g+DyqSMNGHdAPJ9AllyHV86/19Y= -ePE8SR4TAgaDfDqu2lXFby1dYN0NGXkQhD9G1freNek= -4qtqY+hkbg8jKCDtBH29RICWdFYhFJHJGvonr1qVVbk= -AFHJiSTyOb1Jq4yPaAL4uc9K1B6zJy8aNMDNbWHcs9Q= -j0FFvUjMm/ciklkj59LQnMy+mXOlODjKNvlWH3TownE= -LAlx85e6hzewM1hFVpBllJNqzhdTpaS+Xi8RtCukt1k= -UVh8hnv2bzXs5VSgjgpBwTuLvZtZ0mLSBKcDCaZyvuY= -VbDNLMrRjiWXfZBtrJ+ouGQ9eiWOezg38RV6E6wZ0Yc= -/T5EKAyNy+jPjPVVEeBmnIU3lF2h+nRowOpStobdW2g= -dNmS05ELz340uLXNKPkerrT0Hz2mOU14uMQ2ctQ/Tw8= -CrEV3p0Saj1OoQ3fCGPMnYlWdE63tMzat+V9agblhRg= -e4X20IWbJAozYzdKEYHgEgIQR9ztI1KoQfblW10HfqU= -QlIC1Lv9qbeZ0Peqkn2UT0LMpCN8L8GYdbkHXes1piE= -D3UQNcGOHTkunMVXxX6UpV0S+7CG8mpFKeJhNiW/0Tw= -yErgHs0gLa++7h8OZ5ZG3ozNZT14RnGKO19OEpMkKYo= -UrY9G9COg73HI9ex/pYs7BgG5/U/dvLHCFjKNSk6HcE= -4KZw9PEFfpF56dtF4zPON+PuMcNJnxxYSlh72aX1NkA= -oBmBHkNpykxiqqgKFUlhPmD2xc7Tg6+ded+Pjxk/Hf4= -Af1hSHk5SzrhSSUvwVaYZ0FZzlVQZp/5EokuGYNaHkw= -gyJm1rqMv8vyjgYUoB2fTDm45B98h9IHfbtsA4QMqcI= -5z8fGaRFmmBnpF6E21hdbB348SpznXM/WyiZZUbxh1o= -jAFyH2+p0h38KDZvUkNOqLRL+IQ/HEM7llv2WTNE6zY= -+Hiz3yE7CBe/8eXvTozXybV8gP/J+KcwnqRqr1QLrhg= -tA1oOJ/QQcVuKT+j1X19t5LdALYuvf8ZCw4mRAcsk0o= -Q9tljdTkAg+LXGvXEH4V4jNFmiJs0Nd++PcrKxzCmv4= -oK8UGM0U9rJBXsYxbH1Yi7JdQwo9bQJvV94g+WXqvdw= -weRTSNcUy0fbssC5uvm6HyfkIAR82nqLaTVkMATKq5c= -jmkw14oTnzgnFGpZRu+f46dzmbL9DOuwsu0I7hih11g= -2FLeXQmAht/ppvPXKNUmGGVYfEid5nV1PScjdKXW6fw= -6Mpy7LmIXCTqKd4OrJdwQnjSoeWbZmZtMn+wzGvdkS8= -SHR1jWVj4EM7HtzufMxdnCqtjroSvLBwRFS7Tvjq95k= -tc/msLKqhhoLNnwMBTlaU4rUk6nfARVEqO/EaH/bLMg= -uWj7/D7KKFqtw40jZ3JyQf99NzPxJ4+KT1DsyMDvHoA= -/x3SHxpdC0Us2WnPSqVTg1yr4Ck8bHsAnxRaogLALIs= -gGoqp37b08dtj9Bm37XMMxDzWbAQLOksD67BaqQ//wo= -M81TfgCducdYpWhDXAZwb58BE4BU4gud3JPgOXE4qms= -jZkhf/gtYOTfWb6KESFiXN/8TyLyHhJjod8G0twLVA4= -xrclJq9F1orhZnHp8cJFMECNk4+PBEfhEG4vKnDTrSE= -YPBm3Hik4ukpocjtEC7bcH3wMYH4L99Q1TpS2sNVxls= -dkoNhNVVLNWHLHNGTzfwIXXNpwWIECsTraKgGZ/EA+k= -Ko2+vTr9uXK8H3fP576FBoiM/tNe38VMuqY446WrPv8= -W9DA1XmAFWfzOI/mRO95CrMeLbIkbq9MiB5aV9mgpYI= -xbZ5EGlYFS+D+1iG3cQfB4UZPvZ8aXW+PlCfF/KbeoY= -kFjVBl+PPppjqv5c6Jp2RHCw3vnc87nsfQWH+riP66A= -1WAk8MXZI31+CyOFBn2O+I7dro7BmZafXKoszClp2YI= -rCiRmOxpliiAP4hD3WPHZOhWuCdM957bjtjpmXlpf5U= -x0eO33MQG6Eu7N3zSMd0f/94nvtAHMjcrfrglClUPlQ= -4P+T6QVsSO3ca2jpTzFcOiIp6c4krzNjTTTV7wMjMok= -Qf2VNoVpPOZ9DSPMkcoXkmHWuZSLIdO75fet++U5gt4= -3oPj/CRfawSJMeHIE06qypb6p7HvHROV86Ys4BSwUYI= -BmTx7u7RpzVXACAdL0Qx3EOwvOqxKajKv9Tth9BDQe8= -cbPtjO7MwGOM0gIrQm6SF2YnHeYjZ8fNhS7C8eERHnY= -zgJQ8qnxj2jV/5RKTGWfZpL3PgNpzswhX18lfjIlmHg= -t3h0inkrj5HwSwG6/DGjHtfvanEq/4C2YQ2are4get8= -iGWwLhQDkWdWEyLIwewud8R89763MdL+HLusv3LRdyw= -6Va4oVaHH3HUNMrHjr0j1Q79GrkHYc5DbnnQOu0TZkw= -wXOXph2a+dKiKczyYqsdMrSph6E0rlvZ3k/BVfAN0W4= -TzBNg2b/kJHt121TO+1Vko5zt6qtUuNbhudip38EaU4= -DzMgaSsXqRbOVPfk2nv+Ui+zGxrCHgiHdrN8B6NO4qg= -4MJ6zLmueGBbYeeCbAIdkczS92mljEvW2lRgC5njVDg= -FslKjMwVyYOCW1vT8txo1pTIoyDyMo7NjJylfeUdoOU= -AfiXESH0ED0wvkI1zX3A7ubGrhL8p3UISOoOLhP8JCg= -JbrMQKU5K4Kq3qBJA5BaRnEh8oIg5vL34P6YKq/BT6Y= -+5U8T8AEWEbQJJHI7M84e6NDR8F6uw6m1Z9t5NLx6gQ= -+1Tuqbzo6eqXghVPPUFCd/twn0m5R9c5eKwnhUbCzgM= -yKk01qjoRfemX1yPcf5E9Si4XjJldaDWeTnHTYDNF9g= -HJQqIqAWoeVVna537FzoZx+YrgukrC3CWUGOjh6flK0= -z3O1LQQbcwm0OdFiR0FLkMnSbkTjh0ijZQDVgptRh/k= -v17fvuuFmZxRacvz9NtjtnmtLh4icvw3lfn5kh5tBIc= -oBM75bFOAjEKLUvqtgEJTxGU7ovW/Snd/nuTR0Z8Luw= -ygBap14zWUvR3txYTh505RmOux3oiSntTz4un/zjhzs= -I6dHBNd6A8/T/xnmLFAISCFObGD9Kq733Oeo+e6fkjI= -O5Zo9Z9V+jg4/Co7gLf5tbE9Gkbx6qbgvP8ExUGYBWw= -WkBVNcESoKga8NKsyjw/m8Gmd1hs28Yzy09fd44aNVA= -kVPkQg3cfrTm6GSqA3fa30CC7NNQUhE2OOBdPClrwAY= -3QOOh+C00sNpaA0954Y4qzn8HX5QYymWkhEBdo241Ng= -TmPxQkAahPikc9bd7jQaFh+tqG00MMjCxTRTZBPZ25c= -cW824XTQWVrowV39agxJjpe2plWz8g/lSIyo+exgktY= -mk45jSqmA/7RCGPANi4TkNl+wS+gJ4/XgqqzzdV9wh0= -O7YMQ5NGQUpA4twtHW9WYiV40rHygyR2fBSQFaY/80o= -9dLSumgXp6mqDiE1S78Ob5XF4ofuiM8vJ58P/sTtrBU= -cBtDKsDN1NnPlbS4hMMr9cypDUTgFhq9E7k01o44BHI= -xMfENr2I6OaNsAKX34OsyBnhmGOboAUiyOMkWHaJhSM= -XN2AnPRPX4Zl6sFQVVBMWwa3h6wYKUUFvbq0p35Q13Y= -RgOPYyYijNtWYZxSJmYT2gTIykmeDQOw7c/8EQ1c/HA= -7cc0xQFQHceidEj6AsdJMfhXi/KXsXPzS4QegsZpGSY= -J9b9r4ApeEbf7/guf1i5pIrJ4+6ToRKxu+JD7hqXRHw= -yBksMve0nH8yocoAFZWn+eNsnnIFjW6qG6t3UqjBZxg= -vkCBOGmrJ6Bx0SrWqIMFg+vDthjj8jRjWfSxGhyUNO4= -HoZCeMIIgbZxwMbS4UthFQrR8Tz5LG7BS1UNy8R+FUE= -VMN6joU/0dY3jTeLk5MH7DIaMcwaWonnGAYzvBPxh2I= -nomO0D+kaWlpDa1zxylmdQRf+bWgEAo5m+uENamPUYU= -Sw0TktORVzUyB6ZMyxRoPd6dLO0ftYsW4Di+VwfCeBM= -eqRdb1sU2rHGhEwZwoBOFLWBHm7eHwKwrvBlp7NZxo8= -lMZj6epcJ+5PZBJ/m0JYY+mRqeFWwH3xoAgDrjF2QWI= -Logg3A6vrj1tKFwFfs4URws3dDiwAs7dTHK080OlT0M= -TnB4Z5RqwFNDxrqP8SHqZqdYA3kTJXqO5JdDUNOaEDQ= -wlxO28NuP7fD2Te+6fLSnjavsHz6MYgmLg1f3JGeDXc= -Xn/LnJe9pWmTsWWNEgIydh1mWjZEU0MA+mpb7F4NV5U= -ETE43XshZyWEAjji1+7ss3ONsTkGSyTLhT/CcKSeYFc= -vjPRxX693ZJ7V722BL5Fe1Uv5Wjn89y6CTw57Rwwojk= -g/yJGzUNng1+vm3Spr/j0LD0ZT/KBIYVpd7rvAOaP2Y= -VyZLgqhk26HBHvP4CruUysNmBmKwwi9XH/mTs/vPdvs= -dKvl5czrdUkf9yxM8yVAXYrb/jkOGJz0MLpg5ieYh44= -wv6s1nSHjHsMIyWi7O0KMz23eAqG3+w3WBAO/AEBxmU= -ndwuDVW0YeDHMigoLfVrK+8iTKI4VoHRe26MB3hSVzw= -MG6XOeNFj/RUaHe3BLLjkF5YsjXWTjL08CaskbcpXRU= -/ToPPdRIAJK21FBHPeuSAaCzCKiAeDOjxzj4oH64HtM= -Cqny59lccYt9HrfM29AWToYFeunWaSK8YPmQP5Sg8O8= -9XCaLS9otTv29kW7F4rflTRvif2lxjv94IBComSSqrI= -66NMexCWcWFMNn4d4HUSTDlUzhn4X6z2EJDsMZ9/Gn8= -Eo3tGorWDCS0JU4x25T8Q5K/k+1UNEcqpDoLmFYQYGg= -txawif5OU9Gi73ulesheaOxyLPYQUsJaWWJq07FcX0A= -al9MFnjKZeWfBg1Xzf9mUGUxSGHVOo59FFDKktlsoQI= -q8hnBsmNa/ZzcvkI7AGt9jGxkdczron4ND6wR7EIFEs= -QEfJ1pJgwHITvLhgin7F4oOKVrefZ4R4EurAd40NJ/E= -kl7n1aIq1/vpurVNfI0LmnT341qK9q9kXi6MNRmnCS8= -8GjeqhjMAtWovjXLgzgyeRApH25i5yFqk0dkoaukqAA= -sf466/ljp4gOdLCwVWaB6osczOPmmn07EKaKy+huSKE= -//4HdQP9cvDlM4sKe04hjn0f+C5JPn6FKuUaocdYXRc= -lcanR90Lx1WhlBgn6JS4CDWSJBt5JUHi6xsw+5sT9X8= -DTF2xY8yGqNMV8jffBfR9OdseX7BFsnx1pd0jtH859k= -DW5GeE87aU6cdQZ4ZBe8b4f50vc9GbXoCBYSshE3t2Y= -l4vPOcPDqs7+EEj6A2AoPcLr+lFQAkxeN4UU0+decpU= -LYGa85fiidxBcJxKr22jsZBHScN6mVhgdFgDcW19VpA= -oaBOTPxW/RkX60gfxGYpouRmVnVvoI4iSH9Utk4co+c= -Zj/elPiDah/r2DvoMQl5MS1l/4wbcWOU5o9B2COWwfg= -SDlfccwm9kJ0vQbH6xWR+dTsYrZPpsFlMfPLcsJGnYI= -+AhlYjZyW1ZvByHFGBWKZ1bJaQPLcetI3dqO3f6g8mw= -TngnF2hJQIjtH5uycSUxq10TjGxZtGZQ0oWzWFjmexM= -QqIfNyhYBPLnNgdctUh0Nozw4hEAiLPxOEEu7IhZCVQ= -RnFuv2oirqHfH5fmKl73CVEnzkVQtiQuumkHr8XEgs8= -iH1/Het/BxLIWvq/UMrfib8C6xPlUsv1LXtd8/IdsNo= -VbgN72B/iOMcWEBn0J1DNE2UEXuvZINlhHsc5bZ2d8k= -/DqYk44u07024NQfGFF2STIpBrLmszaQiRMlquzRlkQ= -UjV1BHNxu6iCY+WrXe2PawaVxdKhkqbdEUBsR2ujxy8= -KlCKX+YXPE6PASZpShpqcdkABFH6MdsDO4cFiQRB7MU= -/S8zJI85ZFZGh5gCNaXwvLOrVi5H9AT3QuUzQsU3UoA= -U4pFqvkAPU05iLv8+fhZsyXOX3ddwGASBQWFF/m8FeU= -50rUSw1W7r45uxhcUqub7+T3WJm2qBQW6RA9XCm/J/s= -53rALbbqoOmmtXOpmHDG3029uDOd2jvEB4b3DivX9/Y= -HpU+FRI6leRwRL+vBrGXT7mXa4T0Nx69d/itge/QXAA= -5Xu/UHn5gpO0FC80iXLmjjUeUrkDc/BfRvCcyzCvW/A= -vUQgxZKm9/JSromBJOAC8bnP4fJTiGTVazSntN+zH8g= -CE7KcKm0Dk4VY+NXdrcW5BCpcrsn7VHCSMSNMkL6ja8= -bsjTVRvQRUsMxLnbh2Nmrgh2TF/PFbAPlBG94w1TNq4= -qONS2wyOJ7rlg9AVFG6Led7gkdVjj1g6DEQvO/Vd7rM= -LRskT6zq+tlHVbXpmqv3rJHFmdpzv6Rh7uuvtLlptck= -6tyA999jBWDMGzU/aOpmAVnOlm4i1ARYh4B4fAR20OQ= -IuXm21yuQo7alq7QVk4rl3DsoswCUVjjep3e7eMfReU= diff --git a/testssl.sh b/testssl.sh index 6bd0294..f78de7b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8314,7 +8314,6 @@ certificate_info() { local certificate_list_ordering_problem="${12}" local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt" - local badocspcerts="${TESTSSL_INSTALL_DIR}/etc/bad_ocsp_certs.txt" local -i lineno_matched=0 local cert_keyusage cert_ext_keyusage short_keyAlgo local outok=true @@ -8986,10 +8985,8 @@ certificate_info() { /---END CERTIFICATE-----/{ inc=0 }" "$TEMPDIR/intermediatecerts.pem" for cert in $TEMPDIR/intermediatecert?.crt; do - hash=$($OPENSSL x509 -in "$cert" -outform der 2>/dev/null | $OPENSSL dgst -sha256 -binary | $OPENSSL base64) - grep -q "$hash" "$badocspcerts" - badocsp=$? - [[ $badocsp -eq 0 ]] && break + cert_ext_keyusage="$($OPENSSL x509 -in "$cert" -text -noout 2>/dev/null | awk '/X509v3 Extended Key Usage:/ { getline; print $0 }')" + [[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break done if [[ $badocsp -eq 0 ]]; then prln_svrty_medium "NOT ok" From 17ee0245b5f4eb34ef13759773ce741e3a00a9ce Mon Sep 17 00:00:00 2001 From: David Cooper Date: Wed, 15 Jul 2020 11:53:49 -0400 Subject: [PATCH 132/211] Speed up intermediate certificate extraction This commit speeds up extraction of intermediate certificates by using Bash commands rather than awk. --- testssl.sh | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/testssl.sh b/testssl.sh index f78de7b..2ac1f95 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8303,15 +8303,16 @@ certificate_info() { local -i certificate_number=$1 local -i number_of_certificates=$2 local cert_txt="$3" - local cipher=$4 - local cert_keysize=$5 - local cert_type="$6" - local ocsp_response_binary="$7" - local ocsp_response=$8 - local ocsp_response_status=$9 - local sni_used="${10}" - local ct="${11}" - local certificate_list_ordering_problem="${12}" + local intermediate_certs="$4" + local cipher=$5 + local cert_keysize=$6 + local cert_type="$7" + local ocsp_response_binary="$8" + local ocsp_response=$9 + local ocsp_response_status=${10} + local sni_used="${11}" + local ct="${12}" + local certificate_list_ordering_problem="${13}" local cert_sig_algo cert_sig_hash_algo cert_key_algo cert_spki_info local common_primes_file="$TESTSSL_INSTALL_DIR/etc/common-primes.txt" local -i lineno_matched=0 @@ -8320,7 +8321,7 @@ certificate_info() { local expire days2expire secs2warn ocsp_uri crl local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn local issuer_DC issuerfinding cn_nosni="" - local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial cert + local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial intermediates cert local policy_oid local spaces="" local -i trust_sni=0 trust_nosni=0 diffseconds=0 @@ -8979,13 +8980,14 @@ certificate_info() { #FIXME: We just raise the flag saying the chain is bad w/o naming the intermediate # cert to blame. - awk -v n=-1 "{start=1} - /-----BEGIN CERTIFICATE-----/{ if (start) {inc=1; n++} } - inc { print > (\"$TEMPDIR/intermediatecert\" n \".crt\") } - /---END CERTIFICATE-----/{ inc=0 }" "$TEMPDIR/intermediatecerts.pem" - - for cert in $TEMPDIR/intermediatecert?.crt; do - cert_ext_keyusage="$($OPENSSL x509 -in "$cert" -text -noout 2>/dev/null | awk '/X509v3 Extended Key Usage:/ { getline; print $0 }')" + intermediates="$intermediate_certs" + while true; do + [[ "$intermediates" =~ \-\-\-\-\-\BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break + intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}" + cert="${intermediates%%-----END CERTIFICATE-----*}" + intermediates="${intermediates#${cert}-----END CERTIFICATE-----}" + cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----" + cert_ext_keyusage="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert" | awk '/X509v3 Extended Key Usage:/ { getline; print $0 }')" [[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break done if [[ $badocsp -eq 0 ]]; then @@ -9712,7 +9714,7 @@ run_server_defaults() { echo "${previous_hostcert[i]}" > $HOSTCERT echo "${previous_intermediates[i]}" > $TEMPDIR/intermediatecerts.pem echo "${previous_hostcert_issuer[i]}" > $TEMPDIR/hostcert_issuer.pem - certificate_info "$i" "$certs_found" "${previous_hostcert_txt[i]}" \ + certificate_info "$i" "$certs_found" "${previous_hostcert_txt[i]}" "${previous_intermediates[i]}" \ "${tested_cipher[i]}" "${keysize[i]}" "${previous_hostcert_type[i]}" \ "${ocsp_response_binary[i]}" "${ocsp_response[i]}" \ "${ocsp_response_status[i]}" "${sni_used[i]}" "${ct[i]}" \ From bd856e2adaf31f7063173052368b3123a1688290 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 16 Jul 2020 07:57:27 -0400 Subject: [PATCH 133/211] Save intermediate certificates for more use As there as suggestions to check intermediate certificates for things such as expiration date, this commit saves the text versions of each of the intermediate certificates so that they are available to extract additional information. --- testssl.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index 2ac1f95..beb479d 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8303,7 +8303,7 @@ certificate_info() { local -i certificate_number=$1 local -i number_of_certificates=$2 local cert_txt="$3" - local intermediate_certs="$4" + local intermediates="$4" local cipher=$5 local cert_keysize=$6 local cert_type="$7" @@ -8321,13 +8321,14 @@ certificate_info() { local expire days2expire secs2warn ocsp_uri crl local startdate enddate issuer_CN issuer_C issuer_O issuer sans san all_san="" cn local issuer_DC issuerfinding cn_nosni="" - local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial intermediates cert + local cert_fingerprint_sha1 cert_fingerprint_sha2 cert_serial cert + local -a intermediate_certs=() local policy_oid local spaces="" local -i trust_sni=0 trust_nosni=0 diffseconds=0 local has_dns_sans has_dns_sans_nosni local trust_sni_finding - local -i certificates_provided + local -i i certificates_provided=0 local cnfinding trustfinding trustfinding_nosni local cnok="OK" local expfinding expok="OK" @@ -8980,14 +8981,20 @@ certificate_info() { #FIXME: We just raise the flag saying the chain is bad w/o naming the intermediate # cert to blame. - intermediates="$intermediate_certs" + # Store all of the intermediate certificates in an array so that they can + # be used later (e.g., to check their expiration dates). while true; do [[ "$intermediates" =~ \-\-\-\-\-\BEGIN\ CERTIFICATE\-\-\-\-\- ]] || break intermediates="${intermediates#*-----BEGIN CERTIFICATE-----}" cert="${intermediates%%-----END CERTIFICATE-----*}" intermediates="${intermediates#${cert}-----END CERTIFICATE-----}" cert="-----BEGIN CERTIFICATE-----${cert}-----END CERTIFICATE-----" - cert_ext_keyusage="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert" | awk '/X509v3 Extended Key Usage:/ { getline; print $0 }')" + intermediate_certs[certificates_provided]="$($OPENSSL x509 -text -noout 2>/dev/null <<< "$cert")" + certificates_provided+=1 + done + certificates_provided+=1 + for (( i=0; i < certificates_provided-1; i++ )); do + cert_ext_keyusage="$(awk '/X509v3 Extended Key Usage:/ { getline; print $0 }' <<< "${intermediate_certs[i]}")" [[ "$cert_ext_keyusage" =~ OCSP\ Signing ]] && badocsp=0 && break done if [[ $badocsp -eq 0 ]]; then @@ -9121,7 +9128,6 @@ certificate_info() { fileout "cert_validityPeriod${json_postfix}" "INFO" "No finding" fi - certificates_provided=1+$(grep -c "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TEMPDIR/intermediatecerts.pem) out "$indent"; pr_bold " # of certificates provided"; out " $certificates_provided" fileout "certs_countServer${json_postfix}" "INFO" "${certificates_provided}" if "$certificate_list_ordering_problem"; then From 0d9ca76f37bbca1f258498e2ebeba63343407432 Mon Sep 17 00:00:00 2001 From: Magnus Larsen Date: Sat, 18 Jul 2020 21:14:38 +0200 Subject: [PATCH 134/211] Added complete json/csv output for rating --- testssl.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/testssl.sh b/testssl.sh index d5b3c3b..7341d08 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20820,6 +20820,14 @@ run_rating() { pr_bold " Cipher Strength"; out " (weighted) "; outln "0 (0)" pr_bold " Final Score "; outln "0" pr_bold " Overall Grade "; prln_svrty_critical "$GRADE_CAP" + + fileout "protocol_support_score" "INFO" "0" + fileout "protocol_support_score_weighted" "INFO" "0" + fileout "key_exchange_score" "INFO" "0" + fileout "key_exchange_score_weighted" "INFO" "0" + fileout "cipher_strength_score" "INFO" "0" + fileout "cipher_strength_score_weighted" "INFO" "0" + fileout "final_score" "INFO" "0" fileout "grade" "CRITICAL" "$GRADE_CAP" else ## Category 1 @@ -20855,6 +20863,8 @@ run_rating() { let c1_wscore=$c1_score*30/100 # Gets the weighted score for category (30%) pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)" + fileout "protocol_support_score" "INFO" "$c1_score" + fileout "protocol_support_score_weighted" "INFO" "$c1_wscore" ## Category 2 if [[ $KEY_EXCH_SCORE -le 40 ]]; then @@ -20867,6 +20877,8 @@ run_rating() { let c2_wscore=$c2_score*30/100 pr_bold " Key Exchange "; out " (weighted) "; outln "$c2_score ($c2_wscore)" + fileout "key_exchange_score" "INFO" "$c2_score" + fileout "key_exchange_score_weighted" "INFO" "$c2_wscore" ## Category 3 @@ -20899,11 +20911,14 @@ run_rating() { let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%) pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)" + fileout "cipher_strength_score" "INFO" "$c3_score" + fileout "cipher_strength_score_weighted" "INFO" "$c3_wscore" ## Calculate final score and grade let final_score=$c1_wscore+$c2_wscore+$c3_wscore pr_bold " Final Score "; outln $final_score + fileout "final_score" "INFO" "$final_score" # Calculate the pre-cap grade if [[ $final_score -ge 80 ]]; then From 86c730b74e53d1148748eb69b62fbba75350a9a5 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 20 Jul 2020 11:34:44 +0200 Subject: [PATCH 135/211] Polish completion of json/csv output To be more consistent with the screen output: * grade --> overall grade * add rating doc url --- testssl.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/testssl.sh b/testssl.sh index aee635a..a3c7d04 100755 --- a/testssl.sh +++ b/testssl.sh @@ -20877,6 +20877,7 @@ run_rating() { pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" outln fileout "rating_spec" "INFO" "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)" + fileout "rating_doc" "INFO" "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide" # No point in calculating a score, if a cap of "F", "T", or "M" has been set if [[ $GRADE_CAP == F || $GRADE_CAP == T || $GRADE_CAP == M ]]; then @@ -20893,7 +20894,7 @@ run_rating() { fileout "cipher_strength_score" "INFO" "0" fileout "cipher_strength_score_weighted" "INFO" "0" fileout "final_score" "INFO" "0" - fileout "grade" "CRITICAL" "$GRADE_CAP" + fileout "overall_grade" "CRITICAL" "$GRADE_CAP" else ## Category 1 # get best score, by searching for the best protocol, until a hit occurs @@ -21017,22 +21018,22 @@ run_rating() { pr_bold " Overall Grade " case "$final_grade" in A*) prln_svrty_best $final_grade - fileout "grade" "OK" "$final_grade" + fileout "overall_grade" "OK" "$final_grade" ;; B) prln_svrty_medium $final_grade - fileout "grade" "MEDIUM" "$final_grade" + fileout "overall_grade" "MEDIUM" "$final_grade" ;; C) prln_svrty_medium $final_grade fileout "grade" "MEDIUM" "$final_grade" ;; D) prln_svrty_high $final_grade - fileout "grade" "HIGH" "$final_grade" + fileout "overall_grade" "HIGH" "$final_grade" ;; E) prln_svrty_high $final_grade - fileout "grade" "HIGH" "$final_grade" + fileout "overall_grade" "HIGH" "$final_grade" ;; F) prln_svrty_critical $final_grade - fileout "grade" "CRITICAL" "$final_grade" + fileout "overall_grade" "CRITICAL" "$final_grade" ;; esac fi From a51a0a73a76571bfd0a072f180437d99ed78caab Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 20 Jul 2020 11:43:52 +0200 Subject: [PATCH 136/211] Squash "No engine or GOST support via engine..." This is a legacy warning and seems only needed in a very few cases whereas in other few cases we don't issue such warnings. So to be consistent it's right to remove this message as it confuses users unnecessarily, It'll appear in debug mode though. See https://github.com/drwetter/testssl.sh/issues/1119#issuecomment-656271849 --- testssl.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/testssl.sh b/testssl.sh index aee635a..cd32a8f 100755 --- a/testssl.sh +++ b/testssl.sh @@ -19208,8 +19208,7 @@ initialize_engine(){ export OPENSSL_CONF='' return 1 elif $OPENSSL engine gost -v 2>&1 | grep -Eq 'invalid command|no such engine'; then - outln - pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln + [[ $DEBUG -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln fileout_insert_warning "engine_problem" "WARN" "No engine or GOST support via engine with your $OPENSSL" export OPENSSL_CONF='' return 1 @@ -19218,8 +19217,7 @@ initialize_engine(){ # to suppress the warning (confuses users), see #1119 # https://github.com/openssl/openssl/commit/b524b808a1d1ba204dbdcbb42de4e3bddb3472ac if ! grep -q 'using the .include directive' /etc/ssl/openssl.cnf; then - outln - pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln + [[ $DEBUG -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln fi fileout_insert_warning "engine_problem" "WARN" "No engine or GOST support via engine with your $OPENSSL" # Avoid clashes of OpenSSL 1.1.1 config file with our openssl 1.0.2. This is for Debian 10 From 161567f9d29d2bb8872e5e201268b141e6dc4e02 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 20 Jul 2020 20:13:41 +0200 Subject: [PATCH 137/211] add quotes --- testssl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testssl.sh b/testssl.sh index cd32a8f..0d933cb 100755 --- a/testssl.sh +++ b/testssl.sh @@ -19208,7 +19208,7 @@ initialize_engine(){ export OPENSSL_CONF='' return 1 elif $OPENSSL engine gost -v 2>&1 | grep -Eq 'invalid command|no such engine'; then - [[ $DEBUG -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln + [[ "$DEBUG" -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln fileout_insert_warning "engine_problem" "WARN" "No engine or GOST support via engine with your $OPENSSL" export OPENSSL_CONF='' return 1 @@ -19217,7 +19217,7 @@ initialize_engine(){ # to suppress the warning (confuses users), see #1119 # https://github.com/openssl/openssl/commit/b524b808a1d1ba204dbdcbb42de4e3bddb3472ac if ! grep -q 'using the .include directive' /etc/ssl/openssl.cnf; then - [[ $DEBUG -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln + [[ "$DEBUG" -ge 1 ]] && outln && pr_warning "No engine or GOST support via engine with your $OPENSSL"; outln fi fileout_insert_warning "engine_problem" "WARN" "No engine or GOST support via engine with your $OPENSSL" # Avoid clashes of OpenSSL 1.1.1 config file with our openssl 1.0.2. This is for Debian 10 From 5b44e43ec4ce1a22bc0bf1e36f430410fcbe2c97 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:47:40 +0000 Subject: [PATCH 138/211] Fix grammar --- doc/testssl.1.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/testssl.1.html b/doc/testssl.1.html index af08013..57a89ae 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -303,7 +303,7 @@ Also for multiple server certificates are being checked for as well as for the c

    --c, --client-simulation This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site. For the latter case the protocol, cipher and curve is displayed, also if there's Forward Secrecy. testssl.sh uses a handselected set of clients which are retrieved by the SSLlabs API. The output is aligned in columns when combined with the --wide option. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS.

    -

    -g, --grease checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This checks doesn't run per default.

    +

    -g, --grease checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This check doesn't run per default.

    VULNERABILITIES

    From e8d2992add9e0af07701c442b415415d3da179d0 Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:48:15 +0000 Subject: [PATCH 139/211] Fix grammar --- doc/testssl.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 6755235..c246c7f 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -215,7 +215,7 @@ Also for multiple server certificates are being checked for as well as for the c `--c, --client-simulation` This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site. For the latter case the protocol, cipher and curve is displayed, also if there's Forward Secrecy. testssl.sh uses a handselected set of clients which are retrieved by the SSLlabs API. The output is aligned in columns when combined with the `--wide` option. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS. -`-g, --grease` checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This checks doesn't run per default. +`-g, --grease` checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This check doesn't run per default. From e6c5507b20361de23ccfc901cb672149f659abcf Mon Sep 17 00:00:00 2001 From: a1346054 <36859588+a1346054@users.noreply.github.com> Date: Sun, 2 Aug 2020 21:54:27 +0000 Subject: [PATCH 140/211] Fix grammar --- doc/testssl.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 834d339..25a4471 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -334,7 +334,7 @@ Security headers (X\-Frame\-Options, X\-XSS\-Protection, Expect\-CT,\.\.\. , CSP \fB\-\-c, \-\-client\-simulation\fR This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site\. For the latter case the protocol, cipher and curve is displayed, also if there\'s Forward Secrecy\. testssl\.sh uses a handselected set of clients which are retrieved by the SSLlabs API\. The output is aligned in columns when combined with the \fB\-\-wide\fR option\. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS\. . .P -\fB\-g, \-\-grease\fR checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www\.ietf\.org/archive/id/draft\-ietf\-tls\-grease\-01\.txt \. This checks doesn\'t run per default\. +\fB\-g, \-\-grease\fR checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www\.ietf\.org/archive/id/draft\-ietf\-tls\-grease\-01\.txt \. This check doesn\'t run per default\. . .SS "VULNERABILITIES" \fB\-U, \-\-vulnerable, \-\-vulnerabilities\fR Just tests all (of the following) vulnerabilities\. The environment variable \fBVULN_THRESHLD\fR determines after which value a separate headline for each vulnerability is being displayed\. Default is \fB1\fR which means if you check for two vulnerabilities, only the general headline for vulnerabilities section is displayed \-\- in addition to the vulnerability and the result\. Otherwise each vulnerability or vulnerability section gets its own headline in addition to the output of the name of the vulnerabilty and test result\. A vulnerability section is comprised of more than one check, e\.g\. the renegotiation vulnerability check has two checks, so has Logjam\. From 57c49132604a59276d83821b05acb372358d30f4 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 3 Aug 2020 10:43:15 -0400 Subject: [PATCH 141/211] Update GREASE reference The GEASE Internet Draft is now RFC 8701. This commit updates the references. --- doc/testssl.1 | 2 +- doc/testssl.1.html | 2 +- doc/testssl.1.md | 2 +- testssl.sh | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 25a4471..24a5408 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -334,7 +334,7 @@ Security headers (X\-Frame\-Options, X\-XSS\-Protection, Expect\-CT,\.\.\. , CSP \fB\-\-c, \-\-client\-simulation\fR This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site\. For the latter case the protocol, cipher and curve is displayed, also if there\'s Forward Secrecy\. testssl\.sh uses a handselected set of clients which are retrieved by the SSLlabs API\. The output is aligned in columns when combined with the \fB\-\-wide\fR option\. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS\. . .P -\fB\-g, \-\-grease\fR checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www\.ietf\.org/archive/id/draft\-ietf\-tls\-grease\-01\.txt \. This check doesn\'t run per default\. +\fB\-g, \-\-grease\fR checks several server implementation bugs like tolerance to size limitations and GREASE, see RFC 8701\. This check doesn\'t run per default\. . .SS "VULNERABILITIES" \fB\-U, \-\-vulnerable, \-\-vulnerabilities\fR Just tests all (of the following) vulnerabilities\. The environment variable \fBVULN_THRESHLD\fR determines after which value a separate headline for each vulnerability is being displayed\. Default is \fB1\fR which means if you check for two vulnerabilities, only the general headline for vulnerabilities section is displayed \-\- in addition to the vulnerability and the result\. Otherwise each vulnerability or vulnerability section gets its own headline in addition to the output of the name of the vulnerabilty and test result\. A vulnerability section is comprised of more than one check, e\.g\. the renegotiation vulnerability check has two checks, so has Logjam\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 57a89ae..9278e3a 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -303,7 +303,7 @@ Also for multiple server certificates are being checked for as well as for the c

    --c, --client-simulation This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site. For the latter case the protocol, cipher and curve is displayed, also if there's Forward Secrecy. testssl.sh uses a handselected set of clients which are retrieved by the SSLlabs API. The output is aligned in columns when combined with the --wide option. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS.

    -

    -g, --grease checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This check doesn't run per default.

    +

    -g, --grease checks several server implementation bugs like tolerance to size limitations and GREASE, see RFC 8701. This check doesn't run per default.

    VULNERABILITIES

    diff --git a/doc/testssl.1.md b/doc/testssl.1.md index c246c7f..73544d2 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -215,7 +215,7 @@ Also for multiple server certificates are being checked for as well as for the c `--c, --client-simulation` This simulates a handshake with a number of standard clients so that you can figure out which client cannot or can connect to your site. For the latter case the protocol, cipher and curve is displayed, also if there's Forward Secrecy. testssl.sh uses a handselected set of clients which are retrieved by the SSLlabs API. The output is aligned in columns when combined with the `--wide` option. If you want the full nine yards of clients displayed use the environment variable ALL_CLIENTS. -`-g, --grease` checks several server implementation bugs like tolerance to size limitations and GREASE, see https://www.ietf.org/archive/id/draft-ietf-tls-grease-01.txt . This check doesn't run per default. +`-g, --grease` checks several server implementation bugs like tolerance to size limitations and GREASE, see RFC 8701. This check doesn't run per default. diff --git a/testssl.sh b/testssl.sh index 0f22e45..cc9a584 100755 --- a/testssl.sh +++ b/testssl.sh @@ -17718,7 +17718,7 @@ run_tls_truncation() { } # Test for various server implementation errors that aren't tested for elsewhere. -# Inspired by https://datatracker.ietf.org/doc/draft-ietf-tls-grease. +# Inspired by RFC 8701. run_grease() { local -i success local bug_found=false @@ -17729,7 +17729,7 @@ run_grease() { local ciph list temp curve_found local -i i j rnd alpn_list_len extn_len debug_level="" local -i ret=0 - # Note: The following values were taken from https://datatracker.ietf.org/doc/draft-ietf-tls-grease. + # Note: The following values were taken from RFC 8701. # These arrays may need to be updated if the values change in the final version of this document. local -a -r grease_cipher_suites=( "0a,0a" "1a,1a" "2a,2a" "3a,3a" "4a,4a" "5a,5a" "6a,6a" "7a,7a" "8a,8a" "9a,9a" "aa,aa" "ba,ba" "ca,ca" "da,da" "ea,ea" "fa,fa" ) local -a -r grease_supported_groups=( "0a,0a" "1a,1a" "2a,2a" "3a,3a" "4a,4a" "5a,5a" "6a,6a" "7a,7a" "8a,8a" "9a,9a" "aa,aa" "ba,ba" "ca,ca" "da,da" "ea,ea" "fa,fa" ) @@ -17823,7 +17823,7 @@ run_grease() { DEBUG="$debug_level" # Check that server ignores unrecognized extensions - # see https://datatracker.ietf.org/doc/draft-ietf-tls-grease + # see RFC 8701 if "$normal_hello_ok" && [[ "$proto" != "00" ]]; then # Try multiple different randomly-generated GREASE extensions, # but make final test use zero-length extension value, just to @@ -17934,7 +17934,7 @@ run_grease() { fi # Check that server ignores unrecognized cipher suite values - # see https://datatracker.ietf.org/doc/draft-ietf-tls-grease + # see RFC 8701 if "$normal_hello_ok"; then list="" for ciph in "${grease_cipher_suites[@]}"; do @@ -17952,7 +17952,7 @@ run_grease() { # Check that servers that support ECDHE cipher suites ignore # unrecognized named group values. - # see https://datatracker.ietf.org/doc/draft-ietf-tls-grease + # see RFC 8701 if [[ "$proto" != "00" ]]; then # Send a ClientHello that lists all of the ECDHE cipher suites tls_sockets "$proto" "$ecdhe_ciphers, 00,ff" "ephemeralkey" @@ -17999,7 +17999,7 @@ run_grease() { # Check that servers that support the ALPN extension ignore # unrecognized ALPN values. - # see https://datatracker.ietf.org/doc/draft-ietf-tls-grease + # see RFC 8701 if "$normal_hello_ok" && [[ -z $STARTTLS ]] && [[ "$proto" != "00" ]]; then for alpn_proto in $ALPN_PROTOs; do alpn+=",$(printf "%02x" ${#alpn_proto}),$(string_to_asciihex "$alpn_proto")" From 5b17bbcf878a1587248e0ee1b484d971a792aea7 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Mon, 3 Aug 2020 11:14:10 -0400 Subject: [PATCH 142/211] Add RFC 8701 to list of RFCs This commit adds RFC 8701 to the list of RFCs in the documentation. --- doc/testssl.1 | 3 +++ doc/testssl.1.html | 1 + doc/testssl.1.md | 1 + 3 files changed, 5 insertions(+) diff --git a/doc/testssl.1 b/doc/testssl.1 index 24a5408..3d1bd53 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -824,6 +824,9 @@ RFC 8143: Using Transport Layer Security (TLS) with Network News Transfer Protoc RFC 8446: The Transport Layer Security (TLS) Protocol Version 1\.3 . .IP "\(bu" 4 +RFC 8701: Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility +. +.IP "\(bu" 4 W3C CSP: Content Security Policy Level 1\-3 . .IP "\(bu" 4 diff --git a/doc/testssl.1.html b/doc/testssl.1.html index 9278e3a..c5e9de3 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -593,6 +593,7 @@ This is to prevent giving out a misleading or wrong grade.

  • RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security
  • RFC 8143: Using Transport Layer Security (TLS) with Network News Transfer Protocol (NNTP)
  • RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
  • +
  • RFC 8701: Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility
  • W3C CSP: Content Security Policy Level 1-3
  • TLSWG Draft: The Transport Layer Security (TLS) Protocol Version 1.3
  • diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 73544d2..30e07aa 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -491,6 +491,7 @@ Please note that for plain TLS-encrypted ports you must not specify the protocol * RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security * RFC 8143: Using Transport Layer Security (TLS) with Network News Transfer Protocol (NNTP) * RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3 +* RFC 8701: Applying Generate Random Extensions And Sustain Extensibility (GREASE) to TLS Extensibility * W3C CSP: Content Security Policy Level 1-3 * TLSWG Draft: The Transport Layer Security (TLS) Protocol Version 1.3 From e8a930088c74d68676ba3d4435291bcf9664a794 Mon Sep 17 00:00:00 2001 From: Dirk Date: Mon, 3 Aug 2020 23:11:00 +0200 Subject: [PATCH 143/211] Better debugging of STARTTLS commands Improved: * readability of my old code * readability of debugging statements * honor $SNEAKY for SMTP greeting * hook (arg2 to starttls_smtp_dialog() ), if we plan to add / replace SMTP greeting at some point --- testssl.sh | 103 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/testssl.sh b/testssl.sh index 0f22e45..019006b 100755 --- a/testssl.sh +++ b/testssl.sh @@ -10409,10 +10409,10 @@ starttls_io() { # Line-based send with newline characters appended (arg2 empty) -# Stream-based send: arg2: +# Stream-based send (not in use currently): arg2: . starttls_just_send(){ if [[ -z "$2" ]] ; then - debugme echo -e "C: $1 plus lf" + debugme echo "C: $1\r\n" echo -ne "$1\r\n" >&5 else debugme echo -e "C: $1" @@ -10436,37 +10436,37 @@ starttls_just_read(){ } starttls_full_read(){ + local cont_pattern="$1" + local end_pattern="$2" + local regex="$3" local starttls_read_data=() local one_line="" local ret=0 - local cont_pattern="$1" - local end_pattern="$2" local ret_found=0 + local debugpad=" > found: " debugme echo "=== reading banner ... ===" if [[ $# -ge 3 ]]; then - debugme echo "=== we'll have to search for \"$3\" pattern ===" + debugme echo "=== we'll have to search for \"$regex\" pattern ===" ret_found=3 fi local oldIFS="$IFS" IFS='' while read -r -t $STARTTLS_SLEEP one_line; ret=$?; (exit $ret); do - debugme echo "S: ${one_line}" + debugme tmln_out "S: ${one_line}" + if [[ $DEBUG -ge 5 ]]; then + echo "end_pattern/cont_pattern: ${end_pattern} / ${cont_pattern}" + fi if [[ $# -ge 3 ]]; then - if [[ ${one_line} =~ $3 ]]; then + if [[ ${one_line} =~ $regex ]]; then ret_found=0 - debugme echo "^^^^^^^ that's what we were looking for ===" + debugme tmln_out "${debugpad} ${one_line} " fi fi starttls_read_data+=("${one_line}") - if [[ $DEBUG -ge 4 ]]; then - echo "one_line: ${one_line}" - echo "end_pattern: ${end_pattern}" - echo "cont_pattern: ${cont_pattern}" - fi if [[ ${one_line} =~ ${end_pattern} ]]; then - debugme echo "=== full read finished ===" + debugme tmln_out "${debugpad} ${one_line} " IFS="${oldIFS}" return ${ret_found} fi @@ -10488,13 +10488,15 @@ starttls_full_read(){ } starttls_ftp_dialog() { - debugme echo "=== starting ftp STARTTLS dialog ===" + local debugpad=" > " local reAUTHTLS='^ AUTH TLS' - starttls_full_read '^220-' '^220 ' && debugme echo "received server greeting" && - starttls_just_send 'FEAT' && debugme echo "sent FEAT" && - starttls_full_read '^(211-| )' '^211 ' "${reAUTHTLS}" && debugme echo "received server features and checked STARTTLS availability" && - starttls_just_send 'AUTH TLS' && debugme echo "initiated STARTTLS" && - starttls_full_read '^234-' '^234 ' && debugme echo "received ack for STARTTLS" + + debugme echo "=== starting ftp STARTTLS dialog ===" + starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received server greeting" && + starttls_just_send 'FEAT' && debugme echo "${debugpad}sent FEAT" && + starttls_full_read '^(211-| )' '^211 ' "${reAUTHTLS}" && debugme echo "${debugpad}received server features and checked STARTTLS availability" && + starttls_just_send 'AUTH TLS' && debugme echo "${debugpad}initiated STARTTLS" && + starttls_full_read '^234-' '^234 ' && debugme echo "${debugpad}received ack for STARTTLS" local ret=$? debugme echo "=== finished ftp STARTTLS dialog with ${ret} ===" return $ret @@ -10503,44 +10505,55 @@ starttls_ftp_dialog() { # argv1: empty: SMTP, "lmtp" : LMTP # starttls_smtp_dialog() { - local greet_str="EHLO" + local greet_str="EHLO testssl.sh" local proto="smtp" + local re250STARTTLS='^250[ -]STARTTLS' + local debugpad=" > " if [[ "$1" == lmtp ]]; then proto="lmtp" greet_str="LHLO" fi + if [[ -n "$2" ]]; then + # Here we can "add" commands in the future. Please note \r\n will automatically appended + greet_str="$2" + elif "$SNEAKY"; then + greet_str="EHLO google.com" + fi debugme echo "=== starting $proto STARTTLS dialog ===" - local re250STARTTLS='^250[ -]STARTTLS' - starttls_full_read '^220-' '^220 ' && debugme echo "received server greeting" && - starttls_just_send "$greet_str testssl.sh" && debugme echo "sent $greet_str" && - starttls_full_read '^250-' '^250 ' "${re250STARTTLS}" && debugme echo "received server capabilities and checked STARTTLS availability" && - starttls_just_send 'STARTTLS' && debugme echo "initiated STARTTLS" && - starttls_full_read '^220-' '^220 ' && debugme echo "received ack for STARTTLS" + starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received server greeting" && + starttls_just_send "$greet_str" && debugme echo "${debugpad}sent $greet_str" && + starttls_full_read '^250-' '^250 ' "${re250STARTTLS}" && debugme echo "${debugpad}received server capabilities and checked STARTTLS availability" && + starttls_just_send 'STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && + starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received ack for STARTTLS" local ret=$? debugme echo "=== finished $proto STARTTLS dialog with ${ret} ===" return $ret } starttls_pop3_dialog() { + local debugpad=" > " + debugme echo "=== starting pop3 STARTTLS dialog ===" - starttls_full_read '^\+OK' '^\+OK' && debugme echo "received server greeting" && - starttls_just_send 'STLS' && debugme echo "initiated STARTTLS" && - starttls_full_read '^\+OK' '^\+OK' && debugme echo "received ack for STARTTLS" + starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad}received server greeting" && + starttls_just_send 'STLS' && debugme echo "${debugpad}initiated STARTTLS" && + starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad}received ack for STARTTLS" local ret=$? debugme echo "=== finished pop3 STARTTLS dialog with ${ret} ===" return $ret } starttls_imap_dialog() { - debugme echo "=== starting imap STARTTLS dialog ===" local reSTARTTLS='^\* CAPABILITY(( .*)? IMAP4rev1( .*)? STARTTLS(.*)?|( .*)? STARTTLS( .*)? IMAP4rev1(.*)?)$' - starttls_full_read '^\* ' '^\* OK ' && debugme echo "received server greeting" && - starttls_just_send 'a001 CAPABILITY' && debugme echo "sent CAPABILITY" && - starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" && debugme echo "received server capabilities and checked STARTTLS availability" && - starttls_just_send 'a002 STARTTLS' && debugme echo "initiated STARTTLS" && - starttls_full_read '^\* ' '^a002 OK ' && debugme echo "received ack for STARTTLS" + local debugpad=" > " + + debugme echo "=== starting imap STARTTLS dialog ===" + starttls_full_read '^\* ' '^\* OK ' && debugme echo "${debugpad}received server greeting" && + starttls_just_send 'a001 CAPABILITY' && debugme echo "${debugpad}sent CAPABILITY" && + starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" && debugme echo "${debugpad}received server capabilities and checked STARTTLS availability" && + starttls_just_send 'a002 STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && + starttls_full_read '^\* ' '^a002 OK ' && debugme echo "${debugpad}received ack for STARTTLS" local ret=$? debugme echo "=== finished imap STARTTLS dialog with ${ret} ===" return $ret @@ -10562,10 +10575,12 @@ starttls_xmpp_dialog() { } starttls_nntp_dialog() { + local debugpad=" > " + debugme echo "=== starting nntp STARTTLS dialog ===" - starttls_full_read '$^' '^20[01] ' && debugme echo "received server greeting" && - starttls_just_send 'STARTTLS' && debugme echo "initiated STARTTLS" && - starttls_full_read '$^' '^382 ' && debugme echo "received ack for STARTTLS" + starttls_full_read '$^' '^20[01] ' && debugme echo "${debugpad}received server greeting" && + starttls_just_send 'STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && + starttls_full_read '$^' '^382 ' && debugme echo "${debugpad}received ack for STARTTLS" local ret=$? debugme echo "=== finished nntp STARTTLS dialog with ${ret} ===" return $ret @@ -10574,15 +10589,15 @@ starttls_nntp_dialog() { starttls_postgres_dialog() { debugme echo "=== starting postgres STARTTLS dialog ===" local init_tls=", x00, x00 ,x00 ,x08 ,x04 ,xD2 ,x16 ,x2F" - socksend "${init_tls}" 0 && debugme echo "initiated STARTTLS" && - starttls_io "" S 1 && debugme echo "received ack (="S") for STARTTLS" + socksend "${init_tls}" 0 && debugme echo "${debugpad}initiated STARTTLS" && + starttls_io "" S 1 && debugme echo "${debugpad}received ack (="S") for STARTTLS" local ret=$? debugme echo "=== finished postgres STARTTLS dialog with ${ret} ===" return $ret } starttls_mysql_dialog() { - debugme echo "=== starting mysql STARTTLS dialog ===" + local debugpad=" > " local login_request=" , x20, x00, x00, x01, # payload_length, sequence_id x85, xae, xff, x00, # capability flags, CLIENT_SSL always set @@ -10591,8 +10606,10 @@ starttls_mysql_dialog() { x00, x00, x00, x00, x00, x00, x00, x00, # string[23] reserved (all [0]) x00, x00, x00, x00, x00, x00, x00, x00, x00, x00, x00, x00, x00, x00, x00" + + debugme echo "=== starting mysql STARTTLS dialog ===" socksend "${login_request}" 0 - starttls_just_read 1 && debugme echo "read succeeded" + starttls_just_read 1 && debugme echo "${debugpad}read succeeded" # 1 is the timeout value which only MySQL needs. Note, there seems no response whether STARTTLS # succeeded. We could try harder, see https://github.com/openssl/openssl/blob/master/apps/s_client.c # but atm this seems sufficient as later we will fail if there's no STARTTLS. From 9d626472268a84b8519cb4ff9bb8e120c84b3446 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 4 Aug 2020 07:35:05 -0400 Subject: [PATCH 144/211] Align ALPN GREASE test with RFC 8701 The ALPN GREASE test was written based on draft-ietf-tls-grease-01, which reserved all ALPN identifiers beginning with the prefix "ignore/". This commit changes the test to align with RFC 8701, which instead reserves {0x0A,0x0A}, {0x1A,0x1A}, ..., {0xFA,0xFA}. --- testssl.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testssl.sh b/testssl.sh index cc9a584..ca6d540 100755 --- a/testssl.sh +++ b/testssl.sh @@ -18018,7 +18018,7 @@ run_grease() { selected_alpn_protocol="$(grep "ALPN protocol:" "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" | sed 's/ALPN protocol: //')" # If using a "normal" ALPN extension worked, then add an unrecognized # ALPN value to the beginning of the extension and try again. - alpn_proto="ignore/$selected_alpn_protocol" + alpn_proto="ZZ" # "ZZ" = "{0x5A,0x5A}" alpn=",$(printf "%02x" ${#alpn_proto}),$(string_to_asciihex "$alpn_proto")$alpn" alpn_list_len=${#alpn}/3 alpn_list_len_hex=$(printf "%04x" $alpn_list_len) @@ -18029,7 +18029,7 @@ run_grease() { success=$? if [[ $success -ne 0 ]] && [[ $success -ne 2 ]]; then prln_svrty_medium " Server fails if ClientHello contains an application_layer_protocol_negotiation extension with an unrecognized ALPN value." - fileout "$jsonID" "MEDIUM" "erver fails if ClientHello contains an application_layer_protocol_negotiation extension with an unrecognized ALPN value." + fileout "$jsonID" "MEDIUM" "Server fails if ClientHello contains an application_layer_protocol_negotiation extension with an unrecognized ALPN value." bug_found=true else grease_selected_alpn_protocol="$(grep "ALPN protocol:" "$TEMPDIR/$NODEIP.parse_tls_serverhello.txt" | sed 's/ALPN protocol: //')" @@ -18037,9 +18037,9 @@ run_grease() { prln_svrty_medium " Server did not ignore unrecognized ALPN value in the application_layer_protocol_negotiation extension." fileout "$jsonID" "MEDIUM" "Server did not ignore unrecognized ALPN value in the application_layer_protocol_negotiation extension." bug_found=true - elif [[ "$grease_selected_alpn_protocol" =~ ignore/ ]]; then - prln_svrty_medium " Server selected \"ignore/\" ALPN value in the application_layer_protocol_negotiation extension." - fileout "$jsonID" "MEDIUM" "Server selected \"ignore/\" ALPN value in the application_layer_protocol_negotiation extension." + elif [[ "$grease_selected_alpn_protocol" == $alpn_proto ]]; then + prln_svrty_medium " Server selected GREASE ALPN value ($alpn_proto) in the application_layer_protocol_negotiation extension." + fileout "$jsonID" "MEDIUM" "Server selected GREASE ALPN value ($alpn_proto) in the application_layer_protocol_negotiation extension." bug_found=true fi fi From 7f99ffa55d42f0f736e574dc017b9167dc0aad06 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Tue, 4 Aug 2020 07:42:10 -0400 Subject: [PATCH 145/211] Update ETSI ETS test The commit updates the test for the "Visibility Information" certificate extension used in the ETSI Enterprise Transport Security protocol. The main change is to support OpenSSL 3.0.0, which prints more information about subject alternative names that are encoded as otherName. For otherName types for the OpenSSL has no information, it prints "otherName: ::" rather than just "otherName: ". So, testssl.sh needs to account for the possibility that the OID for the visibility information certificate extension will be printed. This commit also updates the reference for this extension and changes the name of the function from etsi_etls_visibility_info() to etls_ets_visibility_info() since the name of the protocol was changed from Enterprise TLS (eTLS) to Enterprise Transport Security (ETS). This commit does not change the output to the terminal or to JSON/CSV, even though those outputs use the previous name of eTLS rather than ETS. --- testssl.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/testssl.sh b/testssl.sh index cc9a584..2f6724e 100755 --- a/testssl.sh +++ b/testssl.sh @@ -8008,8 +8008,8 @@ compare_server_name_to_cert() { # This function determines whether the certificate (arg3) contains "visibility # information" (see Section 4.3.3 of -# https://www.etsi.org/deliver/etsi_ts/103500_103599/10352303/01.01.01_60/ts_10352303v010101p.pdf . -etsi_etls_visibility_info() { +# https://www.etsi.org/deliver/etsi_ts/103500_103599/10352303/01.02.01_60/ts_10352303v010201p.pdf. +etsi_ets_visibility_info() { local jsonID="$1" local spaces="$2" local cert="$3" @@ -8020,11 +8020,12 @@ etsi_etls_visibility_info() { # If "visibility information" is present, it will appear in the subjectAltName # extension (0603551D11) as an otherName with OID 0.4.0.3523.3.1 (060604009B430301). - # OpenSSL displays all names of type otherName as "othername:". + # OpenSSL 1.1.1 and earlier displays all names of type otherName as "othername:". # As certificates will rarely include a name encoded as an otherName, check the # text version of the certificate for "othername:" before calling # external functions to obtain the DER encoded certficate. - if [[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\ ]]; then + if [[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\ ]] || \ + [[ "$cert_txt" =~ X509v3\ Subject\ Alternative\ Name:.*othername:\ 0.4.0.3523.3.1 ]]; then dercert="$($OPENSSL x509 -in "$cert" -outform DER 2>>$ERRFILE | hexdump -v -e '16/1 "%02X"')" if [[ "$dercert" =~ 0603551D110101FF04[0-9A-F]*060604009B430301 ]] || \ [[ "$dercert" =~ 0603551D1104[0-9A-F]*060604009B430301 ]]; then @@ -9008,7 +9009,7 @@ certificate_info() { out "$indent"; pr_bold " ETS/\"eTLS\"" out ", visibility info " jsonID="cert_eTLS" - etsi_etls_visibility_info "${jsonID}${json_postfix}" "$spaces" "$HOSTCERT" "$cert_txt" + etsi_ets_visibility_info "${jsonID}${json_postfix}" "$spaces" "$HOSTCERT" "$cert_txt" # *Currently* this is even listed as a vulnerability (CWE-310, CVE-2019-919), see # https://nvd.nist.gov/vuln/detail/CVE-2019-9191, https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9191 # For now we leave this here. We may want to change that later or add infos to other sections (FS & vulnerability) From fd5928af475d32b0c541264a52f8a929f886d231 Mon Sep 17 00:00:00 2001 From: David Cooper Date: Thu, 6 Aug 2020 07:50:01 -0400 Subject: [PATCH 146/211] Use fewer external function calls This commit modifies a few functions to use fewer external function calls. In most cases this involves replacing external function calls with Bash internal functions, but in one case it involves replacing multiple external function calls with one call to awk. This commit makes a few changes to the way that some functions work. is_ipv4addr() and is_ipv6addr() will now strictly only accept a string that is an IPv4 (or IPv6) address and nothing else. A couple of changes were also made to match_ipv4_httpheader(). First, lines that match $excluded_header (formerly $whitelisted_header) are not processed in the while loop. This prevents the excluded header from being output in the case that $HEADERFILE includes a non-excluded header with an IPv4 address and an excluded header with a string that looks like an IPv4 address. The list of excluded headers was also modified to exclude any line that begins "Server: " rather than just lines that begin "Server: PRTG". According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server, the "Server" header describes the software used by the server, so it seems reasonable to expect that this header line will never contain an IPv4 address. Also, looking at some old test results I found cases in which Oracle software version numbers in the Server header were mistakenly matched as IPv4 addresses. --- testssl.sh | 81 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/testssl.sh b/testssl.sh index 4fb0bdd..3af9081 100755 --- a/testssl.sh +++ b/testssl.sh @@ -124,7 +124,7 @@ trap "child_error" USR1 # declare -r VERSION="3.1dev" declare -r SWCONTACT="dirk aet testssl dot sh" -grep -E -q "dev|rc|beta" <<< "$VERSION" && \ +[[ "$VERSION" =~ dev|rc|beta ]] && \ SWURL="https://testssl.sh/dev/" || SWURL="https://testssl.sh/" if git log &>/dev/null; then @@ -843,24 +843,35 @@ is_ipv4addr() { local ipv4address="$octet\\.$octet\\.$octet\\.$octet" [[ -z "$1" ]] && return 1 - # more than numbers, important for hosts like AAA.BBB.CCC.DDD.in-addr.arpa.DOMAIN.TLS - [[ -n $(tr -d '0-9\.' <<< "$1") ]] && return 1 - grep -Eq "$ipv4address" <<< "$1" && \ + # Check that $1 contains an IPv4 address and nothing else + [[ "$1" =~ $ipv4address ]] && [[ "$1" == $BASH_REMATCH ]] && \ return 0 || \ return 1 } -# a bit easier +# See RFC 4291, Section 2.2 is_ipv6addr() { + local ipv6seg="[0-9A-Fa-f]{1,4}" + local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" + local ipv4address="$octet\\.$octet\\.$octet\\.$octet" + local ipv6address + + ipv6address="($ipv6seg:){7}(:|$ipv6seg)" + ipv6address+="|($ipv6seg:){6}(:|:$ipv6seg|$ipv4address)" + ipv6address+="|($ipv6seg:){5}(:|(:$ipv6seg){1,2}|:$ipv4address)" + ipv6address+="|($ipv6seg:){4}(:|(:$ipv6seg){1,3}|:($ipv6seg:){0,1}$ipv4address)" + ipv6address+="|($ipv6seg:){3}(:|(:$ipv6seg){1,4}|:($ipv6seg:){0,2}$ipv4address)" + ipv6address+="|($ipv6seg:){2}(:|(:$ipv6seg){1,5}|:($ipv6seg:){0,3}$ipv4address)" + ipv6address+="|($ipv6seg:){1}(:|(:$ipv6seg){1,6}|:($ipv6seg:){0,4}$ipv4address)" + ipv6address+="|:((:$ipv6seg){1,7}|:($ipv6seg:){0,5}$ipv4address)" + [[ -z "$1" ]] && return 1 - # less than 2x ":" - [[ $(count_lines "$(tr ':' '\n' <<< "$1")") -le 1 ]] && \ + + # Check that $1 contains an IPv4 address and nothing else + [[ "$1" =~ $ipv6address ]] && [[ "$1" == $BASH_REMATCH ]] && \ + return 0 || \ return 1 - # check which chars allowed: - [[ -n "$(tr -d '0-9:a-fA-F ' <<< "$1" | sed -e '/^$/d')" ]] && \ - return 1 - return 0 } ###### END universal helper function definitions ###### @@ -2379,12 +2390,11 @@ run_http_header() { match_ipv4_httpheader() { local octet="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" local ipv4address="$octet\\.$octet\\.$octet\\.$octet" - local whitelisted_header="pagespeed|page-speed|^Content-Security-Policy|^MicrosoftSharePointTeamServices|^X-OWA-Version|^Location|^Server: PRTG" + local excluded_header="pagespeed|page-speed|^Content-Security-Policy|^MicrosoftSharePointTeamServices|^X-OWA-Version|^Location|^Server: " local your_ip_msg="(check if it's your IP address or e.g. a cluster IP)" - local result + local headers result local first=true local spaces=" " - local count local jsonID="ipv4_in_header" local cwe="CWE-212" local cve="" @@ -2393,27 +2403,24 @@ match_ipv4_httpheader() { run_http_header "$1" || return 1 fi - # Whitelist some headers as they are mistakenly identified as ipv4 address. Issues #158, #323. + # Exclude some headers as they are mistakenly identified as ipv4 address. Issues #158, #323. # Also facebook used to have a CSP rule for 127.0.0.1 - if grep -Evai "$whitelisted_header" $HEADERFILE | grep -Eiq "$ipv4address"; then + headers="$(grep -Evai "$excluded_header" $HEADERFILE)" + if [[ "$headers" =~ $ipv4address ]]; then pr_bold " IPv4 address in header " - count=0 while read line; do - result="$(grep -E "$ipv4address" <<< "$line")" - result=$(strip_lf "$result") - if [[ -n "$result" ]]; then - if ! $first; then - out "$spaces" - your_ip_msg="" - else - first=false - fi - pr_svrty_medium "$result" - outln "\n$spaces$your_ip_msg" - fileout "$jsonID" "MEDIUM" "$result $your_ip_msg" "$cve" "$cwe" + [[ "$line" =~ $ipv4address ]] || continue + result=$(strip_lf "$line") + if ! $first; then + out "$spaces" + your_ip_msg="" + else + first=false fi - count=$count+1 - done < $HEADERFILE + pr_svrty_medium "$result" + outln "\n$spaces$your_ip_msg" + fileout "$jsonID" "MEDIUM" "$result $your_ip_msg" "$cve" "$cwe" + done <<< "$headers" fi } @@ -8437,7 +8444,7 @@ certificate_info() { fileout "${jsonID}${json_postfix}" "OK" "DSA with SHA256" ;; rsassaPss) - cert_sig_hash_algo="$(grep -A 1 "Signature Algorithm" <<< "$cert_txt" | head -2 | tail -1 | sed 's/^.*Hash Algorithm: //')" + cert_sig_hash_algo="$(awk '/Signature Algorithm/ { getline; print $0; exit }' <<< "$cert_txt" | sed 's/^.*Hash Algorithm: //')" case $cert_sig_hash_algo in sha1) prln_svrty_medium "RSASSA-PSS with SHA1" @@ -11221,7 +11228,7 @@ hmac() { else output="$(asciihex_to_binary "$text" | $OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)" ret=$? - tm_out "$(awk '/=/ { print $2 }' <<< "$output")" + tm_out "${output#*= }" fi return $ret } @@ -11246,7 +11253,7 @@ hmac-transcript() { $OPENSSL dgst "$hash_fn" -binary 2>/dev/null | \ $OPENSSL dgst "$hash_fn" -mac HMAC -macopt hexkey:"$key" 2>/dev/null)" ret=$? - tm_out "$(toupper "$(awk '/=/ { print $2 }' <<< "$output")")" + tm_out "$(toupper "${output#*= }")" fi return $ret } @@ -11336,7 +11343,8 @@ derive-secret() { *) return 7 esac - hash_messages="$(asciihex_to_binary "$messages" | $OPENSSL dgst "$hash_fn" 2>/dev/null | awk '/=/ { print $2 }')" + hash_messages="$(asciihex_to_binary "$messages" | $OPENSSL dgst "$hash_fn" 2>/dev/null)" + hash_messages="${hash_messages#*= }" hkdf-expand-label "$hash_fn" "$secret" "$label" "$hash_messages" "$hash_len" return $? } @@ -11380,7 +11388,8 @@ create-initial-transcript() { else return 1 fi - hash_clienthello1="$(asciihex_to_binary "$clienthello1" | $OPENSSL dgst "$hash_fn" 2>/dev/null | awk '/=/ { print $2 }')" + hash_clienthello1="$(asciihex_to_binary "$clienthello1" | $OPENSSL dgst "$hash_fn" 2>/dev/null)" + hash_clienthello1="${hash_clienthello1#*= }" msg_transcript="FE0000$(printf "%02x" $((${#hash_clienthello1}/2)))$hash_clienthello1$hrr$clienthello2$serverhello" else msg_transcript="$clienthello2$serverhello" From ac6b64ce3648a8e3370763fa63a18ff03f94a6b6 Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 11 Aug 2020 12:01:28 +0200 Subject: [PATCH 147/211] Trying to address no STARTTLS offerings (2) This PR will replace #1566. It addresses that if the server side doesn't show STARTTLS testssl.sh should exit and label it accordingly (see #1536). For this to achieve starttls_just_send() was were changed so that a return value from of 3 signals the STARTTLS pattern wasn't found is passed back to the parent fd_socket() whcih will then act accordingly. Also: * starttls_full_read() + starttls_just_send() were improved for readability and debugging. * The caller of starttls_full_read() + starttls_just_send() had redundant indentations which were moved to the callee * minor bugs were squashed (e.g. ``fd_socket()``'s return values =!0 always were referring to STARTTLS also when no STARTTLS was requested) This was tested (negative + test and positive) for FTP and SMTP which worked as expected. For POP, IMAP and NNTP it should work accordingly but I had trouble finding a server whcih DID NOT support STARTTLS. All other protocols basically should also cause testssl.sh to bail out but haven't been tested either. However here starttls_io() won't return 3. It returns 1 in a case of problems. It uses NR_STARTTLS_FAIL. If it's encountered 2+ times that STARTTLS fails it early exists using fatal(). So we maybe want to consider changing starttls_io() in the future to also use return 3 in the case STARTTLS is not offered. --- testssl.sh | 157 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 64 deletions(-) diff --git a/testssl.sh b/testssl.sh index 3af9081..243eb19 100755 --- a/testssl.sh +++ b/testssl.sh @@ -197,6 +197,7 @@ IGN_OCSP_PROXY=${IGN_OCSP_PROXY:-false} # Also when --proxy is supplied it is ig HEADER_MAXSLEEP=${HEADER_MAXSLEEP:-5} # we wait this long before killing the process to retrieve a service banner / http header MAX_SOCKET_FAIL=${MAX_SOCKET_FAIL:-2} # If this many failures for TCP socket connects are reached we terminate MAX_OSSL_FAIL=${MAX_OSSL_FAIL:-2} # If this many failures for s_client connects are reached we terminate +MAX_STARTTLS_FAIL=${MAX_STARTTLS_FAIL:-2} # max number of STARTTLS handshake failures in plaintext phase MAX_HEADER_FAIL=${MAX_HEADER_FAIL:-2} # If this many failures for HTTP GET are encountered we don't try again to get the header MAX_WAITSOCK=${MAX_WAITSOCK:-10} # waiting at max 10 seconds for socket reply. There shouldn't be any reason to change this. CCS_MAX_WAITSOCK=${CCS_MAX_WAITSOCK:-5} # for the two CCS payload (each). There shouldn't be any reason to change this. @@ -252,6 +253,7 @@ TIMEOUT_CMD="" HAD_SLEPT=0 NR_SOCKET_FAIL=0 # Counter for socket failures NR_OSSL_FAIL=0 # .. for OpenSSL connects +NR_STARTTLS_FAIL=0 # .. for STARTTLS failures NR_HEADER_FAIL=0 # .. for HTTP_GET PROTOS_OFFERED="" # This keeps which protocol is being offered. See has_server_protocol(). TLS12_CIPHER_OFFERED="" # This contains the hexcode of a cipher known to be supported by the server with TLS 1.2 @@ -10417,16 +10419,19 @@ starttls_io() { # Line-based send with newline characters appended (arg2 empty) -# Stream-based send (not in use currently): arg2: . +# arg2: debug_string -- what we had in the caller previously starttls_just_send(){ - if [[ -z "$2" ]] ; then - debugme echo "C: $1\r\n" - echo -ne "$1\r\n" >&5 + local -i ret=0 + + debugme echo "C: $1\r\n" + echo -ne "$1\r\n" >&5 + ret=$? + if [[ $ret -eq 0 ]]; then + debugme echo " > succeeded: $2" else - debugme echo -e "C: $1" - echo -ne "$1" >&5 + debugme echo " > failed: $2 ($ret)" fi - return $? + return $ret } # arg1: (optional): wait time @@ -10446,66 +10451,74 @@ starttls_just_read(){ starttls_full_read(){ local cont_pattern="$1" local end_pattern="$2" - local regex="$3" + local starttls_regex="$3" # optional: pattern we search for in the server's response + local debug_str="$4" # optional local starttls_read_data=() local one_line="" local ret=0 local ret_found=0 local debugpad=" > found: " + local oldIFS="$IFS" debugme echo "=== reading banner ... ===" - if [[ $# -ge 3 ]]; then - debugme echo "=== we'll have to search for \"$regex\" pattern ===" + if [[ -n "$starttls_regex" ]]; then + debugme echo "=== we'll have to search for \"$starttls_regex\" pattern ===" + # pre-set an error if we won't find the ~regex ret_found=3 fi - local oldIFS="$IFS" IFS='' + # Now read handshake line by line and act on the args supplied. + # Exit the subshell if timeout has been hit (-t $STARTTLS_SLEEP) while read -r -t $STARTTLS_SLEEP one_line; ret=$?; (exit $ret); do debugme tmln_out "S: ${one_line}" if [[ $DEBUG -ge 5 ]]; then echo "end_pattern/cont_pattern: ${end_pattern} / ${cont_pattern}" fi - if [[ $# -ge 3 ]]; then - if [[ ${one_line} =~ $regex ]]; then - ret_found=0 + if [[ -n "$starttls_regex" ]]; then + if [[ ${one_line} =~ $starttls_regex ]]; then debugme tmln_out "${debugpad} ${one_line} " + # We don't exit here as the buffer is not empty. So we continue reading but save the status: + ret_found=0 fi fi starttls_read_data+=("${one_line}") if [[ ${one_line} =~ ${end_pattern} ]]; then debugme tmln_out "${debugpad} ${one_line} " IFS="${oldIFS}" - return ${ret_found} + break fi if [[ ! ${one_line} =~ ${cont_pattern} ]]; then debugme echo "=== full read syntax error, expected regex pattern ${cont_pattern} (cont) or ${end_pattern} (end) ===" IFS="${oldIFS}" - return 2 + ret_found=2 + break fi done <&5 - if [[ $DEBUG -ge 2 ]]; then + if [[ $ret_found -eq 0 ]]; then + # Print the debug statement we previously had in the caller function + [[ -n "$debug_str" ]] && debugme echo " >> $debug_str" + else if [[ $ret -ge 128 ]]; then - echo "=== timeout reading ===" - else - echo "=== full read error (no timeout) ===" + debugme echo "=== timeout reading ===" + $ret_found=$ret fi fi IFS="${oldIFS}" - return $ret + return $ret_found } starttls_ftp_dialog() { - local debugpad=" > " - local reAUTHTLS='^ AUTH TLS' + local -i ret=0 + local reSTARTTLS='^ AUTH TLS' debugme echo "=== starting ftp STARTTLS dialog ===" - starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received server greeting" && - starttls_just_send 'FEAT' && debugme echo "${debugpad}sent FEAT" && - starttls_full_read '^(211-| )' '^211 ' "${reAUTHTLS}" && debugme echo "${debugpad}received server features and checked STARTTLS availability" && - starttls_just_send 'AUTH TLS' && debugme echo "${debugpad}initiated STARTTLS" && - starttls_full_read '^234-' '^234 ' && debugme echo "${debugpad}received ack for STARTTLS" - local ret=$? + starttls_full_read '^220-' '^220 ' '' "received server greeting" && + starttls_just_send 'FEAT' "sent FEAT" && + starttls_full_read '^(211-| )' '^211 ' "${reSTARTTLS}" "received server features and checked STARTTLS availability" && + starttls_just_send 'AUTH TLS' "initiated STARTTLS" && + starttls_full_read '^234-' '^234 ' '' "received ack for STARTTLS" + ret=$? debugme echo "=== finished ftp STARTTLS dialog with ${ret} ===" return $ret } @@ -10515,59 +10528,61 @@ starttls_ftp_dialog() { starttls_smtp_dialog() { local greet_str="EHLO testssl.sh" local proto="smtp" - local re250STARTTLS='^250[ -]STARTTLS' - local debugpad=" > " + local reSTARTTLS='^250[ -]STARTTLS' + local -i ret=0 if [[ "$1" == lmtp ]]; then proto="lmtp" greet_str="LHLO" fi if [[ -n "$2" ]]; then - # Here we can "add" commands in the future. Please note \r\n will automatically appended + # Here we can "add" commands in the future. Please note \r\n will automatically be appended greet_str="$2" elif "$SNEAKY"; then greet_str="EHLO google.com" fi debugme echo "=== starting $proto STARTTLS dialog ===" - starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received server greeting" && - starttls_just_send "$greet_str" && debugme echo "${debugpad}sent $greet_str" && - starttls_full_read '^250-' '^250 ' "${re250STARTTLS}" && debugme echo "${debugpad}received server capabilities and checked STARTTLS availability" && - starttls_just_send 'STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && - starttls_full_read '^220-' '^220 ' && debugme echo "${debugpad}received ack for STARTTLS" - local ret=$? + starttls_full_read '^220-' '^220 ' '' "received server greeting" && + starttls_just_send "$greet_str" "sent $greet_str" && + starttls_full_read '^250-' '^250 ' "${reSTARTTLS}" "received server capabilities and checked STARTTLS availability" && + starttls_just_send 'STARTTLS' "initiated STARTTLS" && + starttls_full_read '^220-' '^220 ' '' "received ack for STARTTLS" + ret=$? debugme echo "=== finished $proto STARTTLS dialog with ${ret} ===" return $ret } starttls_pop3_dialog() { - local debugpad=" > " + local -i ret=0 debugme echo "=== starting pop3 STARTTLS dialog ===" - starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad}received server greeting" && - starttls_just_send 'STLS' && debugme echo "${debugpad}initiated STARTTLS" && - starttls_full_read '^\+OK' '^\+OK' && debugme echo "${debugpad}received ack for STARTTLS" - local ret=$? + starttls_full_read '^\+OK' '^\+OK' '' "received server greeting" && + starttls_just_send 'STLS' "initiated STARTTLS" && + starttls_full_read '^\+OK' '^\+OK' '' "received ack for STARTTLS" + ret=$? debugme echo "=== finished pop3 STARTTLS dialog with ${ret} ===" return $ret } starttls_imap_dialog() { + local -i ret=0 local reSTARTTLS='^\* CAPABILITY(( .*)? IMAP4rev1( .*)? STARTTLS(.*)?|( .*)? STARTTLS( .*)? IMAP4rev1(.*)?)$' - local debugpad=" > " debugme echo "=== starting imap STARTTLS dialog ===" - starttls_full_read '^\* ' '^\* OK ' && debugme echo "${debugpad}received server greeting" && - starttls_just_send 'a001 CAPABILITY' && debugme echo "${debugpad}sent CAPABILITY" && - starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" && debugme echo "${debugpad}received server capabilities and checked STARTTLS availability" && - starttls_just_send 'a002 STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && - starttls_full_read '^\* ' '^a002 OK ' && debugme echo "${debugpad}received ack for STARTTLS" - local ret=$? + starttls_full_read '^\* ' '^\* OK ' '' "received server greeting" && + starttls_just_send 'a001 CAPABILITY' "sent CAPABILITY" && + starttls_full_read '^\* ' '^a001 OK ' "${reSTARTTLS}" "received server capabilities and checked STARTTLS availability" && + starttls_just_send 'a002 STARTTLS' "initiated STARTTLS" && + starttls_full_read '^\* ' '^a002 OK ' '' "received ack for STARTTLS" + ret=$? debugme echo "=== finished imap STARTTLS dialog with ${ret} ===" return $ret } starttls_xmpp_dialog() { + local -i ret=0 + debugme echo "=== starting xmpp STARTTLS dialog ===" [[ -z $XMPP_HOST ]] && XMPP_HOST="$NODE" @@ -10577,36 +10592,40 @@ starttls_xmpp_dialog() { starttls_io "" 'starttls(.*)features' 1 && starttls_io "" '" 'JUSTSEND' 2 - local ret=$? + ret=$? debugme echo "=== finished xmpp STARTTLS dialog with ${ret} ===" return $ret } starttls_nntp_dialog() { - local debugpad=" > " + local -i ret=0 debugme echo "=== starting nntp STARTTLS dialog ===" - starttls_full_read '$^' '^20[01] ' && debugme echo "${debugpad}received server greeting" && - starttls_just_send 'STARTTLS' && debugme echo "${debugpad}initiated STARTTLS" && - starttls_full_read '$^' '^382 ' && debugme echo "${debugpad}received ack for STARTTLS" - local ret=$? + starttls_full_read '$^' '^20[01] ' '' "received server greeting" && + starttls_just_send 'STARTTLS' "initiated STARTTLS" && + starttls_full_read '$^' '^382 ' '' "received ack for STARTTLS" + ret=$? debugme echo "=== finished nntp STARTTLS dialog with ${ret} ===" return $ret } starttls_postgres_dialog() { + local -i ret=0 + local debugpad=" > " + local starttls_init=", x00, x00 ,x00 ,x08 ,x04 ,xD2 ,x16 ,x2F" + debugme echo "=== starting postgres STARTTLS dialog ===" - local init_tls=", x00, x00 ,x00 ,x08 ,x04 ,xD2 ,x16 ,x2F" - socksend "${init_tls}" 0 && debugme echo "${debugpad}initiated STARTTLS" && + socksend "${starttls_init}" 0 && debugme echo "${debugpad}initiated STARTTLS" && starttls_io "" S 1 && debugme echo "${debugpad}received ack (="S") for STARTTLS" - local ret=$? + ret=$? debugme echo "=== finished postgres STARTTLS dialog with ${ret} ===" return $ret } starttls_mysql_dialog() { local debugpad=" > " - local login_request=" + local -i ret=0 + local starttls_init=" , x20, x00, x00, x01, # payload_length, sequence_id x85, xae, xff, x00, # capability flags, CLIENT_SSL always set x00, x00, x00, x01, # max-packet size @@ -10616,8 +10635,8 @@ starttls_mysql_dialog() { x00, x00, x00, x00, x00, x00, x00" debugme echo "=== starting mysql STARTTLS dialog ===" - socksend "${login_request}" 0 - starttls_just_read 1 && debugme echo "${debugpad}read succeeded" + socksend "${starttls_init}" 0 && debugme echo "${debugpad}initiated STARTTLS" && + starttls_just_read 1 "read succeeded" # 1 is the timeout value which only MySQL needs. Note, there seems no response whether STARTTLS # succeeded. We could try harder, see https://github.com/openssl/openssl/blob/master/apps/s_client.c # but atm this seems sufficient as later we will fail if there's no STARTTLS. @@ -10625,7 +10644,7 @@ starttls_mysql_dialog() { # also there's a banner in the reply "mysql_native_password" # TODO: We could detect if the server supports STARTTLS via the "Server Capabilities" # bit field, but we'd need to parse the binary stream, with greater precision than regex. - local ret=$? + ret=$? debugme echo "=== finished mysql STARTTLS dialog with ${ret} ===" return $ret } @@ -10733,9 +10752,19 @@ fd_socket() { *) # we need to throw an error here -- otherwise testssl.sh treats the STARTTLS protocol as plain SSL/TLS which leads to FP fatal "FIXME: STARTTLS protocol $STARTTLS_PROTOCOL is not yet supported" $ERR_NOSUPPORT esac + ret=$? + case $ret in + 0) return 0 ;; + 3) fatal "No STARTTLS found in handshake" $ERR_CONNECT ;; + *) ((NR_STARTTLS_FAIL++)) + # This are mostly timeouts here (code >=128). We give the client a chance to try again later. For cases + # where we have no STARTTLS in the server banner however - ret code=3 - we don't neet to try again + connectivity_problem $NR_STARTTLS_FAIL $MAX_STARTTLS_FAIL "STARTTLS handshake failed (code: $ret)" "repeated STARTTLS problems, giving up ($ret)" + return 6 ;; + esac fi + # Plain socket ok, yes or no? [[ $? -eq 0 ]] && return 0 - prln_warning " STARTTLS handshake failed" return 1 } From c4841c83eb9b954e02d896c9802bc72bb6c9520a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 11 Aug 2020 15:30:53 +0200 Subject: [PATCH 148/211] Don't penalize rating for CAs which aren't in the Java store This fixes #1648. Java store doesn't seem to be as complete. No downgrading of trust rating to T but we still need to raise a red flag for some Java clients --- testssl.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/testssl.sh b/testssl.sh index 3af9081..490aa54 100755 --- a/testssl.sh +++ b/testssl.sh @@ -7221,10 +7221,9 @@ determine_trust() { # we did to stdout the warning above already, so we could stay here with OK: fileout "${jsonID}${json_postfix}" "OK" "passed. $addtl_warning" else - # at least one failed pr_svrty_critical "NOT ok" if ! "$some_ok"; then - # all failed (we assume with the same issue), we're displaying the reason + # ALL failed (we assume with the same issue), we're displaying the reason out " " code="$(verify_retcode_helper "${verify_retcode[1]}")" if [[ "$code" =~ "pls report" ]]; then @@ -7235,15 +7234,13 @@ determine_trust() { fileout "${jsonID}${json_postfix}" "CRITICAL" "failed $code. $addtl_warning" set_grade_cap "T" "Issues with the chain of trust $code" else - # is one ok and the others not ==> display the culprit store + # alt least one ok and other(s) not ==> display the culprit store(s) if "$some_ok"; then pr_svrty_critical ":" - for ((i=1;i<=num_ca_bundles;i++)); do + for (( i=1; i<=num_ca_bundles; i++ )); do if ${trust[i]}; then ok_was="${certificate_file[i]} $ok_was" else - #code="$(verify_retcode_helper ${verify_retcode[i]})" - #notok_was="${certificate_file[i]} $notok_was" pr_svrty_high " ${certificate_file[i]} " code="$(verify_retcode_helper "${verify_retcode[i]}")" if [[ "$code" =~ "pls report" ]]; then @@ -7252,11 +7249,13 @@ determine_trust() { out "$code" fi notok_was="${certificate_file[i]} $code $notok_was" - set_grade_cap "T" "Issues with chain of trust $code" + if ! [[ ${certificate_file[i]} =~ Java ]]; then + # Exemption for Java AND rating, as this store doesn't seem to be as complete. + # We won't penelize this but we still need to raise a red flag. See #1648 + set_grade_cap "T" "Issues with chain of trust $code" + fi fi done - #pr_svrty_high "$notok_was " - #outln "$code" outln # lf + green ones [[ "$DEBUG" -eq 0 ]] && tm_out "$spaces" @@ -7269,7 +7268,6 @@ determine_trust() { outln return 0 } - # not handled: Root CA supplied ("contains anchor" in SSLlabs terminology) tls_time() { From 46536132111cd39f2dbd21a45a6113600535d3fc Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 11 Aug 2020 15:36:43 +0200 Subject: [PATCH 149/211] Add mitigate_javastore4rating --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ada38e..b57102f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Don't use external pwd anymore * STARTTLS: XMPP server support * Rating (SSL Labs, not complete) +* Don't penalize missing trust in rating when CA not in Java store * Added support for certificates with EdDSA signatures and pubilc keys ### Features implemented / improvements in 3.0 From 1915a7b624ccace78455057c6817cff8c52f048c Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Tue, 11 Aug 2020 15:41:20 +0200 Subject: [PATCH 150/211] STARTTLS --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ada38e..918a568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ * Security fix: DNS input * Don't use external pwd anymore * STARTTLS: XMPP server support +* Code improvements to STARTTLS +* Detect better when no STARTTLS is offered * Rating (SSL Labs, not complete) * Added support for certificates with EdDSA signatures and pubilc keys From 953e1bd0ff89dda8b3d2a296a06673a8eb91cd2a Mon Sep 17 00:00:00 2001 From: Dirk Wetter Date: Thu, 13 Aug 2020 18:11:24 +0200 Subject: [PATCH 151/211] Phrase --version & friends as standalone This PR fixes #1671. Primarily there's now an additional case statement in the main while loop which just calls fatal() when it detects --help -b --banner -v or --version. The documentation was also updated to reflect that. (Some grammar and other errors which I stumbled over were corrected too) --- doc/testssl.1 | 6 +++--- doc/testssl.1.html | 14 ++++++-------- doc/testssl.1.md | 6 +++--- testssl.sh | 24 ++++++++++++------------ 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/doc/testssl.1 b/doc/testssl.1 index 3d1bd53..2f4fea5 100644 --- a/doc/testssl.1 +++ b/doc/testssl.1 @@ -75,8 +75,8 @@ Options are either short or long options\. Any long or short option requiring a .P \fB\fR or \fB\-\-file \fR always needs to be the last parameter\. . -.SS "BANNER OPTIONS" -\fB\-\-help\fR (or no arg) display command line help +.SS "BANNER OPTIONS (standalone)" +\fB\-\-help\fR (or no arg) displays command line help . .P \fB\-b, \-\-banner\fR displays testssl\.sh banner, including license, usage conditions, version of testssl\.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture\. @@ -85,7 +85,7 @@ Options are either short or long options\. Any long or short option requiring a \fB\-v, \-\-version\fR same as before . .P -\fB\-V [pattern] , \-\-local [pattern]\fR pretty print all local ciphers supported by openssl version\. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below\. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits\. It does a word pattern match for non\-numbers, for number just a normal match applies\. Numbers here are defined as [0\-9,A\-F]\. This means (attention: catch) that the pattern CBC is matched as non\-word, but AES as word\. +\fB\-V [pattern], \-\-local [pattern]\fR pretty print all local ciphers supported by openssl version\. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below\. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits\. It does a word pattern match for non\-numbers, for number just a normal match applies\. Numbers here are defined as [0\-9,A\-F]\. This means (attention: catch) that the pattern CBC is matched as non\-word, but AES as word\. This option also accepts -\fB-openssl=\fR\. . .SS "INPUT PARAMETERS" \fBURI\fR can be a hostname, an IPv4 or IPv6 address (restriction see below) or an URL\. IPv6 addresses need to be in square brackets\. For any given parameter port 443 is assumed unless specified by appending a colon and a port number\. The only preceding protocol specifier allowed is \fBhttps\fR\. You need to be aware that checks for an IP address might not hit the vhost you want\. DNS resolution (A/AAAA record) is being performed unless you have an \fB/etc/hosts\fR entry for the hostname\. diff --git a/doc/testssl.1.html b/doc/testssl.1.html index c5e9de3..e3d78d7 100644 --- a/doc/testssl.1.html +++ b/doc/testssl.1.html @@ -143,15 +143,15 @@ linked OpenSSL binaries for major operating systems are supplied in ./bin/

    <URI> or --file <FILE> always needs to be the last parameter.

    - + -

    --help (or no arg) display command line help

    +

    --help (or no arg) displays command line help

    -b, --banner displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture.

    -v, --version same as before

    -

    -V [pattern] , --local [pattern] pretty print all local ciphers supported by openssl version. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits. It does a word pattern match for non-numbers, for number just a normal match applies. Numbers here are defined as [0-9,A-F]. This means (attention: catch) that the pattern CBC is matched as non-word, but AES as word.

    +

    -V [pattern], --local [pattern] pretty print all local ciphers supported by openssl version. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits. It does a word pattern match for non-numbers, for number just a normal match applies. Numbers here are defined as [0-9,A-F]. This means (attention: catch) that the pattern CBC is matched as non-word, but AES as word. This option also accepts --openssl=<path_to_openssl>.

    INPUT PARAMETERS

    @@ -189,7 +189,7 @@ The same can be achieved by setting the environment variable WARNINGSSPECIAL INVOCATIONS -

    -t <protocol>, --starttls <protocol> does a default run against a STARTTLS enabled protocol. protocol must be one of ftp, smtp, pop3, imap, xmpp,xmpp-server, telnet, ldap, irc, lmtp, nntp, postgres, mysql. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with --ssl-native. telnet and irc is WIP.

    +

    -t <protocol>, --starttls <protocol> does a default run against a STARTTLS enabled protocol. protocol must be one of ftp, smtp, pop3, imap, xmpp, xmpp-server, telnet, ldap, irc, lmtp, nntp, postgres, mysql. For the latter four you need e.g. the supplied OpenSSL or OpenSSL version 1.1.1. Please note: MongoDB doesn't offer a STARTTLS connection, LDAP currently only works with --ssl-native. telnet and irc is WIP.

    --xmpphost <jabber_domain> is an additional option for STARTTLS enabled XMPP: It expects the jabber domain as a parameter. This is only needed if the domain is different from the URI supplied.

    @@ -199,7 +199,7 @@ The same can be achieved by setting the environment variable WARNINGS--proxy <host>:<port> does ANY check via the specified proxy. --proxy=auto inherits the proxy setting from the environment. The hostname supplied will be resolved to the first A record. In addition if you want lookups via proxy you can specify DNS_VIA_PROXY=true. OCSP revocation checking (-S --phone-out) is not supported by OpenSSL via proxy. As supplying a proxy is an indicator for port 80 and 443 outgoing being blocked in your network an OCSP revocation check won't be performed. However if IGN_OCSP_PROXY=true has been supplied it will be tried directly. Authentication to the proxy is not supported. Proxying via IPv6 addresses is not possible, no HTTPS or SOCKS proxy is supported.

    -

    -6 does (also) IPv6 checks. Please note that testssl.sh doesn't perform checks on an IPv6 address automatically, because of two reasons: testssl.sh does no connectivity checks for IPv6 and it cannot determine reliably whether the OpenSSL binary you're using has IPv6 s_client support. -6 assumes both is the case. If both conditions are met and you in general prefer to test for IPv6 branches as well you can add HAS_IPv6 to your shell environment. Besides the OpenSSL binary supplied IPv6 is known to work with vanilla OpenSSL >= 1.1.0 and older versions >=1.0.2 in RHEL/CentOS/FC and Gentoo.

    +

    -6 does (also) IPv6 checks. Please note that testssl.sh doesn't perform checks on an IPv6 address automatically, because of two reasons: testssl.sh does no connectivity checks for IPv6 and it cannot determine reliably whether the OpenSSL binary you're using has IPv6 s_client support. -6 assumes both is the case. If both conditions are met and you in general prefer to test for IPv6 branches as well you can add HAS_IPv6 to your shell environment. Besides the OpenSSL binary supplied IPv6 is known to work with vanilla OpenSSL ≥ 1.1.0 and older versions ≥1.0.2 in RHEL/CentOS/FC and Gentoo.

    --ssl-native Instead of using a mixture of bash sockets and a few openssl s_client connects, testssl.sh uses the latter (almost) only. This is faster at the moment but provides less accurate results, especially for the client simulation and for cipher support. For all checks you will see a warning if testssl.sh cannot tell if a particular check cannot be performed. For some checks however you might end up getting false negatives without a warning. This option is only recommended if you prefer speed over accuracy or you know that your target has sufficient overlap with the protocols and cipher provided by your openssl binary.

    @@ -667,9 +667,7 @@ from. That helps us to get bugfixes, other feedback and more contributions.
    1. -
    2. May 2020
    3. -
    4. testssl(1)
    5. -
    +
  • August 2020
  • testssl(1)
  • diff --git a/doc/testssl.1.md b/doc/testssl.1.md index 30e07aa..42f8c7e 100644 --- a/doc/testssl.1.md +++ b/doc/testssl.1.md @@ -66,15 +66,15 @@ Options are either short or long options. Any long or short option requiring a v `` or `--file ` always needs to be the last parameter. -### BANNER OPTIONS +### BANNER OPTIONS (standalone) -`--help` (or no arg) display command line help +`--help` (or no arg) displays command line help `-b, --banner` displays testssl.sh banner, including license, usage conditions, version of testssl.sh, detected openssl version, its path to it, # of ciphers of openssl, its build date and the architecture. `-v, --version` same as before -`-V [pattern] , --local [pattern]` pretty print all local ciphers supported by openssl version. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits. It does a word pattern match for non-numbers, for number just a normal match applies. Numbers here are defined as [0-9,A-F]. This means (attention: catch) that the pattern CBC is matched as non-word, but AES as word. +`-V [pattern], --local [pattern]` pretty print all local ciphers supported by openssl version. If a pattern is supplied it performs a match (ignore case) on any of the strings supplied in the wide output, see below. The pattern will be searched in the any of the columns: hexcode, cipher suite name (OpenSSL or IANA), key exchange, encryption, bits. It does a word pattern match for non-numbers, for number just a normal match applies. Numbers here are defined as [0-9,A-F]. This means (attention: catch) that the pattern CBC is matched as non-word, but AES as word. This option also accepts `--openssl=`. ### INPUT PARAMETERS diff --git a/testssl.sh b/testssl.sh index c365cd9..ed93ca8 100755 --- a/testssl.sh +++ b/testssl.sh @@ -18808,26 +18808,23 @@ help() { "$PROG_NAME [options] " or "$PROG_NAME " - -"$PROG_NAME ", where is: +"$PROG_NAME