Merge pull request #5 from mzeltner/master

Cleaned up options and documented custom OpenSSL build
master
Julien Vehent 2014-04-04 21:26:59 -04:00
commit afcc92db02
2 changed files with 146 additions and 68 deletions

View File

@ -2,32 +2,47 @@ CipherScan
========== ==========
A very simple way to find out which SSL ciphersuites are supported by a target. A very simple way to find out which SSL ciphersuites are supported by a target.
Run: ./cipherscan www.google.com:443 On Linux x86_64 run: ./cipherscan www.google.com:443
And watch. 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. Older versions The newer your version of openssl, the better results you'll get. Versions
of OpenSSL don't support TLS1.2 ciphers, elliptic curves, etc... Build Your Own! 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.
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:
```
./config no-shared
make
```
And get the binary from `app/openssl`. (`./config` will ask you to run `make depend` which will fail - for our purposes this step is not required)
Options Options
------- -------
Enable benchmarking by setting DOBENCHMARK to 1 at the top of the script.
You can use one of the options below (only one. yes, I know...)
Use '-v' to get more stuff to read.
Use '-a' to force openssl to test every single cipher it know.
Use '-json' to output the results in json format
``` ```
$ ./cipherscan -json www.google.com:443 -a | --allciphers Test all known ciphers individually at the end.
-b | --benchmark Activate benchmark mode.
-d | --delay Pause for n seconds between connections
-D | --debug Output ALL the information.
-h | --help Shows this help text.
-j | --json Output results in JSON format.
-o | --openssl path/to/your/openssl binary you want to use.
-v | --verbose Increase verbosity.
``` ```
Example Example
------- -------
Testing plain SSL/TLS:
``` ```
$ ./cipherscan www.google.com:443 linux $ ./cipherscan www.google.com:443
................... ...................
prio ciphersuite protocols pfs_keysize prio ciphersuite protocols pfs_keysize
1 ECDHE-RSA-CHACHA20-POLY1305 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 1 ECDHE-RSA-CHACHA20-POLY1305 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits
@ -48,7 +63,11 @@ prio ciphersuite protocols pfs_keysize
16 ECDHE-RSA-AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 16 ECDHE-RSA-AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits
17 AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2 17 AES128-SHA256 SSLv3,TLSv1,TLSv1.1,TLSv1.2
18 AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2 18 AES128-SHA SSLv3,TLSv1,TLSv1.1,TLSv1.2
$ ./cipherscan -starttls xmpp jabber.ccc.de:5222 ```
Testing STARTTLS:
```
darwin $ ./cipherscan -o ./openssl-mine -starttls xmpp jabber.ccc.de:5222
......... .........
prio ciphersuite protocols pfs_keysize prio ciphersuite protocols pfs_keysize
1 DHE-RSA-AES256-SHA SSLv3,TLSv1 DH,1024bits 1 DHE-RSA-AES256-SHA SSLv3,TLSv1 DH,1024bits

View File

