#!/usr/bin/env bash

###########################################################################################################################
#                                                                                                        Date: 2021-06-10 #
# check_alpaca.sh                                                                                                         #
#                                                                                                                         #
# test if application layer protocols allowing cross-protocol attacks is possible                                         #
# ok, this script just test for ALPN (former NPN) - both TLS extensions                                                   #
# NPN (Next Protocol Negotiation)                - used by SPDY                                                           #
# ALPN (Application Layer Protocol Negotiation)  - used by http/2                                                         #
#                                                                                                                         #
# Resources:                                                                                                              #
#   https://alpaca-attack.com/                                                                                            #
#   https://github.com/RUB-NDS/alpaca-code                                                                                #
#   https://www.golem.de/news/sicherheitsluecke-alpaca-angriff-zeigt-cross-protokoll-schwaeche-von-tls-2106-157143.html   #
#   https://arstechnica.com/gadgets/2021/06/hackers-can-mess-with-https-connections-by-sending-data-to-your-email-server/ #
#   https://www.heise.de/news/ALPACA-Attacke-Angreifer-koennten-mit-TLS-gesicherte-Verbindungen-attackieren-6066915.html  #
#                                                                                                                         #
###########################################################################################################################
#                                                                                                                         #
# Author: Bernd Kohler (kohler@itc.rwth-aachen.de)                                                                        #
#                                                                                                                         #
# Versions                                                                                                                #
#    0.1  initial scripting (20210610)                                                                                    #
#                                                                                                                         #
###########################################################################################################################


set -e
set -u
set -o pipefail
#set -x


NMAP="$(which nmap)" || true
ALP_NSE="/usr/share/nmap/scripts/tls-alpn.nse"
ALPN="yes"

declare -A ALPN_PORTLIST
for i in t443 t8443;do
    ALPN_PORTLIST[${i}]="closed"
done

declare -A ALPACA_PORTLIST
for i in t21 t25 t465 t587;do
    ALPACA_PORTLIST[${i}]="closed"
done

cleanup() {
    if [[ "${ALP_NSE}" != "/usr/share/nmap/scripts/tls-alpn.nse" ]]; then 
        rm -f ${ALP_NSE}
    fi
    unset ALPN_PORTLIST
    unset ALPN
    unset ALP_NSE
    unset NMAP
}


check_open_ports(){
    # check if ports in port list/array are open
    # what about 443/udp
    for i in "${!ALPN_PORTLIST[@]}"; do 
        nc -${i:0:1} -v -w 3 ${1} ${i:1} &>/dev/null && ALPN_PORTLIST[${i}]="open" || true;
    done

    echo -e "\n Port status on ${1}:"
    for i in "${!ALPN_PORTLIST[@]}"; do 
        printf "\t%5s/%s --- %s\n" "${i:1}" "$([[ "${i:0:1}" == "t" ]] && echo "tcp" || echo "udp")" "${ALPN_PORTLIST[$i]}";
    done
}

check_alpn() {

    local port_list="" 
    local alpn_list=""
    local nmap_port=""

    for i in "${!ALPN_PORTLIST[@]}"; do
        if [[ "${ALPN_PORTLIST[$i]}" == "open" ]]; then
            port_list="${port_list}${i:0:1}:${i:1} "
        fi
    done

    echo -e "\n ALPN status on ${1}:"

    for i in ${port_list}; do
        nmap_port="$(tr "[:lower:]" "[:upper:]" <<< ${i:0:1}${i:1})"
        alpn_list="$(nmap --script=tls-alpn ${1} -p ${nmap_port} | grep '|' | sed -e 's/^.*\s\s*//' | tr "\n" " " || true)"
        printf "\t%5s/%s --- %s\n" "${i:2}" "$([[ "${i:0:1}" == "t" ]] && echo "tcp" || echo "udp")" "${alpn_list}";
    done

    if [[ ! -z "${alpn_list}" ]]; then
        ALPN="no"
    fi

    if [[ "${ALPN}" == "yes" ]]; then
        echo -e "\n ALPACA status on ${1}:\n\tmight be vulnerable"
        echo -e "\n\tcheck tls-lib vesion and compare to\n\thttps://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation\n\n\tcross check with:"
        for i in "${!ALPN_PORTLIST[@]}"; do
            if [[ "${ALPN_PORTLIST[$i]}" == "open" ]]; then
               echo -e "\t\"echo | openssl s_client -alpn -connect ${1}:${i:1} | fgrep -i alpn\""
            fi
        done
    else
        echo -e "\n ALPACA status on ${1}:\n\tmight not be vulnerable\n"
    fi


}

check_additional_ports() {
    echo "check check"
}

check_route() {
    local host_list=""

    if [[ ${1} =~ : ]]; then
        ip route get ${1} &> /dev/null || exit 1
    elif [[ ${1} =~ \.[a-zA-Z]{2,}  ]]; then
        host_list="$(dig ${1} A ${1} AAAA +short | tr "\n" " ")"
        if [[ -z "${host_list}" ]]; then
            echo -e "\n\tno targets available\n"
            exit 1
        fi
        for j in $(dig ${1} A ${1} AAAA +short | grep -v '\.$' | tr "\n" " "); do 
            ip route get ${j} &> /dev/null || exit 1
        done
    else 
        ip route get ${1} &> /dev/null || exit 1
    fi
}


#####################
# main

trap cleanup EXIT SIGINT SIGKILL

if [ $# -ne 1 ]; then
    echo -e "\nusage: ${0} FQDN|IPv4|IPv6\n"
    exit
fi

# check if nmap is installed 
[[ -z "${NMAP}" ]] && (echo -e "\n\tnmap is not installed\n" ; exit )

# check if NSE script is available
if [[ ! -f /usr/share/nmap/scripts/tls-alpn.nse ]]; then
    ALP_NSE="$(mktemp)"
     wget -qO ${ALP_NSE} https://svn.nmap.org/nmap/scripts/tls-alpn.nse 
fi

# check if route to target is know
# and if target is valid
check_route ${1}

# check if alive
ping -W 1 -i 1 -c 1 ${1} &>/dev/null || exit

check_open_ports ${1}

check_alpn ${1}


##########
# EOF
