230 lines
6.3 KiB
Bash
Executable File
230 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# bash socket implementation of checking the availability of SSLv2 protocol
|
|
# and ciphers on a remote server (loosely based on my bash-heartbleed implementation).
|
|
#
|
|
# Author: Dirk Wetter, GPLv2 see https://testssl.sh/LICENSE.txt
|
|
|
|
# it helps to wireshark:
|
|
# /<path>/openssl s_client -state -ssl2 -connect AA.BB.YYY.XXX:443 </dev/null
|
|
# /<path>/openssl s_client -state -debug -ssl2 -connect AA.BB.YYY.XXX:443 </dev/null
|
|
|
|
V2_HELLO_CIPHERSPEC_LENGTH=0 # initialize
|
|
IFILE=./mapping-rfc.txt
|
|
NODE=""
|
|
COL_WIDTH=32
|
|
DEBUG=${DEBUG:-0}
|
|
USLEEP_REC=${USLEEP_REC:-0.2}
|
|
USLEEP_SND=${USLEEP_SND:-0.1} # 1 second wait until otherwise specified
|
|
MAX_WAITSOCK=2
|
|
SOCK_REPLY_FILE=""
|
|
NW_STR=""
|
|
|
|
|
|
# 9 cipher specs SSLv2:
|
|
SSLv2_CIPHER_SPECS="
|
|
05 00 80
|
|
03 00 80
|
|
01 00 80
|
|
07 00 c0
|
|
08 00 80
|
|
06 00 40
|
|
04 00 80
|
|
02 00 80
|
|
00 00 00"
|
|
|
|
# SSLV2 chello:
|
|
SSLv2_CLIENT_HELLO="
|
|
,80,34 # length (here: 52)
|
|
,01 # Client Hello
|
|
,00,02 # SSLv2
|
|
,00,1b # cipher spec length (here: 27 )
|
|
,00,00 # session ID length
|
|
,00,10 # challenge length
|
|
,05,00,80 # 1st cipher
|
|
,03,00,80 # 2nd
|
|
,01,00,80 # 3rd
|
|
,07,00,c0 # 4th
|
|
,08,00,80 # 5th
|
|
,06,00,40 # 6th
|
|
,04,00,80 # 7th
|
|
,02,00,80 # 8th
|
|
,00,00,00 # 9th
|
|
,29,22,be,b3,5a,01,8b,04,fe,5f,80,03,a0,13,eb,c4 # Challenge
|
|
"
|
|
|
|
# only classical V2 ciphers are used here, see http://max.euston.net/d/tip_sslciphers.html
|
|
|
|
# there are v3 in v2!!! : https://tools.ietf.org/html/rfc6101#appendix-E
|
|
# Cipher specifications introduced in version 3.0 can be included in version 2.0 client hello messages using
|
|
# the syntax below. [..]
|
|
# V2CipherSpec (see Version 3.0 name) = { 0x00, CipherSuite }; !!!!
|
|
|
|
# see:
|
|
# http://max.euston.net/d/tip_ssldump.html
|
|
# https://idea.popcount.org/2012-06-16-dissecting-ssl-handshake/
|
|
# https://books.google.de/books?id=LfsC03f8oGsC&pg=PA592&lpg=PA592&dq=sslv2+server+hello+struct&source=bl&ots=JWeSD-9pwH&sig=lMzhxTdybJ3tfWC2p9ltIOKlIso&hl=en&sa=X&ei=U3WmVKzyNoTgOOeigNAP&ved=0CDUQ6AEwAw
|
|
|
|
|
|
help() {
|
|
echo
|
|
echo "Syntax $0 <hostname>"
|
|
echo
|
|
exit 1
|
|
}
|
|
|
|
|
|
parse_hn_port() {
|
|
PORT=443 # unless otherwise auto-determined, see below
|
|
NODE="$1"
|
|
|
|
# strip "https", supposed it was supplied additionally
|
|
echo $NODE | grep -q 'https://' && NODE=`echo $NODE | sed -e 's/https\:\/\///' `
|
|
|
|
# strip trailing urlpath
|
|
NODE=`echo $NODE | sed -e 's/\/.*$//'`
|
|
|
|
# determine port, supposed it was supplied additionally
|
|
echo $NODE | grep -q ':' && PORT=`echo $NODE | sed 's/^.*\://'` && NODE=`echo $NODE | sed
|
|
's/\:.*$//'`
|
|
}
|
|
|
|
# arg1: formatted string here in the code
|
|
code2network() {
|
|
NW_STR=`echo "$1" | sed -e 's/,/\\\x/g' | sed -e 's/# .*$//g' -e 's/ //g' -e '/^$/d' | tr -d '\n' | tr -d '\t'`
|
|
}
|
|
|
|
socksend_clienthello() {
|
|
code2network "$SSLv2_CLIENT_HELLO"
|
|
data=`echo $NW_STR`
|
|
[[ "$DEBUG" -ge 3 ]] && echo "\"$data\""
|
|
printf -- "$data" >&5 2>/dev/null &
|
|
sleep $USLEEP_SND
|
|
}
|
|
|
|
sockread_serverhello() {
|
|
[[ "x$2" = "x" ]] && maxsleep=$MAX_WAITSOCK || maxsleep=$2
|
|
ret=0
|
|
|
|
SOCK_REPLY_FILE=`mktemp /tmp/ddreply.XXXXXX` || exit 7
|
|
dd bs=$1 of=$SOCK_REPLY_FILE count=1 <&5 2>/dev/null &
|
|
pid=$!
|
|
|
|
while true; do
|
|
if ! ps ax | grep -v grep | grep -q $pid; then
|
|
break # didn't reach maxsleep yet
|
|
kill $pid >&2 2>/dev/null
|
|
fi
|
|
sleep $USLEEP_REC
|
|
maxsleep=$(($maxsleep - 1))
|
|
[[ $maxsleep -le 0 ]] && break
|
|
done
|
|
|
|
if ps ax | grep -v grep | grep -q $pid; then
|
|
# time's up and dd is still alive --> timeout
|
|
kill $pid >&2 2>/dev/null
|
|
wait $pid 2>/dev/null
|
|
ret=3 # means killed
|
|
fi
|
|
|
|
return $ret
|
|
}
|
|
|
|
display_sslv2serverhello() {
|
|
|
|
# server hello: in hex representation, see below
|
|
# byte 1+2: length of server hello 0123
|
|
# 3: 04=Handshake message, server hello 45
|
|
# 4: session id hit or not (boolean: 00=false, this 67
|
|
# is the normal case)
|
|
# 5: certificate type, 01 = x509 89
|
|
# 6+7 version (00 02 = SSLv2) 10-13
|
|
# 8+9 certificate length 14-17
|
|
# 10+11 cipher spec length 17-20
|
|
# 12+13 connection id length
|
|
# [certificate length] ==> certificate
|
|
# [cipher spec length] ==> ciphers GOOD: HERE ARE ALL CIPHERS ALREADY!
|
|
|
|
v2_hello_ascii=`hexdump -v -e '16/1 "%02X"' $1`
|
|
[[ "$DEBUG" -eq 4 ]] && echo $v2_hello_ascii # one line without any blanks
|
|
[[ -z $v2_hello_ascii ]] && return 0 # no server hello received
|
|
|
|
# now scrape two bytes out of the reply per byte
|
|
v2_hello_initbyte="${v2_hello_ascii:0:1}" # normally this belongs to the next, should be 8!
|
|
v2_hello_length="${v2_hello_ascii:1:3}" # + 0x8000 see above
|
|
v2_hello_handshake="${v2_hello_ascii:4:2}"
|
|
v2_hello_cert_length="${v2_hello_ascii:14:4}"
|
|
v2_hello_cipherspec_length="${v2_hello_ascii:18:4}"
|
|
V2_HELLO_CIPHERSPEC_LENGTH=`printf "%d\n" "0x$v2_hello_cipherspec_length"`
|
|
|
|
if [[ $v2_hello_initbyte != "8" ]] || [[ $v2_hello_handshake != "04" ]]; then
|
|
[[ $DEBUG -ge 1 ]] && echo "$v2_hello_initbyte / $v2_hello_handshake"
|
|
return 1
|
|
fi
|
|
|
|
if [[ $DEBUG -ge 2 ]]; then
|
|
echo "SSLv2 server hello length: 0x0$v2_hello_length"
|
|
echo "SSLv2 certificate length: 0x$v2_hello_cert_length"
|
|
echo "SSLv2 cipher spec length: 0x$v2_hello_cipherspec_length"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
|
|
#### main
|
|
|
|
[[ -z "$1" ]] && help # hostname
|
|
|
|
echo
|
|
parse_hn_port "$1"
|
|
|
|
if ! exec 5<> /dev/tcp/$NODE/$PORT; then
|
|
echo "`basename $0`: unable to connect to $NODE:$PORT"
|
|
exit 2
|
|
fi
|
|
# socket is now open with fd 5
|
|
|
|
[[ "$DEBUG" -ge 1 ]] && printf "sending client hello...\n\n"
|
|
socksend_clienthello
|
|
|
|
sockread_serverhello 32768 0
|
|
[[ "$DEBUG" -ge 1 ]] && printf "\nreading server hello...\n\n"
|
|
if [[ "$DEBUG" -eq 3 ]]; then
|
|
#xxd -c$COL_WIDTH $SOCK_REPLY_FILE | head -3
|
|
#hexdump -v -e '"%04_ax: " 16/1 "%02X " "\n"' $SOCK_REPLY_FILE | head -6
|
|
hexdump -C $SOCK_REPLY_FILE | head -6
|
|
echo
|
|
fi
|
|
|
|
display_sslv2serverhello "$SOCK_REPLY_FILE"
|
|
|
|
# see https://secure.wand.net.nz/trac/libprotoident/wiki/SSL
|
|
lines=`cat "$SOCK_REPLY_FILE" 2>/dev/null | hexdump -C | wc -l`
|
|
|
|
printf "Protocol: "; tput bold
|
|
if [[ "$lines" -gt 1 ]] ;then
|
|
tput setaf 1; printf "available with $(($V2_HELLO_CIPHERSPEC_LENGTH / 3 )) ciphers"
|
|
ret=0
|
|
else
|
|
tput setaf 2; printf "NOT available"
|
|
ret=1
|
|
fi
|
|
tput sgr0
|
|
|
|
|
|
[[ "$DEBUG" -ge 2 ]] && printf " ($lines lines)"
|
|
echo
|
|
|
|
|
|
# closing fd:
|
|
exec 5<&-
|
|
exec 5>&-
|
|
|
|
rm $SOCK_REPLY_FILE
|
|
|
|
echo
|
|
exit 0
|
|
|
|
# vim:tw=110:ts=5:sw=5
|
|
# $Id: prototype.ssl2proto-check.bash,v 1.9 2015/01/07 22:56:22 dirkw Exp $
|