Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
5amu committed Dec 11, 2021
0 parents commit 8bf9362
Showing 1 changed file with 289 additions and 0 deletions.
289 changes: 289 additions & 0 deletions dnshunter.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
#!/bin/sh
#
# Author: 5amu
#

# @TODO:
# BGP assessment: ROA / IRR / ASN georedundancy
# ASN verify ROA: whois -h whois.bgpmon.net " --roa <asnid> <subnet/netmask>"
# ASN lookup for IP: whois -h whois.cymru.com "-v <ip>"
# ASN whois -h whois.ripe.net " -T route 47.73.31.237"
# SPF record validation
# DMARC record validation
# DKIM record validation

### IO from blackarch.org/strap.sh
### simple error message wrapper
###############################################################################

BOLD="$(tput bold)"; RESET="$(tput sgr0)"
RED="$(tput setaf 1)"; GREEN="$(tput setaf 2)"; YELLOW="$(tput setaf 3)"

# simple error message wrapper
err() { echo >&2 "${BOLD}${RED}[-] ERROR: ${*}${RESET}"; exit 1337; }

# simple warning message wrapper
warn() { echo >&2 "${BOLD}${YELLOW}[!] WARNING: ${*}${RESET}"; }

# simple message wrapper
msg() { echo "${BOLD}${GREEN}[+] ${*}${RESET}"; }

# simple echo info wrapper
info() { echo "${*}"; }

### DNS version and banner
###############################################################################

version()
{
msg "[version] Checking info of nameservers"
# get all nameservers for a specific domain and launch nmap on them
for ns in $NAMESERVERS; do
nmap --script dns-nsid -p 53 -Pn -sV -oG - "$ns"
dig version.bind CHAOS TXT @"$ns" +short
done
}

### SOA information and suggestions
### https://en.wikipedia.org/wiki/SOA_record
###############################################################################

soa_info_msg()
{
[ ${1} -eq 0 ] && return

info "It is reccomended to set this records properly, following the"
info "RIPE-203 guides (https://www.ripe.net/publications/docs/ripe-203),"
info "to improve performance due to less computational steps to comform"
info "to global standards. This might be unapplicable depending on the"
info "purpose. TLDR: if you know what you're doing, this is fine."
}

soa_info()
{
# Variable to be set if verbose message is needed
ret_code=0

msg "[soa_info] Running SOA checks"
soa=$( dig SOA $1 +short +nostats )

name=$( echo "$soa" | awk '{print $1}' )
soa_r=$( echo "$soa" | awk '{print $2}' )
serial=$( echo "$soa" | awk '{print $3}' )
refresh=$( echo "$soa" | awk '{print $4}' )
retry=$( echo "$soa" | awk '{print $5}' )
expire=$( echo "$soa" | awk '{print $6}' )
ttl=$( echo "$soa" | awk '{print $7}' )

# check if the soa values are compliant with RIPE-203
# https://www.ripe.net/publications/docs/ripe-203
#####################################################

info "Zone name: $name"
info "Start of Authority: $soa_r"

# if serial is not in the acceptable range, then does not follow RIPE-203
# dummy_u serial created today, with id=99, dummy_l is created at time 0
# (1 gen 1970) with id=00, this value should tell the day and revision in
# which it was last modified
dummy_u="$( date +'%Y%m%d' )99"; dummy_l="$( date -d @0 +'%Y%m%d' )00"
if date +'%Y%m%d' -d "${serial%??}" >/dev/null 2>&1 \
|| [ $dummy_u -lt $serial ] || [ $dummy_l -gt $serial ]; then
warn "Serial number: $serial - should follow standard (RIPE-203)"
ret_code=1
else
info "Serial number: $serial"
fi

# refresh and retry time(s) should be fairly high if the zone is stable
if [ $(date -d @86400 +'%d%H%M%S') -gt $(date -d @$refresh +'%d%H%M%S') ]; then
warn "Refresh: $refresh - should be higher (RIPE-203)"
ret_code=1
else
info "Refresh: $refresh"
fi
if [ $(date -d @7200 +'%d%H%M%S') -gt $(date -d @$retry +'%d%H%M%S') ]; then
warn "Retry: $retry - should be higher (RIPE-203)"
ret_code=1
else
info "Retry: $retry"
fi


# a value of a week or two has proven to be way too short, so a longer
# time must be used
if [ $(date -d @3600000 +'%d%H%M%S') -gt $(date -d @$expire +'%d%H%M%S') ]; then
warn "Expire: $expire - should be higher (RIPE-203)"
ret_code=1
else
info "Expire: $expire"
fi

# this could be correct either way depending on the paradigm, so no checks
# will be performed on this value
info "TTL: $ttl"

return $ret_code
}

### DNS glue records for nameservers
### https://www.barryodonovan.com/2011/02/16/querying-for-dns-glue-records
###############################################################################

