limit number of forks needed to speed up execution

bash has a built in regular expression processor, we can match
lines using =~

moreover, stuff that will match while being inside parentheses is
later available in the BASH_REMATCH array

the IFS (Internal Field Separator) by default includes space, tab and
new line, as such we can use it to split longer lines to separate
words, just as awk '{print $1}' can, just need to put the value to
an array for that

we also don't have to use $(echo $var) when assigning variables, $var
is enough

bash has also built in substitution engine, so we can do ${var/,/ & }
to switch all commas to ampersands when using the variable
This commit is contained in:
Hubert Kario 2014-10-18 17:20:20 +02:00
parent 26204115bf
commit f1faa28a8c
1 changed files with 146 additions and 71 deletions

View File

@ -64,7 +64,9 @@ ratelimit() {
}
usage() {
echo -e "usage: $0 [-a|--allciphers] [-b|--benchmark] [--capath directory] [--saveca] [--savecrt directory] [-d|--delay seconds] [-D|--debug] [-j|--json] [-v|--verbose] [-o|--openssl file] [openssl s_client args] <target:port>
echo -e "usage: $0 [-a|--allciphers] [-b|--benchmark] [--capath directory]
[--saveca] [--savecrt directory] [-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 knows.
@ -110,7 +112,7 @@ debug(){
c_hash() {
local h=$(${OPENSSLBIN} x509 -hash -noout -in "$1/$2" 2>/dev/null)
for num in $(seq 0 100); do
for ((num=0; num<=100; num++)) ; do
if [[ $1/${h}.${num} -ef $2 ]]; then
# file already linked, ignore
break
@ -125,6 +127,106 @@ c_hash() {
done
}
parse_openssl_output() {
# clear variables in case matching doesn't hit them
current_ocspstaple="False"
current_cipher=""
current_pfs=""
current_protocol=""
current_tickethint="None"
current_pubkey=0
current_trusted="False"
current_sigalg="None"
certs_found=0
current_raw_certificates=()
while read line; do
# check if there isn't OCSP response data (response and responder cert)
if [[ $line =~ ^====================================== ]]; then
while read data; do
# check if there is a OCSP response in output
if [[ $data =~ OCSP\ Response\ Data ]]; then
current_ocspstaple="True"
continue
fi
# skip all data from a OCSP response
if [[ $data =~ ^====================================== ]]; then
break
fi
done
continue
fi
# extract selected cipher
if [[ $line =~ New,\ ]]; then
local match=($line)
current_cipher="${match[4]}"
continue
fi
# extract data about selected temporary key
if [[ $line =~ Server\ Temp\ Key ]]; then
local match=($line)
current_pfs="${match[3]}${match[4]}${match[5]}${match[6]}"
continue
fi
# extract used protocol
if [[ $line =~ ^Protocol\ + ]]; then
local match=($line)
current_protocol="${match[2]}"
continue
fi
# extract session ticket hint
if [[ $line =~ ticket\ lifetime\ hint ]]; then
local match=($line)
current_tickethint="${match[5]}"
continue
fi
# extract size of server public key
if [[ $line =~ Server\ public\ key\ is\ ]]; then
local match=($line)
current_pubkey="${match[4]}"
continue
fi
# check if connection used trused certificate
if [[ $line =~ Verify\ return\ code:\ 0 ]]; then
current_trusted="True"
continue
fi
# extract certificates
if [[ $line =~ -----BEGIN\ CERTIFICATE----- ]]; then
current_raw_certificates[$certs_found]="$line"$'\n'
while read data; do
current_raw_certificates[$certs_found]+="$data"$'\n'
if [[ $data =~ -----END\ CERTIFICATE----- ]]; then
break
fi
done
certs_found=$((certs_found+1))
continue
fi
done
# if we found any certs in output, process the first one and extract
# the signature algorithm on it (it's the server's certificate)
if [[ $certs_found -gt 0 ]]; then
local ossl_out=$(${OPENSSLBIN} x509 -noout -text 2>/dev/null <<<"${current_raw_certificates[0]}")
while read data; do
if [[ $data =~ Signature\ Algorithm ]]; then
local match=($data)
current_sigalg="${match[2]}"
fi
done <<<"$ossl_out"
fi
}
# Connect to a target host with the selected ciphersuite
test_cipher_on_target() {
local sslcommand=$@
@ -143,60 +245,31 @@ test_cipher_on_target() {
# in SSLv3 mode OpenSSL just ignores the setting so it's ok
# -status exception is ignored in SSLv2, go figure
if [ "$tls_version" == "-ssl2" ]; then
cmnd=$(sed 's/-servername\ [^ ]*//'<<<$sslcommand)
if [[ "$sslcommand" =~ (.*)(-servername\ [^ ]*)(.*) ]]; then
cmnd="${BASH_REMATCH[1]} ${BASH_REMATCH[3]}"
else
cmnd="$sslcommand"
fi
else
cmnd=$sslcommand
fi
ratelimit
debug echo \"Q\" \| $cmnd $tls_version
local tmp=$(echo "Q" | $cmnd $tls_version 1>/dev/stdout 2>/dev/null)
if grep 'OCSP Response Data' <<<"$tmp" >/dev/null; then
current_ocspstaple="True"
else
current_ocspstaple="False"
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")
# session metadata
current_cipher=$(awk '/New, / {print $5; exit}' <<<"$tmp")
current_pfs=$(awk '/Server Temp Key/ {print $4$5$6$7; exit}' <<<"$tmp")
current_protocol=$(awk '/^ +Protocol +:/ {print $3; exit}' <<<"$tmp")
current_tickethint=$(awk '/ticket lifetime hint/ {print $6; exit}' <<<"$tmp")
if [ -z $current_tickethint ]; then
current_tickethint=None
fi
# certificate metadata
current_pubkey=$(awk '/Server public key is / {print $5;exit}' <<<"$tmp")
if [ -z $current_pubkey ]; then
current_pubkey=0
fi
current_sigalg=$(${OPENSSLBIN} x509 -noout -text 2>/dev/null <<<"$tmp"|\
awk '/Signature Algorithm/ {print $3; exit}') || 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
parse_openssl_output <<<"$tmp"
verbose "selected cipher is '$current_cipher'"
verbose "using protocol '$current_protocol'"
# collect certificate data
current_certificates=""
local certificate_count=$(grep --count -- '-----END CERTIFICATE-----'\
<<<"$tmp")
local certificate_count=$certs_found
debug "server presented $certificate_count certificates"
local i
for ((i=0; i<$certificate_count; i=i+1 )); do
# extract i'th certificate
local cert=$(awk -v i=$i 'BEGIN { output=0;n=0 }
/-----BEGIN CERTIFICATE-----/ { output=1 }
output==1 { if (n==i) print }
/-----END CERTIFICATE-----/ { output=0; n++ }' <<<"$tmp")
local cert="${current_raw_certificates[$i]}"
# put the output to an array instead running awk '{print $1}'
local cksum=($(cksum <<<"$cert"))
# compare the values not just checksums so that eventual collision
@ -210,9 +283,9 @@ test_cipher_on_target() {
fi
# compute sha256 fingerprint of the certificate
local sha256sum=$(${OPENSSLBIN} x509 -outform DER\
local sha256sum=($(${OPENSSLBIN} x509 -outform DER\
<<<"$cert" 2>/dev/null |\
${OPENSSLBIN} dgst -sha256 -r 2>/dev/null| awk '{print $1}')
${OPENSSLBIN} dgst -sha256 -r 2>/dev/null))
# check if it is a CA certificate
local isCA="False"
@ -234,7 +307,7 @@ test_cipher_on_target() {
# signed ones)
local saved="False"
if ${OPENSSLBIN} verify "${trust_source[@]}" \
-untrusted <(echo "$tmp") <(echo "$cert") 2>/dev/null | \
-untrusted <(printf "%s" "${current_raw_certificates[@]}") <(echo "$cert") 2>/dev/null | \
grep ': OK$' >/dev/null; then
# if the certificate is an intermediate CA it may be useful
@ -354,7 +427,7 @@ get_cipher_pref() {
if [ $success -eq 0 ]; then
cipherspref=("${cipherspref[@]}" "$result")
ciphercertificates=("${ciphercertificates[@]}" "$certificates")
pciph=($(echo $result))
pciph=($result)
get_cipher_pref "!$pciph:$ciphersuite"
return 0
fi
@ -371,30 +444,32 @@ display_results_in_terminal() {
local different=False
echo "Target: $TARGET"; echo
for cipher in "${cipherspref[@]}"; do
pciph=($(echo $cipher))
# get first in array
pciph=($cipher)
if [ $DOBENCHMARK -eq 1 ]; then
bench_cipher "$pciph"
r="$ctr $cipher $cipherbenchms"
else
r="$ctr $cipher"
fi
local cipher_data=($cipher)
if [ $ctr -eq 1 ]; then
pubkey=$(awk '{print $3}' <<<$cipher)
sigalg=$(awk '{print $4}' <<<$cipher)
trusted=$(awk '{print $5}' <<<$cipher)
tickethint=$(awk '{print $6}' <<<$cipher)
ocspstaple=$(awk '{print $7}' <<<$cipher)
pubkey="${cipher_data[2]}"
sigalg="${cipher_data[3]}"
trusted="${cipher_data[4]}"
tickethint="${cipher_data[5]}"
ocspstaple="${cipher_data[6]}"
else
if [ "$pubkey" != "$(awk '{print $3}' <<<$cipher)" ]; then
if [ "$pubkey" != "${cipher_data[2]}" ]; then
different=True
fi
if [ "$sigalg" != "$(awk '{print $4}' <<<$cipher)" ]; then
if [ "$sigalg" != "${cipher_data[3]}" ]; then
different=True
fi
if [ "$trusted" != "$(awk '{print $5}' <<<$cipher)" ]; then
if [ "$trusted" != "${cipher_data[4]}" ]; then
different=True
fi
if [ "$tickethint" != "$(awk '{print $6}' <<<$cipher)" ]; then
if [ "$tickethint" != "${cipher_data[5]}" ]; then
different=True
fi
fi
@ -454,18 +529,19 @@ display_results_in_json() {
ctr=0
echo -n "{\"target\":\"$TARGET\",\"utctimestamp\":\"$(date -u '+%FT%T.0Z')\",\"serverside\":\"${serverside}\",\"ciphersuite\": ["
for cipher in "${cipherspref[@]}"; do
local cipher_arr=($cipher)
[ $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 "\"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')\","
echo -n "{\"cipher\":\"${cipher_arr[0]}\","
echo -n "\"protocols\":[\"${cipher_arr[1]//,/\",\"}\"],"
echo -n "\"pubkey\":[\"${cipher_arr[2]//,/\",\"}\"],"
echo -n "\"sigalg\":[\"${cipher_arr[3]//,/\",\"}\"],"
echo -n "\"trusted\":\"${cipher_arr[4]//,/\",\"}\","
if [[ -n $CAPATH ]]; then
echo -n "\"certificates\":[${ciphercertificates[$ctr]}],"
fi
echo -n "\"ticket_hint\":\"$(echo $cipher|awk '{print $6}')\","
echo -n "\"ocsp_stapling\":\"$(echo $cipher|awk '{print $7}')\","
pfs=$(echo $cipher|awk '{print $8}')
echo -n "\"ticket_hint\":\"${cipher_arr[5]}\","
echo -n "\"ocsp_stapling\":\"${cipher_arr[6]}\","
pfs="${cipher_arr[7]}"
[ "$pfs" == "" ] && pfs="None"
echo -n "\"pfs\":\"$pfs\"}"
ctr=$((ctr+1))
@ -483,25 +559,25 @@ test_serverside_ordering() {
# server supports just two ciphers, so rotate them, that should be enough
elif [[ ${#cipherspref[@]} -eq 2 ]]; then
local cipher=$(awk '{print $1}' <<< ${cipherspref[1]})
local cipher=(${cipherspref[1]})
prefered="$cipher"
ciphersuite=$cipher
cipher=$(awk '{print $1}' <<< ${cipherspref[0]})
cipher=(${cipherspref[0]})
ciphersuite+=":$cipher"
# server supports 3 or more ciphers, rotate all three. This is necessary because google does
# select first client provided cipher, if it is either CDHE-RSA-AES128-GCM-SHA256 or
# ECDHE-RSA-CHACHA20-POLY1305
else
local cipher=$(awk '{print $1}' <<< ${cipherspref[2]})
local cipher=(${cipherspref[2]})
prefered="$cipher"
ciphersuite="$cipher"
cipher=$(awk '{print $1}' <<< ${cipherspref[1]})
cipher=(${cipherspref[1]})
ciphersuite+=":$cipher"
cipher=$(awk '{print $1}' <<< ${cipherspref[0]})
cipher=(${cipherspref[0]})
ciphersuite+=":$cipher"
fi
@ -517,7 +593,7 @@ test_serverside_ordering() {
if [ $? -ne 0 ]; then
serverside="True"
else
local selected=$(awk '{print $1}' <<< $result)
local selected=($result)
if [[ $selected == $prefered ]]; then
serverside="False"
else
@ -614,8 +690,7 @@ TARGET=$HOST:$PORT
debug "target: $TARGET"
# test our openssl is usable
tmp="$($OPENSSLBIN -h 2>&1 1>/dev/null)"
if [ $? -gt 0 ]; then
if [ ! -x $OPENSSLBIN ]; then
OPENSSLBIN=$(which openssl)
if [ "$OUTPUTFORMAT" == "terminal" ]; then
echo "custom openssl not executable, falling back to system one from $OPENSSLBIN"