From 0662fa61d8f04c94a2631bf144fb65d7a9e33f32 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Mon, 15 Sep 2014 17:06:59 +0200 Subject: [PATCH 01/10] FreeBSD compatible Detect the OS and use gtimeout from sysutils/coreutils --- cipherscan | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/cipherscan b/cipherscan index eb91255..8196bae 100755 --- a/cipherscan +++ b/cipherscan @@ -4,9 +4,19 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # Author: Julien Vehent [:ulfr] - 2013 +# +# Modified by Olivier Paroz in September 2014 +# +# On FreeBSD, you will need the following ports: textproc/gnugrep and sysutils/coreutils +# +OS=`uname` DOBENCHMARK=0 BENCHMARKITER=30 +TIMEOUTBIN=timeout +if [ "${OS}" = "FreeBSD" ]; then + TIMEOUTBIN=gtimeout +fi OPENSSLBIN="$(dirname $0)/openssl" if [ -z "$CACERTS" ]; then for f in /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt; do @@ -33,10 +43,14 @@ TIMEOUT=10 usage() { echo -e "usage: $0 [-a|--allciphers] [-b|--benchmark] [-d|--delay seconds] [-D|--debug] [-j|--json] [-v|--verbose] [-o|--openssl file] [openssl s_client args] - usage: $0 -h|--help +usage: $0 -h|--help $0 attempts to connect to a target site using all the ciphersuites it knows. -Julien Vehent [:ulfr] - https://github.com/jvehent/cipherscan + +Original script by Julien Vehent. [:ulfr] - https://github.com/jvehent/cipherscan +FreeBSD version by Olivier Paroz. [:ulfr] - https://github.com/oparoz/cipherscan + +OpenSSL 1.0.2 is a REQUIREMENT and you will also need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils Port defaults to 443 @@ -165,7 +179,7 @@ test_cipher_on_target() { # Calculate the average handshake time for a specific ciphersuite bench_cipher() { local ciphersuite="$1" - local sslcommand="timeout $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + local sslcommand="$TIMEOUTBIN $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" local t="$(date +%s%N)" verbose "Benchmarking handshake on '$TARGET' with ciphersuite '$ciphersuite'" for i in $(seq 1 $BENCHMARKITER); do @@ -189,9 +203,9 @@ get_cipher_pref() { [ "$OUTPUTFORMAT" == "terminal" ] && [ $DEBUG -lt 1 ] && echo -n '.' local ciphersuite="$1" if [ -e $CACERTS ]; then - local sslcommand="timeout $TIMEOUT $OPENSSLBIN s_client -CAfile $CACERTS -status $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + local sslcommand="$TIMEOUTBIN $TIMEOUT $OPENSSLBIN s_client -CAfile $CACERTS -status $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" else - local sslcommand="timeout $TIMEOUT $OPENSSLBIN s_client -status $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" + local sslcommand="$TIMEOUTBIN $TIMEOUT $OPENSSLBIN s_client -status $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" fi verbose "Connecting to '$TARGET' with ciphersuite '$ciphersuite'" test_cipher_on_target "$sslcommand" @@ -410,7 +424,7 @@ if [ $ALLCIPHERS -gt 0 ]; then echo; echo "All accepted ciphersuites" for c in $($OPENSSLBIN ciphers -v ALL:COMPLEMENTOFALL 2>/dev/null |awk '{print $1}'|sort|uniq); do r="fail" - osslcommand="timeout $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $c" + osslcommand="$TIMEOUTBIN $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $c" test_cipher_on_target "$osslcommand" if [ $? -eq 0 ]; then r="pass" From d3b2c48e13408654f7264bbee973c2294f5360b0 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Mon, 15 Sep 2014 17:31:06 +0200 Subject: [PATCH 02/10] FreeBSD + extra chacha20 repo I've found another repository which offers 1.0.2 with chacha20 I've added some extra instructions for FreeBSD users --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3953659..48535e0 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,18 @@ On Linux x86_64 run: ./cipherscan www.google.com:443 On any other *nix or *tux run: ./cipherscan -o /path/to/openssl www.google.com:443 and watch. -The newer your version of openssl, the better results you'll get. Versions -of OpenSSL below 1.0.1 don't support TLS1.2 ciphers, elliptic curves, etc... Build your own or test what your system's OpenSSL supports. +On FreeBSD, you will need the following ports: textproc/gnugrep and sysutils/coreutils + +The newer your version of openssl, the better results you'll get. Versions of OpenSSL below 1.0.1 don't support TLS1.2 ciphers, elliptic curves, etc... +Version 1.0.2 gives extra information about the ciphers used for the key exchange. +Build your own or test what your system's OpenSSL supports. Cipherscan should work fine on Linux, Mac OS X, Solaris, Illumos, SmartOS, OpenIndiana if you specify a an openssl binary with -o. Build OpenSSL with ChaCha20-Poly1305 support (Optional) ------------------------------------------------------- -The OpenSSL binary in this repository is built for 64bit Linux. If you wish to build a version with the same features for your own platform, [the snapshot from the OpenSSL gitweb view](http://git.openssl.org/gitweb/?p=openssl.git;a=tree;h=161b23361778c155f9c174694b1db2506a2e0b52;hb=9a8646510b) and build it like this: +The OpenSSL binary in this repository is built for 64bit Linux. If you wish to build a version with the same features for your own platform, you can use [this snapshot from the OpenSSL gitweb view](http://git.openssl.org/gitweb/?p=openssl.git;a=tree;h=161b23361778c155f9c174694b1db2506a2e0b52;hb=9a8646510b) or [this Github repository](https://github.com/PeterMosmans/openssl) and build it like this: ``` ./config no-shared @@ -226,3 +229,4 @@ Contributors * Pepi Zawodsky * Michael Zeltner * Simon Deziel +* Olivier Paroz From 9438d647622be4663a74bf4a5e051dc27cb5590f Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Mon, 15 Sep 2014 17:38:47 +0200 Subject: [PATCH 03/10] OpenSSL 1.0.2 is not a requirement Back when egrep wasn't working, I tried to fix various things and 1.0.2 was allowing the script to go further without breaking. After having installed a more recent version of GNU grep, things were back to normal and 1.0.1 works fine --- cipherscan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cipherscan b/cipherscan index 8196bae..0b48c11 100755 --- a/cipherscan +++ b/cipherscan @@ -50,7 +50,7 @@ $0 attempts to connect to a target site using all the ciphersuites it knows. Original script by Julien Vehent. [:ulfr] - https://github.com/jvehent/cipherscan FreeBSD version by Olivier Paroz. [:ulfr] - https://github.com/oparoz/cipherscan -OpenSSL 1.0.2 is a REQUIREMENT and you will also need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils +On FreeBSD, you will need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils Port defaults to 443 From 3cc5001ebf254a2c8a1580b0fc2efba49345fd71 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Tue, 16 Sep 2014 02:05:01 +0200 Subject: [PATCH 04/10] SNI fix Without this fix you always get the first cert attached to an IP and not necessarily the cert attached to the domain you're trying to scan. Could be made modular in order to simulate a client which doesn't support SNI... --- cipherscan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cipherscan b/cipherscan index eb91255..e26771b 100755 --- a/cipherscan +++ b/cipherscan @@ -387,8 +387,8 @@ debug "Port: $PORT" TARGET=$HOST:$PORT debug "target: $TARGET" - -SCLIENTARGS=$(sed -e s,${TEMPTARGET},,<<<"${@}") +SNI_FIX="-servername ${HOST}" +SCLIENTARGS="$SNI_FIX $(sed -e s,${TEMPTARGET},,<<<"${@}")" debug "sclientargs: $SCLIENTARGS" From 2d75e7dc4a4a0ec210f5f8e32aa43f59f02f8216 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 21 Sep 2014 15:35:27 +0200 Subject: [PATCH 05/10] removed Mozilla handle --- cipherscan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cipherscan b/cipherscan index 0b48c11..719a68a 100755 --- a/cipherscan +++ b/cipherscan @@ -48,7 +48,7 @@ usage: $0 -h|--help $0 attempts to connect to a target site using all the ciphersuites it knows. Original script by Julien Vehent. [:ulfr] - https://github.com/jvehent/cipherscan -FreeBSD version by Olivier Paroz. [:ulfr] - https://github.com/oparoz/cipherscan +FreeBSD version by Olivier Paroz - https://github.com/oparoz/cipherscan On FreeBSD, you will need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils From 54a54aa4281ec251681856f8e40d7ea188ac50d8 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 21 Sep 2014 15:59:28 +0200 Subject: [PATCH 06/10] New option to enable SNI Simulates a SNI capable client --- cipherscan | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cipherscan b/cipherscan index 99017e3..b02e27d 100755 --- a/cipherscan +++ b/cipherscan @@ -39,6 +39,7 @@ DELAY=0 ALLCIPHERS=0 OUTPUTFORMAT="terminal" TIMEOUT=10 +SNISCAN=0 usage() { @@ -50,7 +51,7 @@ $0 attempts to connect to a target site using all the ciphersuites it knows. Original script by Julien Vehent. [:ulfr] - https://github.com/jvehent/cipherscan FreeBSD version by Olivier Paroz. [:ulfr] - https://github.com/oparoz/cipherscan -On FreeBSD, you will need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils +OpenSSL 1.0.2 is a REQUIREMENT and you will also need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils Port defaults to 443 @@ -65,6 +66,7 @@ Use one of the options below: -h | --help Shows this help text. -j | --json Output results in JSON format. -o | --openssl path/to/your/openssl binary you want to use. +-s | --sni Activates SNI -v | --verbose Increase verbosity. The rest of the arguments will be interpreted as openssl s_client argument. @@ -337,6 +339,10 @@ do -o | --openssl) OPENSSLBIN=$2 # You might want to check if you really got FILE shift 2 + ;; + -s | --sni) + SNISCAN=1 + shift ;; -a | --allciphers) ALLCIPHERS=1 @@ -401,8 +407,11 @@ debug "Port: $PORT" TARGET=$HOST:$PORT debug "target: $TARGET" -SNI_FIX="-servername ${HOST}" -SCLIENTARGS="$SNI_FIX $(sed -e s,${TEMPTARGET},,<<<"${@}")" +SNIPARAM="" +if [ $SNISCAN -gt 0 ]; then + SNIPARAM="-servername ${HOST}" +fi +SCLIENTARGS="$SNIPARAM $(sed -e s,${TEMPTARGET},,<<<"${@}")" debug "sclientargs: $SCLIENTARGS" From 4bc2539f441d329aa72620e248db19e54520aa87 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 21 Sep 2014 16:04:35 +0200 Subject: [PATCH 07/10] Local branch was out of sync... --- cipherscan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cipherscan b/cipherscan index 088631b..00feb32 100755 --- a/cipherscan +++ b/cipherscan @@ -51,7 +51,7 @@ $0 attempts to connect to a target site using all the ciphersuites it knows. Original script by Julien Vehent. [:ulfr] - https://github.com/jvehent/cipherscan FreeBSD version by Olivier Paroz - https://github.com/oparoz/cipherscan -OpenSSL 1.0.2 is a REQUIREMENT and you will also need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils +On FreeBSD, you will need the following ports on FreeBSD: textproc/gnugrep and sysutils/coreutils Port defaults to 443 From ad88f047e675223a9d7944623ef998a9d989c56f Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 21 Sep 2014 16:08:50 +0200 Subject: [PATCH 08/10] Style fix --- cipherscan | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cipherscan b/cipherscan index 00feb32..c112a31 100755 --- a/cipherscan +++ b/cipherscan @@ -15,7 +15,7 @@ DOBENCHMARK=0 BENCHMARKITER=30 TIMEOUTBIN=timeout if [ "${OS}" = "FreeBSD" ]; then - TIMEOUTBIN=gtimeout + TIMEOUTBIN=gtimeout fi OPENSSLBIN="$(dirname $0)/openssl" if [ -z "$CACERTS" ]; then @@ -66,7 +66,7 @@ Use one of the options below: -h | --help Shows this help text. -j | --json Output results in JSON format. -o | --openssl path/to/your/openssl binary you want to use. --s | --sni Activates SNI +-s | --sni Activates SNI -v | --verbose Increase verbosity. The rest of the arguments will be interpreted as openssl s_client argument. @@ -340,7 +340,7 @@ do OPENSSLBIN=$2 # You might want to check if you really got FILE shift 2 ;; - -s | --sni) + -s | --sni) SNISCAN=1 shift ;; @@ -409,7 +409,7 @@ debug "target: $TARGET" SNIPARAM="" if [ $SNISCAN -gt 0 ]; then - SNIPARAM="-servername ${HOST}" + SNIPARAM="-servername ${HOST}" fi SCLIENTARGS="$SNIPARAM $(sed -e s,${TEMPTARGET},,<<<"${@}")" debug "sclientargs: $SCLIENTARGS" From c92c1f70ce7d0a2a77c1b90558977edfe1106187 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Sun, 21 Sep 2014 16:19:23 +0200 Subject: [PATCH 09/10] Added a common location for the cert bundle on FreeBSD --- cipherscan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cipherscan b/cipherscan index c112a31..a1a6c16 100755 --- a/cipherscan +++ b/cipherscan @@ -19,7 +19,7 @@ if [ "${OS}" = "FreeBSD" ]; then fi OPENSSLBIN="$(dirname $0)/openssl" if [ -z "$CACERTS" ]; then - for f in /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt; do + for f in /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt /usr/local/share/certs/ca-root-nss.crt; do if [ -e "$f" ]; then CACERTS="$f" break From e97e57ae1f40471e3d3029dd22fbef2292683a06 Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Mon, 22 Sep 2014 02:15:39 +0200 Subject: [PATCH 10/10] Common name detector Let's extract the Common Name and all the Subject Alternative Names This has not been widely tested --- cipherscan | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/cipherscan b/cipherscan index a1a6c16..93a7391 100755 --- a/cipherscan +++ b/cipherscan @@ -108,8 +108,13 @@ test_cipher_on_target() { fi # filter out the OCSP server certificate tmp=$(awk 'BEGIN { pr="yes" } /^======================================/ { if ( pr=="yes" ) pr="no"; else pr="yes" } { if ( pr == "yes" ) print }' <<<"$tmp") + current_certcn=$(grep "subject=" <<<"$tmp"| grep -oP "(?<=CN\=)[^ |\/]+") + current_subjaltnames=$(${OPENSSLBIN} x509 -noout -text 2>/dev/null <<<"$tmp"|awk '/X509v3 Subject Alternative Name/ {getline;gsub(/ /, "", $0);print}' | tr -d "DNS:") current_cipher=$(grep "New, " <<<"$tmp"|awk '{print $5}') current_pfs=$(grep 'Server Temp Key' <<<"$tmp"|awk '{print $4$5$6$7}') + if [ -z $current_pfs ]; then + current_pfs="None" + fi 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 @@ -147,6 +152,16 @@ test_cipher_on_target() { else protocols="$protocols,$current_protocol" fi + certcns="$current_certcn" + # Let's add the alternative subject names if they exist + if [ -n "$current_subjaltnames" ]; then + IFS=',' read -ra subjaltnamesarray <<<"$current_subjaltnames" + for altname in "${subjaltnamesarray[@]}"; do + if [[ "$altname" != "$current_certcn" ]]; then + certcns="$certcns,$altname" + fi + done + fi cipher=$current_cipher pfs=$current_pfs pubkey=$current_pubkey @@ -165,13 +180,13 @@ test_cipher_on_target() { # if cipher contains NONE, the cipher wasn't accepted elif [ "$cipher" == '(NONE) ' ]; then - result="$cipher $protocols $pubkey $sigalg $trusted $tickethint $ocspstaple $pfs" + result="$cipher $protocols $pubkey $sigalg $trusted $tickethint $ocspstaple $pfs $certcns" verbose "handshake failed, server returned ciphersuite '$result'" return 1 # the connection succeeded else - result="$cipher $protocols $pubkey $sigalg $trusted $tickethint $ocspstaple $pfs" + result="$cipher $protocols $pubkey $sigalg $trusted $tickethint $ocspstaple $pfs $certcns" verbose "handshake succeeded, server returned ciphersuite '$result'" return 0 fi @@ -226,6 +241,7 @@ get_cipher_pref() { display_results_in_terminal() { # Display the results ctr=1 + local certcns local pubkey local sigalg local trusted @@ -246,6 +262,7 @@ display_results_in_terminal() { trusted=$(awk '{print $5}' <<<$cipher) tickethint=$(awk '{print $6}' <<<$cipher) ocspstaple=$(awk '{print $7}' <<<$cipher) + certcns=$(awk '{print $9}' <<<$cipher) else if [ "$pubkey" != "$(awk '{print $3}' <<<$cipher)" ]; then different=True @@ -291,11 +308,28 @@ display_results_in_terminal() { fi done|column -t echo + + matchhostname=0 + hostcertcn="Not a match" + if [ -z "$certcns" ]; then + hostcertcn="CN not found" + else + IFS=',' read -ra certcnsarray <<<"$certcns" + for certcn in "${certcnsarray[@]}"; do + if [[ "$certcn" == "$HOST" ]]; then + matchhostname=1 + hostcertcn="$certcn" + break + fi + done + + + fi if [ $different != "True" ]; then if [ "$trusted" == "True" ]; then - echo "Certificate: trusted, $pubkey bit, $sigalg signature" + echo "Certificate: $hostcertcn, trusted, $pubkey bit, $sigalg signature" else - echo "Certificate: UNTRUSTED, $pubkey bit, $sigalg signature" + echo "Certificate: $hostcertcn, UNTRUSTED, $pubkey bit, $sigalg signature" fi echo "TLS ticket lifetime hint: $tickethint" fi @@ -304,6 +338,13 @@ display_results_in_terminal() { else echo "OCSP stapling: not supported" fi + if [ $matchhostname -eq 0 ]; then + echo "WARNING - None of the CNs match the hostname given" + fi + if [ -n "$certcns" ]; then + echo "Here is the list of common names found in the certificate" + echo $certcns + fi } @@ -315,6 +356,7 @@ 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')\"]," + echo -n "\"certcns\":[\"$(echo $cipher|awk '{print $9}'|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')\"]," echo -n "\"trusted\":\"$(echo $cipher|awk '{print $5}'|sed 's/,/","/g')\","