@ -8,28 +8,17 @@
DOBENCHMARK=0 DOBENCHMARK=0
BENCHMARKITER=30 BENCHMARKITER=30
OPENSSLBIN="$(dirname $0)/openssl" OPENSSLBIN="$(dirname $0)/openssl"
TIMEOUT=10
CIPHERSUITE="ALL:COMPLEMENTOFALL" CIPHERSUITE="ALL:COMPLEMENTOFALL"
HOST=$(sed -e 's/:.*//'<<<"${@: -1}") DEBUG=0
PORT=$(sed -e 's/.*://'<<<"${@: -1}")
if [ "$HOST" = "$PORT" ]; then
PORT=443
fi
TARGET=$HOST:$PORT
SCLIENTARGS=$(sed -e "s/${@: -1}//" -e "s/-v//" -e "s/-a//" -e "s/-json//" <<<"$@")
VERBOSE=0 VERBOSE=0
DELAY=0
ALLCIPHERS=0 ALLCIPHERS=0
OUTPUTFORMAT="terminal" OUTPUTFORMAT="terminal"
REQUEST="GET / HTTP/1.1
Host: $TARGET
Connection: close
"
usage() { usage() {
echo -e "usage: $0 [-a|-v|-json] [openssl s_client args] <target:port> echo -e "usage: $0 [-a|--allciphers] [-b|--benchmark] [-d|--delay seconds] [-D|--debug] [-j|--json] [-v|--verbose] [-o|--openssl file] [openssl s_client args] <target:port>
usage: $0 -h|--help
$0 attempts to connect to a target site using all the ciphersuites it knowns. $0 attempts to connect to a target site using all the ciphersuites it knowns.
Julien Vehent [:ulfr] - https://github.com/jvehent/cipherscan Julien Vehent [:ulfr] - https://github.com/jvehent/cipherscan
@ -38,29 +27,36 @@ Port defaults to 443
example: $ $0 www.google.com:443 example: $ $0 www.google.com:443
Use one of the options below as the first argument: Use one of the options below:
-v increase verbosity
-a test all known ciphers individually at the end -a | --allciphers Test all known ciphers individually at the end.
-json output results in json format -b | --benchmark Activate benchmark mode.
-d | --delay Pause for n seconds between connections
-D | --debug Output ALL the information.
-h | --help Shows this help text.
-j | --json Output results in JSON format.
-o | --openssl path/to/your/openssl binary you want to use.
-v | --verbose Increase verbosity.
The rest of the arguments will be interpreted as openssl s_client argument. The rest of the arguments will be interpreted as openssl s_client argument.
This enables checking smtp/imap/pop3/ftp/xmpp via -starttls This enables checking smtp/imap/pop3/ftp/xmpp via -starttls
example: $0 -starttls xmpp jabber.ccc.de:5222 EXAMPLES: $0 -starttls xmpp jabber.ccc.de:5222
OpenSSL path can be changed in the OPENSSLBIN variable
Benchmarking can be enabled in the DOBENCHMARK variable
" "
exit 1
} }
verbose() { verbose() {
if [ $VERBOSE -eq 1 ];then if [ $VERBOSE != 0 ]; then
echo $@ echo "$@" >&2
fi fi
} }
debug(){
if [ $DEBUG == 1 ]; then
echo Debug: "$@" >&2
fi
}
# Connect to a target host with the selected ciphersuite # Connect to a target host with the selected ciphersuite
test_cipher_on_target() { test_cipher_on_target() {
@ -70,16 +66,13 @@ test_cipher_on_target() {
pfs="" pfs=""
for tls_version in "-ssl2" "-ssl3" "-tls1" "-tls1_1" "-tls1_2" for tls_version in "-ssl2" "-ssl3" "-tls1" "-tls1_1" "-tls1_2"
do do
local tmp=$(mktemp) debug echo \"quit\\n\" \| $sslcommand $tls_version
$sslcommand $tls_version 1>"$tmp" 2>/dev/null << EOF local tmp=$(echo "quit\n" | $sslcommand $tls_version 1>/dev/stdout 2>/dev/null)
$REQUEST current_cipher=$(grep "New, " <<<"$tmp"|awk '{print $5}')
EOF current_pfs=$(grep 'Server Temp Key' <<<"$tmp"|awk '{print $4$5$6$7}')
current_cipher=$(grep "New, " $tmp|awk '{print $5}') current_protocol=$(egrep "^\s+Protocol\s+:" <<<"$tmp"|awk '{print $3}')
current_pfs=$(grep 'Server Temp Key' $tmp|awk '{print $4$5$6$7}')
current_protocol=$(grep -E "^\s+Protocol\s+:" $tmp|awk '{print $3}')
if [[ -z "$current_protocol" || "$current_cipher" == '(NONE)' ]]; then if [[ -z "$current_protocol" || "$current_cipher" == '(NONE)' ]]; then
# connection failed, try again with next TLS version # connection failed, try again with next TLS version
rm "$tmp"
continue continue
fi fi
# connection succeeded, add TLS version to positive results # connection succeeded, add TLS version to positive results
@ -91,7 +84,6 @@ EOF
cipher=$current_cipher cipher=$current_cipher
pfs=$current_pfs pfs=$current_pfs
# grab the cipher and PFS key size # grab the cipher and PFS key size
rm "$tmp"
done done
# if cipher is empty, that means none of the TLS version worked with # if cipher is empty, that means none of the TLS version worked with
# the current cipher # the current cipher
@ -118,13 +110,12 @@ EOF
# Calculate the average handshake time for a specific ciphersuite # Calculate the average handshake time for a specific ciphersuite
bench_cipher() { bench_cipher() {
local ciphersuite="$1" local ciphersuite="$1"
local sslcommand="timeout $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" local sslcommand="$OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite"
local t="$(date +%s%N)" local t="$(date +%s%N)"
verbose "Benchmarking handshake on '$TARGET' with ciphersuite '$ciphersuite'" verbose "Benchmarking handshake on '$TARGET' with ciphersuite '$ciphersuite'"
for i in $(seq 1 $BENCHMARKITER); do for i in $(seq 1 $BENCHMARKITER); do
$sslcommand 2>/dev/null 1>/dev/null << EOF debug Connection $i
$REQUEST (echo "quit\n" | $sslcommand 2>/dev/null 1>/dev/null)
EOF
if [ $? -gt 0 ]; then if [ $? -gt 0 ]; then
break break
fi fi
@ -140,9 +131,9 @@ EOF
# Connect to the target and retrieve the chosen cipher # Connect to the target and retrieve the chosen cipher
# recursively until the connection fails # recursively until the connection fails
get_cipher_pref() { get_cipher_pref() {
[ "$OUTPUTFORMAT" == "terminal" ] && echo -n '.' [ "$OUTPUTFORMAT" == "terminal" ] && [ $DEBUG -lt 1 ] && echo -n '.'
local ciphersuite="$1" local ciphersuite="$1"
local sslcommand="timeout $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite" local sslcommand="$OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $ciphersuite"
verbose "Connecting to '$TARGET' with ciphersuite '$ciphersuite'" verbose "Connecting to '$TARGET' with ciphersuite '$ciphersuite'"
test_cipher_on_target "$sslcommand" test_cipher_on_target "$sslcommand"
local success=$? local success=$?
@ -153,6 +144,7 @@ get_cipher_pref() {
get_cipher_pref "!$pciph:$ciphersuite" get_cipher_pref "!$pciph:$ciphersuite"
return 0 return 0
fi fi
sleep $DELAY
} }
@ -203,20 +195,85 @@ display_results_in_json() {
echo ']}' echo ']}'
} }
# UNKNOWNOPTIONS=""
while :
do
case $1 in
-h | --help | -\?)
usage
exit 0 # This is not an error, User asked help. Don't do "exit 1"
;;
-o | --openssl)
OPENSSLBIN=$2 # You might want to check if you really got FILE
shift 2
;;
-a | --allciphers)
ALLCIPHERS=1
shift
;;
-v | --verbose)
# Each instance of -v adds 1 to verbosity
VERBOSE=$((VERBOSE+1))
shift
;;
-j | -json | --json | --JSON)
OUTPUTFORMAT="json"
shift
;;
-b | --benchmark)
DOBENCHMARK=1
shift
;;
-D | --debug)
DEBUG=1
shift
;;
-d | --delay)
DELAY=$2
shift 2
;;
--) # End of all options
shift
break
;;
# -*)
# UNKNOWNOPTIONS=$((UNKNOWNOPTIONS+$1))
# # echo "WARN: Unknown option (ignored): $1" >&2
# shift
# ;;
*) # no more options we understand.
break
;;
esac
done
[[ -z $1 || "$1" == "-h" || "$1" == "--help" ]] && usage if [ $VERBOSE != 0 ] ; then
if [ ! -z $2 ]; then echo "Loading $($OPENSSLBIN ciphers -v $CIPHERSUITE 2>/dev/null|grep Kx|wc -l) ciphersuites from $(echo -n $($OPENSSLBIN version 2>/dev/null))"
if [ "$1" == "-v" ]; then $OPENSSLBIN ciphers ALL 2>/dev/null
VERBOSE=1
echo "Loading $($OPENSSLBIN ciphers -v $CIPHERSUITE 2>/dev/null|grep Kx|wc -l) ciphersuites from $(echo -n $($OPENSSLBIN version 2>/dev/null))"
$OPENSSLBIN ciphers ALL 2>/dev/null
elif [ "$1" == "-a" ]; then
ALLCIPHERS=1
elif [ "$1" == "-json" ]; then
OUTPUTFORMAT="json"
fi
fi fi
# echo paramters left: $@
TEMPTARGET=$(sed -e 's/^.* //'<<<"${@}")
HOST=$(sed -e 's/:.*//'<<<"${TEMPTARGET}")
PORT=$(sed -e 's/.*://'<<<"${TEMPTARGET}")
# Default to https if no port given
if [ "$HOST" = "$PORT" ]; then
PORT=443
fi
debug "host: $HOST"
debug "Port: $PORT"
TARGET=$HOST:$PORT
debug "target: $TARGET"
SCLIENTARGS=$(sed -e s,${TEMPTARGET},,<<<"${@}")
debug "sclientargs: $SCLIENTARGS"
cipherspref=(); cipherspref=();
results=() results=()
@ -235,11 +292,13 @@ if [ $ALLCIPHERS -gt 0 ]; then
echo; echo "All accepted ciphersuites" echo; echo "All accepted ciphersuites"
for c in $($OPENSSLBIN ciphers -v ALL:COMPLEMENTOFALL 2>/dev/null |awk '{print $1}'|sort|uniq); do for c in $($OPENSSLBIN ciphers -v ALL:COMPLEMENTOFALL 2>/dev/null |awk '{print $1}'|sort|uniq); do
r="fail" r="fail"
osslcommand="timeout $TIMEOUT $OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $c" osslcommand="$OPENSSLBIN s_client $SCLIENTARGS -connect $TARGET -cipher $c"
test_cipher_on_target "$osslcommand" test_cipher_on_target "$osslcommand"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
r="pass" r="pass"
fi fi
echo "$c $r"|awk '{printf "%-35s %s\n",$1,$2}' echo "$c $r"|awk '{printf "%-35s %s\n",$1,$2}'
debug "Sleeping for $DELAY."
sleep $DELAY
done done
fi fi