Done ciphers and http header

This commit is contained in:
Frank Breedijk 2015-12-11 12:50:42 +01:00
parent 36043ad925
commit bfbc6e0dce

View File

@ -437,7 +437,7 @@ output_finding() { # ID, IP, PORT, SEVERITY, FINDING
if ! $FIRST_FINDING; then
echo "," >> $JSONFILE
fi
echo "
echo -e "
{
'id' : '$1',
'ip' : '$2',
@ -609,26 +609,26 @@ runs_HTTP() {
case $SERVICE in
HTTP)
out " $SERVICE"
output_finding "service" "$NODEIP" "$PORT" "info" "Service detected: $SERVICE"
output_finding "service" "$NODEIP" "$PORT" "INFO" "Service detected: $SERVICE"
ret=0 ;;
IMAP|POP|SMTP|NNTP)
out " $SERVICE, thus skipping HTTP specific checks"
output_finding "service" "$NODEIP" "$PORT" "info" "Service detected: $SERVICE, thus skipping HTTP specific checks"
output_finding "service" "$NODEIP" "$PORT" "INFO" "Service detected: $SERVICE, thus skipping HTTP specific checks"
ret=0 ;;
*) if $CLIENT_AUTH; then
out "certificate based authentication => skipping all HTTP checks"
echo "certificate based authentication => skipping all HTTP checks" >$TMPFILE
output_finding "client_auth" "$NODEIP" "$PORT" "warn" "certificate based authentication => skipping all HTTP checks"
output_finding "client_auth" "$NODEIP" "$PORT" "WARN" "certificate based authentication => skipping all HTTP checks"
else
out " Couldn't determine what's running on port $PORT"
if $ASSUMING_HTTP; then
SERVICE=HTTP
out " -- ASSUMING_HTTP set though"
output_finding "service" "$NODEIP" "$PORT" "warn" "Couldn't determine service, --ASSUMING_HTTP set"
output_finding "service" "$NODEIP" "$PORT" "WARN" "Couldn't determine service, --ASSUMING_HTTP set"
ret=0
else
out ", assuming no HTTP service => skipping all HTTP checks"
output_finding "service" "$NODEIP" "$PORT" "warn" "Couldn't determine service, skipping all HTTP checks"
output_finding "service" "$NODEIP" "$PORT" "WARN" "Couldn't determine service, skipping all HTTP checks"
ret=1
fi
fi
@ -822,13 +822,20 @@ run_http_date() {
includeSubDomains() {
if grep -aiqw includeSubDomains "$1"; then
pr_litegreen ", includeSubDomains"
return 1
else
pr_litecyan ", just this domain"
return 0
fi
}
preload() {
grep -aiqw preload "$1" && pr_litegreen ", preload"
if grep -aiqw preload "$1"; then
pr_litegreen ", preload"
return 1
else
return 0
fi
}
@ -849,16 +856,27 @@ run_hsts() {
hsts_age_days=$(( hsts_age_sec / 86400))
if [[ $hsts_age_days -gt $HSTS_MIN ]]; then
pr_litegreen "$hsts_age_days days" ; out "=$hsts_age_sec s"
output_finding "hsts_time" "$NODEIP" "$PORT" "OK" "HSTS timeout $hsts_age_days days (=$hsts_age_sec seconds) > $HSTS_MIN days"
else
out "$hsts_age_sec s = "
pr_brown "$hsts_age_days days, <$HSTS_MIN days is too short"
output_finding "hsts_time" "$NODEIP" "$PORT" "NOT OK" "HSTS timeout too short. $hsts_age_days days (=$hsts_age_sec seconds) < $HSTS_MIN days"
fi
if includeSubDomains "$TMPFILE"; then
output_finding "hsts_subdomains" "$NODEIP" "$PORT" "OK" "HSTS includes subdomains"
else
output_finding "hsts_subdomains" "$NODEIP" "$PORT" "WARN" "HSTS only for this domain, consider to include subdomains as well"
fi
if preload "$TMPFILE"; then
output_finding "hsts_preload" "$NODEIP" "$PORT" "OK" "HSTS domain is marked for preloading"
else
output_finding "hsts_preload" "$NODEIP" "$PORT" "INFO" "HSTS domain is NOT marked for preloading"
fi
includeSubDomains "$TMPFILE"
preload "$TMPFILE"
#FIXME: To be checked against e.g. https://dxr.mozilla.org/mozilla-central/source/security/manager/boot/src/nsSTSPreloadList.inc
# and https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json
else
out "--"
output_finding "hsts" "$NODEIP" "$PORT" "NOT OK" "No support for HTTP Strict Transport Security"
fi
outln
@ -875,6 +893,8 @@ run_hpkp() {
local spaces=" "
local key_found=false
local i
local hpkp_headers
local first_hpkp_header
if [[ ! -s $HEADERFILE ]]; then
run_http_header "$1" || return 3
@ -886,13 +906,17 @@ run_hpkp() {
if egrep -aciw '^Public-Key-Pins|Public-Key-Pins-Report-Only' $HEADERFILE | egrep -waq "1" ; then
:
else
pr_brown "two HPKP headers: "
hpkp_headers=""
pr_brown "multiple HPKP headers: "
for i in $(newline_to_spaces "$(egrep -ai '^Public-Key-Pins' $HEADERFILE | awk -F':' '/Public-Key-Pins/ { print $1 }')"); do
pr_italic $i
hpkp_headers="$hpkp_headers$i "
out " "
done
out "\n$spaces using first "
pr_italic "$(awk -F':' '/Public-Key-Pins/ { print $1 }' $HEADERFILE | head -1), "
first_hpkp_header=`awk -F':' '/Public-Key-Pins/ { print $1 }' $HEADERFILE | head -1`
pr_italic "$first_hpkp_header, "
output_finding "hpkp_multiple" "$NODEIP" "$PORT" "WARN" "Multiple HPKP headers\n$hpkp_headers\nUsing first header: $first_hpkp_header"
fi
# remove leading Public-Key-Pins*, any colons, double quotes and trailing spaces and taking the first -- whatever that is
@ -906,8 +930,10 @@ run_hpkp() {
out "# of keys: "
if [[ $hpkp_nr_keys -eq 1 ]]; then
pr_litered "1 (NOT ok), "
output_finding "hpkp_keys" "$NODEIP" "$PORT" "NOT OK" "Only one key pinned in HPKP header, this means the site may become unavaiable if the key is revoked"
else
out "$hpkp_nr_keys, "
output_finding "hpkp_keys" "$NODEIP" "$PORT" "OK" "$hpkp_nr_keys keys pinned in HPKP header, additional keys are available if the current key is revoked"
fi
# print key=value pair with awk, then strip non-numbers, to be improved with proper parsing of key-value with awk
@ -915,13 +941,23 @@ run_hpkp() {
hpkp_age_days=$((hpkp_age_sec / 86400))
if [[ $hpkp_age_days -ge $HPKP_MIN ]]; then
pr_litegreen "$hpkp_age_days days" ; out "=$hpkp_age_sec s"
output_finding "hpkp_age" "$NODEIP" "$PORT" "OK" "HPKP age is set to $hpkp_age_days days ($hpkp_age_sec sec)"
else
out "$hpkp_age_sec s = "
pr_brown "$hpkp_age_days days (<$HPKP_MIN days is not good enough)"
output_finding "hpkp_age" "$NODEIP" "$PORT" "NOT OK" "HPKP age is set to $hpkp_age_days days ($hpkp_age_sec sec) < $HPKP_MIN days is not good enough."
fi
includeSubDomains "$TMPFILE"
preload "$TMPFILE"
if includeSubDomains "$TMPFILE"; then
output_finding "hpkp_subdomains" "$NODEIP" "$PORT" "INFO" "HPKP header is valid for subdomains as well"
else
output_finding "hpkp_subdomains" "$NODEIP" "$PORT" "INFO" "HPKP header is valid for this domain only"
fi
if preload "$TMPFILE"; then
output_finding "hpkp_preload" "$NODEIP" "$PORT" "INFO" "HPKP header is marked for browser preloading"
else
output_finding "hpkp_preload" "$NODEIP" "$PORT" "INFO" "HPKP header is NOT marked for browser preloading"
fi
[[ -s "$HOSTCERT" ]] || get_host_cert
# get the key fingerprints
@ -931,6 +967,7 @@ run_hpkp() {
if [[ "$hpkp_key_hostcert" == "$hpkp_key" ]] || [[ "$hpkp_key_hostcert" == "$hpkp_key=" ]]; then
out "\n$spaces matching host key: "
pr_litegreen "$hpkp_key"
output_finding "hpkp_keymatch" "$NODEIP" "$PORT" "OK" "Key matches a key pinned in the HPKP header"
key_found=true
fi
debugme out "\n $hpkp_key | $hpkp_key_hostcert"
@ -939,9 +976,11 @@ run_hpkp() {
out "\n$spaces"
pr_litered " No matching key for pins found "
out "(CAs pinned? -- not yet checked)"
output_finding "hpkp_keymatch" "$NODEIP" "$PORT" "WARN" "The TLS key does not match any key pinned in the HPKP header. If you pinned a CA key you can ignore this"
fi
else
out "--"
output_finding "hpkp" "$NODEIP" "$PORT" "WARN" "No support for HTTP Public Key Pinning"
fi
outln
@ -993,16 +1032,21 @@ run_server_banner() {
serverbanner=$(sed -e 's/^Server: //' -e 's/^server: //' $TMPFILE)
if [[ x"$serverbanner" == "x\n" ]] || [[ x"$serverbanner" == "x\n\r" ]] || [[ x"$serverbanner" == "x" ]]; then
outln "banner exists but empty string"
output_finding "serverbanner" "$NODEIP" "$PORT" "INFO" "\"Server\" banner exists but empty string"
else
emphasize_stuff_in_headers "$serverbanner"
[[ "$serverbanner" = *Microsoft-IIS/6.* ]] && [[ $OSSL_VER == 1.0.2* ]] && \
output_finding "serverbanner" "$NODEIP" "$PORT" "INFO" "\"Server\" banner identified: $serverbanner"
if [[ "$serverbanner" = *Microsoft-IIS/6.* ]] && [[ $OSSL_VER == 1.0.2* ]]; then
pr_litemagentaln " It's recommended to run another test w/ OpenSSL 1.01 !"
# see https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892
output_finding "IIS6_openssl_mismatch" "$NODEIP" "$PORT" "WARN" "It is recommended to rerun this test w/ OpenSSL 1.01\nSee https://github.com/PeterMosmans/openssl/issues/19#issuecomment-100897892"
fi
fi
# mozilla.github.io/server-side-tls/ssl-config-generator/
# https://support.microsoft.com/en-us/kb/245030
else
outln "(no \"Server\" line in header, interesting!)"
output_finding "serverbanner" "$NODEIP" "$PORT" "WARN" "No \"Server\" banner in header, interesting!"
fi
tmpfile_handle $FUNCNAME.txt
@ -1013,6 +1057,7 @@ run_rp_banner() {
local line
local first=true
local spaces=" "
local rp_banners=""
if [[ ! -s $HEADERFILE ]]; then
run_http_header "$1" || return 3
@ -1021,7 +1066,8 @@ run_rp_banner() {
egrep -ai '^Via:|^X-Cache|^X-Squid|^X-Varnish:|^X-Server-Name:|^X-Server-Port:|^x-forwarded' $HEADERFILE >$TMPFILE
if [[ $? -ne 0 ]]; then
outln "--"
else
output_finding "rp_header" "$NODEIP" "$PORT" "INFO" "No reverse proxy banner found"
else
while read line; do
line=$(strip_lf "$line")
if ! $first; then
@ -1030,7 +1076,9 @@ run_rp_banner() {
first=false
fi
emphasize_stuff_in_headers "$line"
rp_banners="$rp_banners\n$line"
done < $TMPFILE
output_finding "rp_header" "$NODEIP" "$PORT" "INFO" "Reverse proxy banner(s) found: $rp_banners"
fi
outln
@ -1043,6 +1091,7 @@ run_application_banner() {
local line
local first=true
local spaces=" "
local app_banners=""
if [[ ! -s $HEADERFILE ]]; then
run_http_header "$1" || return 3
@ -1051,6 +1100,7 @@ run_application_banner() {
egrep -ai '^X-Powered-By|^X-AspNet-Version|^X-Version|^Liferay-Portal|^X-OWA-Version' $HEADERFILE >$TMPFILE
if [[ $? -ne 0 ]]; then
outln "--"
output_finding "app_banner" "$NODEIP" "$PORT" "INFO" "No Applicaiton Banners found"
else
cat $TMPFILE | while read line; do
line=$(strip_lf "$line")
@ -1060,7 +1110,9 @@ run_application_banner() {
first=false
fi
emphasize_stuff_in_headers "$line"
app_banners="$app_banners\n$line"
done
output_finding "app_banner" "$NODEIP" "$PORT" "WARN" "Applicaiton Banners found: $app_banners"
fi
tmpfile_handle $FUNCNAME.txt
return 0
@ -1079,6 +1131,7 @@ run_cookie_flags() { # ARG1: Path, ARG2: path
if [[ $? -eq 0 ]]; then
nr_cookies=$(wc -l < $TMPFILE | sed 's/ //g')
out "$nr_cookies issued: "
output_finding "cookie_count" "$NODEIP" "$PORT" "INFO" "$nr_cookies cookie(s) issued at \"$1\""
if [[ $nr_cookies -gt 1 ]]; then
negative_word="NONE"
else
@ -1090,15 +1143,26 @@ run_cookie_flags() { # ARG1: Path, ARG2: path
[123456789]) pr_litegreen "$nr_secure/$nr_cookies";;
esac
out " secure, "
if [ $nr_cookies == $nr_secure ]; then
output_finding "cookie_secure" "$NODEIP" "$PORT" "OK" "All $nr_cookies cookie(s) issued at \"$1\" marked as secure"
else
output_finding "cookie_secure" "$NODEIP" "$PORT" "WARN" "$nr_secure/$nr_cookies cookie(s) issued at \"$1\" marked as secure"
fi
nr_httponly=$(grep -cai httponly $TMPFILE)
case $nr_httponly in
0) pr_brown "$negative_word" ;;
[123456789]) pr_litegreen "$nr_httponly/$nr_cookies";;
esac
out " HttpOnly"
if [ $nr_cookies == $nr_httponly ]; then
output_finding "cookie_httponly" "$NODEIP" "$PORT" "OK" "All $nr_cookies cookie(s) issued at \"$1\" marked as HttpOnly"
else
output_finding "cookie_httponly" "$NODEIP" "$PORT" "WARN" "$nr_secure/$nr_cookies cookie(s) issued at \"$1\" marked as HttpOnly"
fi
else
out "(none issued at \"$1\")"
fi
output_finding "cookie_count" "$NODEIP" "$PORT" "INFO" "No cookies issued at \"$1\""
fi
outln
tmpfile_handle $FUNCNAME.txt
@ -1123,6 +1187,7 @@ run_more_flags() {
egrep -ai "$egrep_pattern" $HEADERFILE >$TMPFILE
if [[ $? -ne 0 ]]; then
outln "--"
output_finding "sec_headers" "$NODEIP" "$PORT" "WARN" "No security (or other interesting) headers detected"
ret=1
else
ret=0
@ -1141,7 +1206,7 @@ run_more_flags() {
#pr_litegreen "$(sed 's/:.*$/:/' <<< "$result_str")"
# print value in plain text:
outln "${result_str#*:}"
output_finding "${result_str%%:*}" "$NODEIP" "$PORT" "OK" "${result_str%%:*}: ${result_str#*:}"
done
# now the same with other flags
for f2t in $other_flags2test; do
@ -1156,6 +1221,7 @@ run_more_flags() {
pr_litecyan "${result_str%%:*}:"
# print value in plain text:
outln "${result_str#*:}"
output_finding "${result_str%%:*}" "$NODEIP" "$PORT" "WARN" "${result_str%%:*}: ${result_str#*:}"
done
fi
#TODO: I am not testing for the correctness or anything stupid yet, e.g. "X-Frame-Options: allowall"
@ -1254,21 +1320,41 @@ std_cipherlists() {
debugme cat $ERRFILE
case $3 in
0) # ok to offer
[[ $sclient_success -eq 0 ]] && \
pr_greenln "offered (OK)" || \
pr_brownln "not offered (NOT ok)" ;;
if [[ $sclient_success -eq 0 ]]; then
pr_greenln "offered (OK)"
output_finding "std_$4" "$NODEIP" "$PORT" "OK" "$2 offered (OK)"
else
pr_brownln "not offered (NOT ok)"
output_finding "std_$4" "$NODEIP" "$PORT" "NOT OK" "$2 not offered (NOT OK)"
fi
;;
1) # the ugly ones
[[ $sclient_success -eq 0 ]] && \
pr_redln "offered (NOT ok)" || \
pr_greenln "not offered (OK)" ;;
if [[ $sclient_success -eq 0 ]]; then
pr_redln "offered (NOT ok)"
output_finding "std_$4" "$NODEIP" "$PORT" "NOT OK" "$2 offered (NOT OK) - ugly"
else
pr_greenln "not offered (OK)"
output_finding "std_$4" "$NODEIP" "$PORT" "OK" "$2 not offered (OK)"
fi
;;
2) # bad but not worst
[[ $sclient_success -eq 0 ]] && \
pr_literedln "offered (NOT ok)" || \
pr_litegreenln "not offered (OK)" ;;
if [[ $sclient_success -eq 0 ]]; then
pr_literedln "offered (NOT ok)"
output_finding "std_$4" "$NODEIP" "$PORT" "NOT OK" "$2 offered (NOT OK) - bad"
else
pr_litegreenln "not offered (OK)"
output_finding "std_$4" "$NODEIP" "$PORT" "OK" "$2 not offered (OK)"
fi
;;
3) # not totally bad
[[ $sclient_success -eq 0 ]] && \
pr_brownln "offered (NOT ok)" || \
outln "not offered (OK)" ;;
if [[ $sclient_success -eq 0 ]]; then
pr_brownln "offered (NOT ok)"
output_finding "std_$4" "$NODEIP" "$PORT" "NOT OK" "$2 offered (NOT OK) - not too bad"
else
outln "not offered (OK)"
output_finding "std_$4" "$NODEIP" "$PORT" "OK" "$2 not offered (OK)"
fi
;;
*) # we shouldn't reach this
pr_litemagenta "? (please report this)" ;;
esac
@ -1276,6 +1362,7 @@ std_cipherlists() {
else
singlespaces=$(echo "$2" | sed -e 's/ \+/ /g' -e 's/^ //' -e 's/ $//g' -e 's/ //g')
local_problem "No $singlespaces configured in $OPENSSL"
output_finding "std_$4" "$NODEIP" "$PORT" "WARN" "Cipher $2 ($1) not supported by local OpenSSL ($OPENSSL)"
fi
# we need 1xlf in those cases:
debugme echo
@ -1376,9 +1463,13 @@ test_just_one(){
local re='^[0-9A-Fa-f]+$'
pr_headline " Testing single cipher with "
[[ $1 =~ $re ]] && \
pr_headline "matching number pattern \"$1\" " || \
if [[ $1 =~ $re ]]; then
pr_headline "matching number pattern \"$1\" "
tjolines="$tjolines matching number pattern \"$1\"\n\n"
else
pr_headline "word pattern "\"$1\"" (ignore case) "
tjolines="$tjolines word pattern \"$1\" (ignore case)\n\n"
fi
outln
! $HAS_DH_BITS && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
outln
@ -1410,12 +1501,15 @@ test_just_one(){
neat_list $HEXC $ciph "$kx" $enc
if [[ $sclient_success -eq 0 ]]; then
pr_cyan " available"
output_finding "cipher_$HEXC" "$NODEIP" "$PORT" "INFO" "$(neat_header)\n$(neat_list $HEXC $ciph "$kx" $enc) available"
else
out " not a/v"
output_finding "cipher_$HEXC" "$NODEIP" "$PORT" "INFO" "$(neat_header)\n$(neat_list $HEXC $ciph "$kx" $enc) not a/v"
fi
outln
fi
done
exit
done
outln
@ -1431,6 +1525,7 @@ run_allciphers(){
local -i sclient_success=0
local hexcode n ciph sslvers kx auth enc mac export
local dhlen
local available
nr_ciphers=$(count_ciphers "$($OPENSSL ciphers 'ALL:COMPLEMENTOFALL:@STRENGTH' 2>$ERRFILE)")
outln
@ -1453,13 +1548,16 @@ run_allciphers(){
kx="$kx $dhlen"
fi
neat_list $HEXC $ciph "$kx" $enc
available="available"
if [[ "$SHOW_EACH_C" -ne 0 ]]; then
if [[ $sclient_success -eq 0 ]]; then
pr_cyan " available"
else
out " not a/v"
available="not a/v"
fi
fi
output_finding "cipher_$HEXC" "$NODEIP" "$PORT" "INFO" "$(neat_header)\n$(neat_list $HEXC $ciph "$kx" $enc) $available"
outln
tmpfile_handle $FUNCNAME.txt
done
@ -1473,6 +1571,8 @@ run_cipher_per_proto(){
local hexcode n ciph sslvers kx auth enc mac export
local -i sclient_success=0
local dhlen
local available
local id
pr_headlineln " Testing all locally available ciphers per protocol against the server, ordered by encryption strength "
! $HAS_DH_BITS && pr_litemagentaln " (Your $OPENSSL cannot show DH/ECDH bits)"
@ -1494,14 +1594,19 @@ run_cipher_per_proto(){
kx="$kx $dhlen"
fi
neat_list $HEXC $ciph "$kx" $enc
available="available"
if [[ "$SHOW_EACH_C" -ne 0 ]]; then
if [[ $sclient_success -eq 0 ]]; then
pr_cyan " available"
else
out " not a/v"
available="not a/v"
fi
fi
outln
id="cipher$proto"
id+="_$HEXC"
output_finding "$id" "$NODEIP" "$PORT" "INFO" "$proto_text\n$(neat_header)\n$(neat_list $HEXC $ciph "$kx" $enc) $available"
tmpfile_handle $FUNCNAME.txt
done
done
@ -2058,17 +2163,17 @@ run_std_cipherlists() {
pr_headlineln " Testing ~standard cipher lists "
outln
# see ciphers(1ssl)
std_cipherlists 'NULL:eNULL' " Null Ciphers " 1
std_cipherlists 'aNULL' " Anonymous NULL Ciphers " 1
std_cipherlists 'ADH' " Anonymous DH Ciphers " 1
std_cipherlists 'EXPORT40' " 40 Bit encryption " 1
std_cipherlists 'EXPORT56' " 56 Bit encryption " 1
std_cipherlists 'EXPORT' " Export Ciphers (general) " 1
std_cipherlists 'LOW:!ADH' " Low (<=64 Bit) " 1
std_cipherlists 'DES:!ADH:!EXPORT:!aNULL' " DES Ciphers " 1
std_cipherlists 'MEDIUM:!NULL:!aNULL:!SSLv2' " Medium grade encryption " 2
std_cipherlists '3DES:!ADH:!aNULL' " Triple DES Ciphers " 3
std_cipherlists 'HIGH:!NULL:!aNULL:!DES:!3DES' " High grade encryption " 0
std_cipherlists 'NULL:eNULL' " Null Ciphers " 1 "NULL"
std_cipherlists 'aNULL' " Anonymous NULL Ciphers " 1 "aNULL"
std_cipherlists 'ADH' " Anonymous DH Ciphers " 1 "ADH"
std_cipherlists 'EXPORT40' " 40 Bit encryption " 1 "EXPORT40"
std_cipherlists 'EXPORT56' " 56 Bit encryption " 1 "EXPORT56"
std_cipherlists 'EXPORT' " Export Ciphers (general) " 1 "EXPORT"
std_cipherlists 'LOW:!ADH' " Low (<=64 Bit) " 1 "LOW"
std_cipherlists 'DES:!ADH:!EXPORT:!aNULL' " DES Ciphers " 1 "DES"
std_cipherlists 'MEDIUM:!NULL:!aNULL:!SSLv2' " Medium grade encryption " 2 "MEDIUM"
std_cipherlists '3DES:!ADH:!aNULL' " Triple DES Ciphers " 3 "3DES"
std_cipherlists 'HIGH:!NULL:!aNULL:!DES:!3DES' " High grade encryption " 0 "HIGH"
outln
return 0
}