glue_record_msg()
{
[ ${1} -eq 0 ] && return
info "Glue records are meant to avoid cyclic queries between nameservers"
info "You should 'Glue' records for NS in the additional section of the"
info "answer. The severity of this misconfiguration is arguably medium"
}

glue_record()
{
msg "[glue_records] Looking for configured glue record for nameservers"
if ! dig NS $1 +trace +noall +additional | grep "${1}.*A" >/dev/null; then
warn "GLUE record not set for nameservers"
return 1
fi
}

### DNS zone transfer checker
### https://www.iana.org/go/rfc1035
### https://www.rfc-editor.org/rfc/rfc5936.html
###############################################################################

zone_transfer_msg()
{
[ ${1} -eq 0 ] && return
info "The nameserver allows zone transfers from unauthorized sources, this"
info "leads to the disclosure of all the zone's domains handled by the dns"
info "(at best). In the worse case scenario, the attacker might be able to"
info "get ownership on the zone handled by the dns."
}

zone_transfer()
{
ret_code=0
msg "[zone_transfer] Checking dns misconfigurations"
for ns in $NAMESERVERS; do
output="$( dig AXFR "$1" @"$ns" +nostats | grep -v SOA )"
if echo "$output" | grep -v "^;\|^$" >/dev/null; then
records=$( echo "$output" | grep -v "^;\|^$" )
warn "Found zone transfer: $ns"
info "$records" \
| awk '{ printf $1 " ==> (" $4 ") "; $1=$2=$3=$4=""; print $0; }' \
| tr -s ' '
ret_code=1
fi
done
return $ret_code
}

### is DNSSEC implemented
###############################################################################

dnssec_msg()
{
[ ${1} -eq 0 ] && return
echo ""
}

dnssec()
{
msg "[dnssec] Running checks for compliance"
if ! dig DNSKEY "$1" +short | grep ''; then
warn "Failed: DNSSEC not implemented"
return 1
fi
}

### Mail records
### https://dmarcian.com/spf-syntax-table/
###############################################################################

spf_msg()
{
[ ${1} -eq 0 ] && return
echo ""
}

spf()
{
msg "[spf] checking SPF record(s)"
ret_code=0
for ns in $NAMESERVERS; do
_spf="$( dig TXT $1 +short @$ns | grep -i 'v=spf1' )"
if [ -z "$_spf" ]; then
warn "No SPF for $1 in $ns"
elif echo $_spf | grep -- '-all' >/dev/null; then
info "Secure SPF for $1 in $ns"
ret_code=1
elif echo $_spf | grep -- '~all' >/dev/null; then
warn "Partially Secure (~all) SPF for $1 in $ns"
ret_code=1
elif echo $_spf | grep -- '+all' >/dev/null; then
warn "Insecure (+all) SPF for $1 in $ns"
ret_code=1
elif echo $_spf | grep -- '?all' >/dev/null; then
warn "Insecure (?all) SPF for $1 in $ns"
ret_code=1
fi
done; return $ret_code
}


###############################################################################
###############################################################################

usage()
{
echo "Usage: dns-hunter.sh [-h] [-v] [-a] -d DOMAIN"
echo ""
echo "-h|--help Display help and exit"
echo "-v|--verbose Show verbose output"
echo "-a|--aggressive Run in aggressive mode"
echo "-d|--domain Specify target domain"
echo "-n|--file-ns File with nameservers (new-line separated)"
echo ""
}

# check for needed software
NEEDED="whois dig nmap"
if ! command -v $( echo $NEEDED ) >/dev/null; then
err "The script needs the following binaries to work: $NEEDED"
fi

VERBOSE=0; AGGRESS=0
while [ $# -ne 0 ]; do case $1 in
-h|--help) usage; exit 0 ;;
-v|--verbose) VERBOSE=1 ;;
-a|--aggressive) AGGRESS=1 ;;
-d|--domain) shift; TARGET=$1 ;;
-n|--file-ns) shift; NAMESERVERS="$1" ;;
*) usage; err "Unrecognized option $1" ;;
esac; shift; done

if [ -z $TARGET ]; then
err "Target domain not defined (-d|--domain)"
fi

if [ -n "$NAMESERVERS" ] && [ -f "$NAMESERVERS" ]; then
export NAMESERVERS="$( cat "$NAMESERVERS" | tr '\n' ' ' )"
else
export NAMESERVERS=$( dig NS "$TARGET" +short | sed 's/\.$//g' | tr '\n' ' ' )
fi

if [ $AGGRESS -eq 1 ]; then
version $TARGET
fi

checks="zone_transfer soa_info glue_record dnssec spf"
for check in $checks; do
if ${check} "${TARGET}"; then
msg "No misconfiguration found"
else
echo
${check}_msg "${VERBOSE}"
fi; echo
done

0 comments on commit 8bf9362

Please sign in to comment.