From ac3e5f4d6200c3c8fe2e8396f6f86fdeadf32ab1 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Thu, 3 Apr 2014 23:37:46 +0200 Subject: [PATCH 01/11] Correctly report TLSv1.2 only ciphers as negotiable with TLSv1.2 Previously scan would report: prio ciphersuite protocols pfs_keysize 1 ECDHE-RSA-AES128-GCM-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 2 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits Now it correctly reports: prio ciphersuite protocols pfs_keysize 1 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 ECDH,P-256,256bits 2 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits --- cipherscan | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cipherscan b/cipherscan index 4d861aa..4bb7b75 100755 --- a/cipherscan +++ b/cipherscan @@ -64,6 +64,7 @@ test_cipher_on_target() { cipher="" protocols="" pfs="" + previous_cipher="" for tls_version in "-ssl2" "-ssl3" "-tls1" "-tls1_1" "-tls1_2" do debug echo \"quit\\n\" \| $sslcommand $tls_version @@ -74,7 +75,15 @@ test_cipher_on_target() { if [[ -z "$current_protocol" || "$current_cipher" == '(NONE)' ]]; then # connection failed, try again with next TLS version continue + else + verbose "connection successful; protocol: $current_protocol, cipher: $current_cipher, previous cipher: $previous_cipher" fi + # handling of TLSv1.2 only cipher suites + if [ ! -z "$previous_cipher" ] && [ "$previous_cipher" != "$current_cipher" ] && [ "$current_cipher" != "0000" ]; then + unset protocols + fi + previous_cipher=$current_cipher + # connection succeeded, add TLS version to positive results if [ -z "$protocols" ]; then protocols=$current_protocol From 32eba4e64435d8091caa1418db63011463d17273 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 18:46:41 +0200 Subject: [PATCH 02/11] update examples from README since now the scan reports protocols correctly, update the example to illustrate that --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 651e846..bdc43d3 100644 --- a/README.md +++ b/README.md @@ -45,24 +45,24 @@ Testing plain SSL/TLS: linux $ ./cipherscan www.google.com:443 ................... prio ciphersuite protocols pfs_keysize -1 ECDHE-RSA-CHACHA20-POLY1305 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -2 ECDHE-RSA-AES128-GCM-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -3 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -4 ECDHE-RSA-AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -5 AES128-GCM-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -6 RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -7 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -8 ECDHE-RSA-AES256-GCM-SHA384 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -9 ECDHE-RSA-AES256-SHA384 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -10 ECDHE-RSA-AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -11 AES256-GCM-SHA384 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -12 AES256-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -13 AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -14 ECDHE-RSA-DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -15 DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -16 ECDHE-RSA-AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -17 AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -18 AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 +1 ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 ECDH,P-256,256bits +2 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 ECDH,P-256,256bits +3 ECDHE-RSA-AES128-SHA TLSv1.2 ECDH,P-256,256bits +4 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits +5 AES128-GCM-SHA256 TLSv1.2 +6 AES128-SHA256 TLSv1.2 +7 AES128-SHA TLSv1.2 +8 RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 +9 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 +10 ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 ECDH,P-256,256bits +11 ECDHE-RSA-AES256-SHA384 TLSv1.2 ECDH,P-256,256bits +12 ECDHE-RSA-AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits +13 AES256-GCM-SHA384 TLSv1.2 +14 AES256-SHA256 TLSv1.2 +15 AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 +16 ECDHE-RSA-DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits +17 DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 +18 ECDHE-RSA-AES128-SHA256 TLSv1.2 ECDH,P-256,256bits ``` Testing STARTTLS: From f9fdd62a59b5968650b916175a08811d1d9e24e6 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 19:09:05 +0200 Subject: [PATCH 03/11] report key size used in server's certificate Extend the report to show also server certificate key size: prio ciphersuite protocols pubkey_size pfs_keysize 1 ECDHE-RSA-AES128-SHA256 TLSv1.2 2048 ECDH,P-256,256bits 2 ECDHE-ECDSA-AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 256 ECDH,P-256,256bits 3 AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 4 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 5 EXP-RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 RSA,512bits --- cipherscan | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cipherscan b/cipherscan index 4bb7b75..2eca621 100755 --- a/cipherscan +++ b/cipherscan @@ -72,6 +72,10 @@ test_cipher_on_target() { current_cipher=$(grep "New, " <<<"$tmp"|awk '{print $5}') current_pfs=$(grep 'Server Temp Key' <<<"$tmp"|awk '{print $4$5$6$7}') current_protocol=$(egrep "^\s+Protocol\s+:" <<<"$tmp"|awk '{print $3}') + current_pubkey=$(grep 'Server public key is ' <<<"$tmp"|awk '{print $5}') + if [ -z $current_pubkey ]; then + current_pubkey=0 + fi if [[ -z "$current_protocol" || "$current_cipher" == '(NONE)' ]]; then # connection failed, try again with next TLS version continue @@ -92,6 +96,7 @@ test_cipher_on_target() { fi cipher=$current_cipher pfs=$current_pfs + pubkey=$current_pubkey # grab the cipher and PFS key size done # if cipher is empty, that means none of the TLS version worked with @@ -103,13 +108,13 @@ test_cipher_on_target() { # if cipher contains NONE, the cipher wasn't accepted elif [ "$cipher" == '(NONE) ' ]; then - result="$cipher $protocols $pfs" + result="$cipher $protocols $pubkey $pfs" verbose "handshake failed, server returned ciphersuite '$result'" return 1 # the connection succeeded else - result="$cipher $protocols $pfs" + result="$cipher $protocols $pubkey $pfs" verbose "handshake succeeded, server returned ciphersuite '$result'" return 0 fi @@ -173,9 +178,9 @@ display_results_in_terminal() { done if [ $DOBENCHMARK -eq 1 ]; then - header="prio ciphersuite protocols pfs_keysize avg_handshake_microsec" + header="prio ciphersuite protocols pubkey_size pfs_keysize avg_handshake_microsec" else - header="prio ciphersuite protocols pfs_keysize" + header="prio ciphersuite protocols pubkey_size pfs_keysize" fi ctr=0 for result in "${results[@]}"; do @@ -196,7 +201,8 @@ display_results_in_json() { [ $ctr -gt 0 ] && echo -n ',' echo -n "{\"cipher\":\"$(echo $cipher|awk '{print $1}')\"," echo -n "\"protocols\":[\"$(echo $cipher|awk '{print $2}'|sed 's/,/","/g')\"]," - pfs=$(echo $cipher|awk '{print $3}') + echo -n "\"pubkey\":[\"$(echo $cipher|awk '{print $3}'|sed 's/,/","/g')\"]," + pfs=$(echo $cipher|awk '{print $4}') [ "$pfs" == "" ] && pfs="None" echo -n "\"pfs\":\"$pfs\"}" ctr=$((ctr+1)) From 946cc6a9acfff2ca1937cf7cf7842466b635dff4 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 19:21:59 +0200 Subject: [PATCH 04/11] Report the signature type used on server certificate Parse the certificate used by server and report the signature used: prio ciphersuite protocols pubkey_size signature_algorithm pfs_keysize 1 ECDHE-RSA-AES128-SHA256 TLSv1.2 2048 sha1WithRSAEncryption ECDH,P-256,256bits 2 ECDHE-ECDSA-AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 256 ecdsa-with-SHA512 ECDH,P-256,256bits 3 AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption 4 AECDH-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 0 None ECDH,P-256,256bits 5 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption 6 EXP-RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption RSA,512bits --- cipherscan | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cipherscan b/cipherscan index 2eca621..aa3c202 100755 --- a/cipherscan +++ b/cipherscan @@ -76,6 +76,10 @@ test_cipher_on_target() { if [ -z $current_pubkey ]; then current_pubkey=0 fi + current_sigalg=$(openssl x509 -noout -text 2>/dev/null <<<"$tmp"|grep Signature\ Algorithm | head -n 1 | awk '{print $3}') || current_sigalg="None" + if [ -z $current_sigalg ]; then + current_sigalg=None + fi if [[ -z "$current_protocol" || "$current_cipher" == '(NONE)' ]]; then # connection failed, try again with next TLS version continue @@ -97,6 +101,7 @@ test_cipher_on_target() { cipher=$current_cipher pfs=$current_pfs pubkey=$current_pubkey + sigalg=$current_sigalg # grab the cipher and PFS key size done # if cipher is empty, that means none of the TLS version worked with @@ -108,13 +113,13 @@ test_cipher_on_target() { # if cipher contains NONE, the cipher wasn't accepted elif [ "$cipher" == '(NONE) ' ]; then - result="$cipher $protocols $pubkey $pfs" + result="$cipher $protocols $pubkey $sigalg $pfs" verbose "handshake failed, server returned ciphersuite '$result'" return 1 # the connection succeeded else - result="$cipher $protocols $pubkey $pfs" + result="$cipher $protocols $pubkey $sigalg $pfs" verbose "handshake succeeded, server returned ciphersuite '$result'" return 0 fi @@ -178,9 +183,9 @@ display_results_in_terminal() { done if [ $DOBENCHMARK -eq 1 ]; then - header="prio ciphersuite protocols pubkey_size pfs_keysize avg_handshake_microsec" + header="prio ciphersuite protocols pubkey_size signature_algoritm pfs_keysize avg_handshake_microsec" else - header="prio ciphersuite protocols pubkey_size pfs_keysize" + header="prio ciphersuite protocols pubkey_size signature_algorithm pfs_keysize" fi ctr=0 for result in "${results[@]}"; do @@ -202,7 +207,8 @@ display_results_in_json() { echo -n "{\"cipher\":\"$(echo $cipher|awk '{print $1}')\"," echo -n "\"protocols\":[\"$(echo $cipher|awk '{print $2}'|sed 's/,/","/g')\"]," echo -n "\"pubkey\":[\"$(echo $cipher|awk '{print $3}'|sed 's/,/","/g')\"]," - pfs=$(echo $cipher|awk '{print $4}') + echo -n "\"sigalg\":[\"$(echo $cipher|awk '{print $4}'|sed 's/,/","/g')\"]," + pfs=$(echo $cipher|awk '{print $5}') [ "$pfs" == "" ] && pfs="None" echo -n "\"pfs\":\"$pfs\"}" ctr=$((ctr+1)) From f04567d40edf65007aefacb18432d99eaa88a530 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 19:36:51 +0200 Subject: [PATCH 05/11] check if certificate used by server is trused Use system trust anchors to check if certificate chain used by server is actually valid. --- cipherscan | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/cipherscan b/cipherscan index aa3c202..43469b2 100755 --- a/cipherscan +++ b/cipherscan @@ -8,6 +8,10 @@ DOBENCHMARK=0 BENCHMARKITER=30 OPENSSLBIN="$(dirname $0)/openssl" +CACERTS=${CACERTS:-/etc/pki/tls/certs/ca-bundle.crt} +if [ ! -e "$CACERTS" ]; then + echo "Warning: CA Certificates not found at $CACERTS, export CACERTS variable with location of your trust anchors" 1>&2 +fi CIPHERSUITE="ALL:COMPLEMENTOFALL" DEBUG=0 VERBOSE=0 @@ -77,6 +81,12 @@ test_cipher_on_target() { current_pubkey=0 fi current_sigalg=$(openssl x509 -noout -text 2>/dev/null <<<"$tmp"|grep Signature\ Algorithm | head -n 1 | awk '{print $3}') || current_sigalg="None" + grep 'Verify return code: 0 ' <<<"$tmp" >/dev/null + if [ $? -eq 0 ]; then + current_trusted="True" + else + current_trusted="False" + fi if [ -z $current_sigalg ]; then current_sigalg=None fi @@ -102,6 +112,7 @@ test_cipher_on_target() { pfs=$current_pfs pubkey=$current_pubkey sigalg=$current_sigalg + trusted=$current_trusted # grab the cipher and PFS key size done # if cipher is empty, that means none of the TLS version worked with @@ -113,13 +124,13 @@ test_cipher_on_target() { # if cipher contains NONE, the cipher wasn't accepted elif [ "$cipher" == '(NONE) ' ]; then - result="$cipher $protocols $pubkey $sigalg $pfs" + result="$cipher $protocols $pubkey $sigalg $trusted $pfs" verbose "handshake failed, server returned ciphersuite '$result'" return 1 # the connection succeeded else - result="$cipher $protocols $pubkey $sigalg $pfs" + result="$cipher $protocols $pubkey $sigalg $trusted $pfs" verbose "handshake succeeded, server returned ciphersuite '$result'" return 0 fi @@ -152,7 +163,11 @@ bench_cipher() { get_cipher_pref() { [ "$OUTPUTFORMAT" == "terminal" ] && [ $DEBUG -lt 1 ] && echo -n '.' local ciphersuite="$1" - local sslcommand="$OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + if [ -e $CACERTS ]; then + local sslcommand="$OPENSSLBIN s_client -CAfile $CACERTS $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + else + local sslcommand="$OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + fi verbose "Connecting to '$TARGET' with ciphersuite '$ciphersuite'" test_cipher_on_target "$sslcommand" local success=$? @@ -183,9 +198,9 @@ display_results_in_terminal() { done if [ $DOBENCHMARK -eq 1 ]; then - header="prio ciphersuite protocols pubkey_size signature_algoritm pfs_keysize avg_handshake_microsec" + header="prio ciphersuite protocols pubkey_size signature_algoritm trusted pfs_keysize avg_handshake_microsec" else - header="prio ciphersuite protocols pubkey_size signature_algorithm pfs_keysize" + header="prio ciphersuite protocols pubkey_size signature_algorithm trusted pfs_keysize" fi ctr=0 for result in "${results[@]}"; do @@ -208,7 +223,8 @@ display_results_in_json() { echo -n "\"protocols\":[\"$(echo $cipher|awk '{print $2}'|sed 's/,/","/g')\"]," echo -n "\"pubkey\":[\"$(echo $cipher|awk '{print $3}'|sed 's/,/","/g')\"]," echo -n "\"sigalg\":[\"$(echo $cipher|awk '{print $4}'|sed 's/,/","/g')\"]," - pfs=$(echo $cipher|awk '{print $5}') + echo -n "\"trusted\":\"$(echo $cipher|awk '{print $5}'|sed 's/,/","/g')\"," + pfs=$(echo $cipher|awk '{print $6}') [ "$pfs" == "" ] && pfs="None" echo -n "\"pfs\":\"$pfs\"}" ctr=$((ctr+1)) From 9931ca2a2d7d022ad48efcf8a40cf74826e76bf0 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 19:40:19 +0200 Subject: [PATCH 06/11] update README with new examples New features = new examples --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index bdc43d3..808bfc9 100644 --- a/README.md +++ b/README.md @@ -44,38 +44,38 @@ Testing plain SSL/TLS: ``` linux $ ./cipherscan www.google.com:443 ................... -prio ciphersuite protocols pfs_keysize -1 ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 ECDH,P-256,256bits -2 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 ECDH,P-256,256bits -3 ECDHE-RSA-AES128-SHA TLSv1.2 ECDH,P-256,256bits -4 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -5 AES128-GCM-SHA256 TLSv1.2 -6 AES128-SHA256 TLSv1.2 -7 AES128-SHA TLSv1.2 -8 RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -9 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 -10 ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 ECDH,P-256,256bits -11 ECDHE-RSA-AES256-SHA384 TLSv1.2 ECDH,P-256,256bits -12 ECDHE-RSA-AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -13 AES256-GCM-SHA384 TLSv1.2 -14 AES256-SHA256 TLSv1.2 -15 AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -16 ECDHE-RSA-DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits -17 DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 -18 ECDHE-RSA-AES128-SHA256 TLSv1.2 ECDH,P-256,256bits +prio ciphersuite protocols pubkey_size signature_algorithm trusted pfs_keysize +1 ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +2 ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +3 ECDHE-RSA-AES128-SHA TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +4 ECDHE-RSA-RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +5 AES128-GCM-SHA256 TLSv1.2 2048 sha1WithRSAEncryption True +6 AES128-SHA256 TLSv1.2 2048 sha1WithRSAEncryption True +7 AES128-SHA TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True +8 RC4-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True +9 RC4-MD5 SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True +10 ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +11 ECDHE-RSA-AES256-SHA384 TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +12 ECDHE-RSA-AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +13 AES256-GCM-SHA384 TLSv1.2 2048 sha1WithRSAEncryption True +14 AES256-SHA256 TLSv1.2 2048 sha1WithRSAEncryption True +15 AES256-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True +16 ECDHE-RSA-DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits +17 DES-CBC3-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 2048 sha1WithRSAEncryption True +18 ECDHE-RSA-AES128-SHA256 TLSv1.2 2048 sha1WithRSAEncryption True ECDH,P-256,256bits ``` Testing STARTTLS: ``` darwin $ ./cipherscan -o ./openssl-mine -starttls xmpp jabber.ccc.de:5222 ......... -prio ciphersuite protocols pfs_keysize -1 DHE-RSA-AES256-SHA SSLv3,TLSv1 DH,1024bits -2 AES256-SHA SSLv3,TLSv1 -3 EDH-RSA-DES-CBC3-SHA SSLv3,TLSv1 DH,1024bits -4 DES-CBC3-SHA SSLv3,TLSv1 -5 DHE-RSA-AES128-SHA SSLv3,TLSv1 DH,1024bits -6 AES128-SHA SSLv3,TLSv1 -7 RC4-SHA SSLv3,TLSv1 -8 RC4-MD5 SSLv3,TLSv1 +prio ciphersuite protocols pubkey_size signature_algorithm trusted pfs_keysize +1 DHE-RSA-AES256-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False DH,1024bits +2 AES256-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False +3 EDH-RSA-DES-CBC3-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False DH,1024bits +4 DES-CBC3-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False +5 DHE-RSA-AES128-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False DH,1024bits +6 AES128-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False +7 RC4-SHA SSLv3,TLSv1 2048 sha1WithRSAEncryption False +8 RC4-MD5 SSLv3,TLSv1 2048 sha1WithRSAEncryption False ``` From 7a92186122e83f556487331d384a5811dc7cd45a Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 4 Apr 2014 20:12:50 +0200 Subject: [PATCH 07/11] Improve scanning performance and reduce false negatives scan all the machines from top-1m.csv file, wait for completion of all jobs i=1 is an off-by-one-error support top-1m.csv files with arbitrary number of sites run scans for many hosts at a time, but don't run more than specified amount in case where default domain name doesn't resolve or doesn't have port 443 open, retry with www. prefix --- top1m/testtop1m.sh | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/top1m/testtop1m.sh b/top1m/testtop1m.sh index 52f504b..71b1b0f 100755 --- a/top1m/testtop1m.sh +++ b/top1m/testtop1m.sh @@ -1,14 +1,37 @@ #!/usr/bin/env bash parallel=50 +max_bg=400 [ ! -e "results" ] && mkdir results -i=1 -while [ $i -lt 1000000 ] + +function wait_for_jobs() { + local no_jobs + no_jobs=$(jobs | wc -l) + + while [ $no_jobs -gt $1 ]; do + sleep 1 + no_jobs=$(jobs | wc -l) + done +} + +i=0 +count=$(wc -l top-1m.csv | awk '{print $1}') +while [ $i -lt $count ] do echo processings sites $i to $((i + parallel)) - for t in $(tail -$((1000000 - $i)) top-1m.csv | head -$parallel |cut -d ',' -f 2) + for t in $(tail -$(($count - $i)) top-1m.csv | head -$parallel |cut -d ',' -f 2) do - (tcping -u 10000000 $t 443; if [ $? -gt 0 ];then continue;fi;../cipherscan $t:443 -json > results/$t )& + (tcping -u 10000000 $t 443; + if [ $? -gt 0 ];then + tcping -u 10000000 www.$t 443; + if [ $? -gt 0 ]; then + continue; + else + ../cipherscan -json www.$t:443 > results/www.$t + continue; + fi; + fi;../cipherscan -json $t:443 > results/$t )& done - sleep 7 i=$(( i + parallel)) + wait_for_jobs $max_bg done +wait From 8b2b6f591698d8e6984d29aee96a73fb272f1f43 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 4 Apr 2014 20:18:36 +0200 Subject: [PATCH 08/11] parsing of signature algorithm and key size add parsing of signature algorithm and key size from the individual results, report summary --- top1m/parse_results.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/top1m/parse_results.py b/top1m/parse_results.py index 90306d2..6fd52bd 100644 --- a/top1m/parse_results.py +++ b/top1m/parse_results.py @@ -13,6 +13,8 @@ cipherstats = defaultdict(int) pfsstats = defaultdict(int) protocolstats = defaultdict(int) handshakestats = defaultdict(int) +keysize = defaultdict(int) +sigalg = defaultdict(int) total = 0 for r,d,flist in os.walk(path): @@ -20,6 +22,10 @@ for r,d,flist in os.walk(path): """ initialize variables for stats of the current site """ temppfsstats = {} + tempkeystats = {} + tempecckeystats = {} + tempdsakeystats = {} + tempsigstats = {} ciphertypes = 0 AESGCM = False AES = False @@ -87,6 +93,17 @@ for r,d,flist in os.walk(path): DHE = True temppfsstats[entry['pfs']] = 1 + """ save the key size """ + if 'ECDSA' in entry['cipher']: + tempecckeystats[entry['pubkey'][0]] = 1 + elif 'DSS' in entry['cipher']: + tempdsakeystats[entry['pubkey'][0]] = 1 + else: + tempkeystats[entry['pubkey'][0]] = 1 + + """ save key signatures size """ + tempsigstats[entry['sigalg'][0]] = 1 + """ store the versions of TLS supported """ for protocol in entry['protocols']: if protocol == 'SSLv2': @@ -109,6 +126,16 @@ for r,d,flist in os.walk(path): for s in temppfsstats: pfsstats[s] += 1 + for s in tempkeystats: + keysize['RSA ' + s] += 1 + for s in tempecckeystats: + keysize['ECDSA ' + s] += 1 + for s in tempdsakeystats: + keysize['DSA ' + s] += 1 + + for s in tempsigstats: + sigalg[s] += 1 + """ store cipher stats """ if AESGCM: cipherstats['AES-GCM'] += 1 @@ -192,6 +219,18 @@ for stat in sorted(pfsstats): pfspercent = round(pfsstats[stat] / handshakestats['DHE'] * 100, 4) sys.stdout.write(stat.ljust(25) + " " + str(pfsstats[stat]).ljust(10) + str(percent).ljust(9) + str(pfspercent) + "\n") +print("\nCertificate sig alg Count Percent ") +print("-------------------------+---------+--------") +for stat in sorted(sigalg): + percent = round(sigalg[stat] / total * 100, 4) + sys.stdout.write(stat.ljust(25) + " " + str(sigalg[stat]).ljust(10) + str(percent).ljust(9) + "\n") + +print("\nCertificate key size Count Percent ") +print("-------------------------+---------+--------") +for stat in sorted(keysize): + percent = round(keysize[stat] / total * 100, 4) + sys.stdout.write(stat.ljust(25) + " " + str(keysize[stat]).ljust(10) + str(percent).ljust(9) + "\n") + print("\nSupported Protocols Count Percent") print("-------------------------+---------+-------") for stat in sorted(protocolstats): From 167ef8b50277f2b33a813d36e4d366bef1d7509a Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Fri, 4 Apr 2014 21:08:38 +0200 Subject: [PATCH 09/11] report number of servers that use ECDSA and RSA certificates Since use of both ECDSA and RSA certificates is easy, it is relatively simple to support both. Report the total number of such servers --- top1m/parse_results.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/top1m/parse_results.py b/top1m/parse_results.py index 6fd52bd..0a9817b 100644 --- a/top1m/parse_results.py +++ b/top1m/parse_results.py @@ -15,6 +15,7 @@ protocolstats = defaultdict(int) handshakestats = defaultdict(int) keysize = defaultdict(int) sigalg = defaultdict(int) +dsarsastack = 0 total = 0 for r,d,flist in os.walk(path): @@ -40,6 +41,8 @@ for r,d,flist in os.walk(path): TLS1 = False TLS1_1 = False TLS1_2 = False + dualstack = False + ECDSA = False """ process the file """ f_abs = os.path.join(r,f) @@ -95,11 +98,16 @@ for r,d,flist in os.walk(path): """ save the key size """ if 'ECDSA' in entry['cipher']: + ECDSA = True tempecckeystats[entry['pubkey'][0]] = 1 elif 'DSS' in entry['cipher']: tempdsakeystats[entry['pubkey'][0]] = 1 + elif 'AECDH' in entry['cipher'] or 'ADH' in entry['cipher']: + """ skip """ else: tempkeystats[entry['pubkey'][0]] = 1 + if ECDSA: + dualstack = True """ save key signatures size """ tempsigstats[entry['sigalg'][0]] = 1 @@ -133,6 +141,9 @@ for r,d,flist in os.walk(path): for s in tempdsakeystats: keysize['DSA ' + s] += 1 + if dualstack: + dsarsastack += 1 + for s in tempsigstats: sigalg[s] += 1 @@ -231,6 +242,8 @@ for stat in sorted(keysize): percent = round(keysize[stat] / total * 100, 4) sys.stdout.write(stat.ljust(25) + " " + str(keysize[stat]).ljust(10) + str(percent).ljust(9) + "\n") +sys.stdout.write("RSA/ECDSA Dual Stack".ljust(25) + " " + str(dsarsastack).ljust(10) + str(round(dsarsastack/total * 100, 4)) + "\n") + print("\nSupported Protocols Count Percent") print("-------------------------+---------+-------") for stat in sorted(protocolstats): From 9f43f3df2d8c774ca6be7867a1f134777f627764 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 01:33:33 +0200 Subject: [PATCH 10/11] add ability to ignore results from untrusted servers --- top1m/parse_results.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/top1m/parse_results.py b/top1m/parse_results.py index 0a9817b..9edf509 100644 --- a/top1m/parse_results.py +++ b/top1m/parse_results.py @@ -9,6 +9,8 @@ import sys from collections import defaultdict import os +report_untrused=False + cipherstats = defaultdict(int) pfsstats = defaultdict(int) protocolstats = defaultdict(int) @@ -43,6 +45,7 @@ for r,d,flist in os.walk(path): TLS1_2 = False dualstack = False ECDSA = False + trusted = False """ process the file """ f_abs = os.path.join(r,f) @@ -62,6 +65,9 @@ for r,d,flist in os.walk(path): """ loop over list of ciphers """ for entry in results['ciphersuite']: + if 'True' in entry['trusted']: + trusted = True + """ store the ciphers supported """ if 'AES-GCM' in entry['cipher']: if not AESGCM: @@ -126,6 +132,10 @@ for r,d,flist in os.walk(path): TLS1_2 = True json_file.close() + """ don't store stats from unusued servers """ + if report_untrused == False and trusted == False: + continue + """ done with this file, storing the stats """ if DHE or ECDHE: pfsstats['Support PFS'] += 1 From ba1031367f45079c7d1a818e791e33de7e304220 Mon Sep 17 00:00:00 2001 From: Hubert Kario Date: Sat, 5 Apr 2014 20:21:35 +0200 Subject: [PATCH 11/11] in "no-untrusted mode": filter out ADH and AECDH suites If server negotiates ADH or AECDH suite, openssl returns "ok" in cert checking. Don't mark server as trusted because of that. Don't collect statistics on servers that provide only untrusted connections. --- top1m/parse_results.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/top1m/parse_results.py b/top1m/parse_results.py index 9edf509..18c67d9 100644 --- a/top1m/parse_results.py +++ b/top1m/parse_results.py @@ -65,8 +65,11 @@ for r,d,flist in os.walk(path): """ loop over list of ciphers """ for entry in results['ciphersuite']: - if 'True' in entry['trusted']: - trusted = True + # some servers return different certificates with different + # ciphers, also we may become redirected to other server with + # different config (because over-reactive IPS) + if 'False' in entry['trusted'] and report_untrused == False: + continue; """ store the ciphers supported """ if 'AES-GCM' in entry['cipher']: @@ -115,6 +118,9 @@ for r,d,flist in os.walk(path): if ECDSA: dualstack = True + if 'True' in entry['trusted'] and not 'ADH' in entry['cipher'] and not 'AECDH' in entry['cipher']: + trusted = True + """ save key signatures size """ tempsigstats[entry['sigalg'][0]] = 1 @@ -216,6 +222,10 @@ for r,d,flist in os.walk(path): # break print("SSL/TLS survey of %i websites from Alexa's top 1 million" % total) +if report_untrused == False: + print("Stats only from connections that did provide valid certificates") + print("(or anonymous DH from servers that do also have valid certificate installed)\n") + """ Display stats """ print("\nSupported Ciphers Count Percent") print("-------------------------+---------+-------")