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 9f06829486
commit c4a8495a54
1 changed files with 146 additions and 71 deletions

View File

@ -62,7 +62,9 @@ ratelimit() {
} }
usage() { 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 usage: $0 -h|--help
$0 attempts to connect to a target site using all the ciphersuites it knows. $0 attempts to connect to a target site using all the ciphersuites it knows.
@ -108,7 +110,7 @@ debug(){
c_hash() { c_hash() {
local h=$(${OPENSSLBIN} x509 -hash -noout -in "$1/$2" 2>/dev/null) 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 if [[ $1/${h}.${num} -ef $2 ]]; then
# file already linked, ignore # file already linked, ignore
break break
@ -123,6 +125,106 @@ c_hash() {
done 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 # Connect to a target host with the selected ciphersuite
test_cipher_on_target() { test_cipher_on_target() {
local sslcommand=$@ local sslcommand=$@
@ -138,60 +240,31 @@ test_cipher_on_target() {
# in SSLv3 mode OpenSSL just ignores the setting so it's ok # in SSLv3 mode OpenSSL just ignores the setting so it's ok
# -status exception is ignored in SSLv2, go figure # -status exception is ignored in SSLv2, go figure
if [ "$tls_version" == "-ssl2" ]; then 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 else
cmnd=$sslcommand cmnd=$sslcommand
fi fi
ratelimit ratelimit
debug echo \"Q\" \| $cmnd $tls_version debug echo \"Q\" \| $cmnd $tls_version
local tmp=$(echo "Q" | $cmnd $tls_version 1>/dev/stdout 2>/dev/null) 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 parse_openssl_output <<<"$tmp"
current_cipher=$(awk '/New, / {print $5; exit}' <<<"$tmp") verbose "selected cipher is '$current_cipher'"
current_pfs=$(awk '/Server Temp Key/ {print $4$5$6$7; exit}' <<<"$tmp") verbose "using protocol '$current_protocol'"
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
# collect certificate data # collect certificate data
current_certificates="" current_certificates=""
local certificate_count=$(grep --count -- '-----END CERTIFICATE-----'\ local certificate_count=$certs_found
<<<"$tmp")
debug "server presented $certificate_count certificates" debug "server presented $certificate_count certificates"
local i local i
for ((i=0; i<$certificate_count; i=i+1 )); do for ((i=0; i<$certificate_count; i=i+1 )); do
# extract i'th certificate # extract i'th certificate
local cert=$(awk -v i=$i 'BEGIN { output=0;n=0 } local cert="${current_raw_certificates[$i]}"
/-----BEGIN CERTIFICATE-----/ { output=1 }
output==1 { if (n==i) print }
/-----END CERTIFICATE-----/ { output=0; n++ }' <<<"$tmp")
# put the output to an array instead running awk '{print $1}' # put the output to an array instead running awk '{print $1}'
local cksum=($(cksum <<<"$cert")) local cksum=($(cksum <<<"$cert"))
# compare the values not just checksums so that eventual collision # compare the values not just checksums so that eventual collision
@ -205,9 +278,9 @@ test_cipher_on_target() {
fi fi
# compute sha256 fingerprint of the certificate # compute sha256 fingerprint of the certificate
local sha256sum=$(${OPENSSLBIN} x509 -outform DER\ local sha256sum=($(${OPENSSLBIN} x509 -outform DER\
<<<"$cert" 2>/dev/null |\ <<<"$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 # check if it is a CA certificate
local isCA="False" local isCA="False"
@ -229,7 +302,7 @@ test_cipher_on_target() {
# signed ones) # signed ones)
local saved="False" local saved="False"
if ${OPENSSLBIN} verify "${trust_source[@]}" \ 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 grep ': OK$' >/dev/null; then
# if the certificate is an intermediate CA it may be useful # if the certificate is an intermediate CA it may be useful
@ -348,7 +421,7 @@ get_cipher_pref() {
if [ $success -eq 0 ]; then if [ $success -eq 0 ]; then
cipherspref=("${cipherspref[@]}" "$result") cipherspref=("${cipherspref[@]}" "$result")
ciphercertificates=("${ciphercertificates[@]}" "$certificates") ciphercertificates=("${ciphercertificates[@]}" "$certificates")
pciph=($(echo $result)) pciph=($result)
get_cipher_pref "!$pciph:$ciphersuite" get_cipher_pref "!$pciph:$ciphersuite"
return 0 return 0
fi fi
@ -365,30 +438,32 @@ display_results_in_terminal() {
local different=False local different=False
echo "Target: $TARGET"; echo echo "Target: $TARGET"; echo
for cipher in "${cipherspref[@]}"; do for cipher in "${cipherspref[@]}"; do
pciph=($(echo $cipher)) # get first in array
pciph=($cipher)
if [ $DOBENCHMARK -eq 1 ]; then if [ $DOBENCHMARK -eq 1 ]; then
bench_cipher "$pciph" bench_cipher "$pciph"
r="$ctr $cipher $cipherbenchms" r="$ctr $cipher $cipherbenchms"
else else
r="$ctr $cipher" r="$ctr $cipher"
fi fi
local cipher_data=($cipher)
if [ $ctr -eq 1 ]; then if [ $ctr -eq 1 ]; then
pubkey=$(awk '{print $3}' <<<$cipher) pubkey="${cipher_data[2]}"
sigalg=$(awk '{print $4}' <<<$cipher) sigalg="${cipher_data[3]}"
trusted=$(awk '{print $5}' <<<$cipher) trusted="${cipher_data[4]}"
tickethint=$(awk '{print $6}' <<<$cipher) tickethint="${cipher_data[5]}"
ocspstaple=$(awk '{print $7}' <<<$cipher) ocspstaple="${cipher_data[6]}"
else else
if [ "$pubkey" != "$(awk '{print $3}' <<<$cipher)" ]; then if [ "$pubkey" != "${cipher_data[2]}" ]; then
different=True different=True
fi fi
if [ "$sigalg" != "$(awk '{print $4}' <<<$cipher)" ]; then if [ "$sigalg" != "${cipher_data[3]}" ]; then
different=True different=True
fi fi
if [ "$trusted" != "$(awk '{print $5}' <<<$cipher)" ]; then if [ "$trusted" != "${cipher_data[4]}" ]; then
different=True different=True
fi fi
if [ "$tickethint" != "$(awk '{print $6}' <<<$cipher)" ]; then if [ "$tickethint" != "${cipher_data[5]}" ]; then
different=True different=True
fi fi
fi fi
@ -448,18 +523,19 @@ display_results_in_json() {
ctr=0 ctr=0
echo -n "{\"target\":\"$TARGET\",\"utctimestamp\":\"$(date -u '+%FT%T.0Z')\",\"serverside\":\"${serverside}\",\"ciphersuite\": [" echo -n "{\"target\":\"$TARGET\",\"utctimestamp\":\"$(date -u '+%FT%T.0Z')\",\"serverside\":\"${serverside}\",\"ciphersuite\": ["
for cipher in "${cipherspref[@]}"; do for cipher in "${cipherspref[@]}"; do
local cipher_arr=($cipher)
[ $ctr -gt 0 ] && echo -n ',' [ $ctr -gt 0 ] && echo -n ','
echo -n "{\"cipher\":\"$(echo $cipher|awk '{print $1}')\"," echo -n "{\"cipher\":\"${cipher_arr[0]}\","
echo -n "\"protocols\":[\"$(echo $cipher|awk '{print $2}'|sed 's/,/","/g')\"]," echo -n "\"protocols\":[\"${cipher_arr[1]//,/\",\"}\"],"
echo -n "\"pubkey\":[\"$(echo $cipher|awk '{print $3}'|sed 's/,/","/g')\"]," echo -n "\"pubkey\":[\"${cipher_arr[2]//,/\",\"}\"],"
echo -n "\"sigalg\":[\"$(echo $cipher|awk '{print $4}'|sed 's/,/","/g')\"]," echo -n "\"sigalg\":[\"${cipher_arr[3]//,/\",\"}\"],"
echo -n "\"trusted\":\"$(echo $cipher|awk '{print $5}'|sed 's/,/","/g')\"," echo -n "\"trusted\":\"${cipher_arr[4]//,/\",\"}\","
if [[ -n $CAPATH ]]; then if [[ -n $CAPATH ]]; then
echo -n "\"certificates\":[${ciphercertificates[$ctr]}]," echo -n "\"certificates\":[${ciphercertificates[$ctr]}],"
fi fi
echo -n "\"ticket_hint\":\"$(echo $cipher|awk '{print $6}')\"," echo -n "\"ticket_hint\":\"${cipher_arr[5]}\","
echo -n "\"ocsp_stapling\":\"$(echo $cipher|awk '{print $7}')\"," echo -n "\"ocsp_stapling\":\"${cipher_arr[6]}\","
pfs=$(echo $cipher|awk '{print $8}') pfs="${cipher_arr[7]}"
[ "$pfs" == "" ] && pfs="None" [ "$pfs" == "" ] && pfs="None"
echo -n "\"pfs\":\"$pfs\"}" echo -n "\"pfs\":\"$pfs\"}"
ctr=$((ctr+1)) ctr=$((ctr+1))
@ -477,25 +553,25 @@ test_serverside_ordering() {
# server supports just two ciphers, so rotate them, that should be enough # server supports just two ciphers, so rotate them, that should be enough
elif [[ ${#cipherspref[@]} -eq 2 ]]; then elif [[ ${#cipherspref[@]} -eq 2 ]]; then
local cipher=$(awk '{print $1}' <<< ${cipherspref[1]}) local cipher=(${cipherspref[1]})
prefered="$cipher" prefered="$cipher"
ciphersuite=$cipher ciphersuite=$cipher
cipher=$(awk '{print $1}' <<< ${cipherspref[0]}) cipher=(${cipherspref[0]})
ciphersuite+=":$cipher" ciphersuite+=":$cipher"
# server supports 3 or more ciphers, rotate all three. This is necessary because google does # 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 # select first client provided cipher, if it is either CDHE-RSA-AES128-GCM-SHA256 or
# ECDHE-RSA-CHACHA20-POLY1305 # ECDHE-RSA-CHACHA20-POLY1305
else else
local cipher=$(awk '{print $1}' <<< ${cipherspref[2]}) local cipher=(${cipherspref[2]})
prefered="$cipher" prefered="$cipher"
ciphersuite="$cipher" ciphersuite="$cipher"
cipher=$(awk '{print $1}' <<< ${cipherspref[1]}) cipher=(${cipherspref[1]})
ciphersuite+=":$cipher" ciphersuite+=":$cipher"
cipher=$(awk '{print $1}' <<< ${cipherspref[0]}) cipher=(${cipherspref[0]})
ciphersuite+=":$cipher" ciphersuite+=":$cipher"
fi fi
@ -511,7 +587,7 @@ test_serverside_ordering() {
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
serverside="True" serverside="True"
else else
local selected=$(awk '{print $1}' <<< $result) local selected=($result)
if [[ $selected == $prefered ]]; then if [[ $selected == $prefered ]]; then
serverside="False" serverside="False"
else else
@ -608,8 +684,7 @@ TARGET=$HOST:$PORT
debug "target: $TARGET" debug "target: $TARGET"
# test our openssl is usable # test our openssl is usable
tmp="$($OPENSSLBIN -h 2>&1 1>/dev/null)" if [ ! -x $OPENSSLBIN ]; then
if [ $? -gt 0 ]; then
OPENSSLBIN=$(which openssl) OPENSSLBIN=$(which openssl)
if [ "$OUTPUTFORMAT" == "terminal" ]; then if [ "$OUTPUTFORMAT" == "terminal" ]; then
echo "custom openssl not executable, falling back to system one from $OPENSSLBIN" echo "custom openssl not executable, falling back to system one from $OPENSSLBIN"