#!/usr/bin/env bash set -o pipefail export LC_ALL=C # COLOURS # standard terminal output hues red='\033[0;31m' green='\033[0;32m' yellow='\033[1;33m' cyan='\033[0;36m' nc='\033[0m' # CONFIGURATION # defaults to mullvad dns for privacy-respecting resolution resolver="@194.242.2.2" timeout_val=2 ua="mozilla/5.0 (x11; linux x86_64) applewebkit/537.36" # USAGE GUIDE # displays instructions if arguments are missing usage() { printf "${cyan}usage:${nc} reconctl [-t timeout] \n" printf " -t set network timeout in seconds (default: 2)\n" printf " -h show help menu\n\n" exit 1 } while getopts "t:h" opt; do case ${opt} in t) timeout_val=$OPTARG ;; h) usage ;; *) usage ;; esac done shift $((OPTIND - 1)) domain="$1" [[ -z "$domain" ]] && usage # INPUT VALIDATION # rejects malformed hostnames before they reach dig/curl/openssl/whois if [[ ! "$domain" =~ ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]]; then printf "${red}error: '%s' is not a valid domain${nc}\n" "$domain" exit 1 fi # HELPER FUNCTIONS # standardises section headers for clean output section_header() { printf "\n${cyan}%s${nc}\n" "$1" } # DEPENDENCY AUDIT # validates required packages before execution for cmd in dig curl jq openssl whois awk; do if ! command -v "$cmd" >/dev/null 2>&1; then printf "${red}error: missing dependency '%s'${nc}\n" "$cmd" exit 1 fi done # DNS ANALYSIS # extracts primary records and security configurations section_header "DNS RECORDS" ipv4=$(dig $resolver +short A "$domain") ipv6=$(dig $resolver +short AAAA "$domain") cname=$(dig $resolver +short CNAME "$domain") mx=$(dig $resolver +short MX "$domain") caa=$(dig $resolver +short CAA "$domain") txt=$(dig $resolver +short TXT "$domain" | grep -Ei "spf|dmarc" || true) dnskey=$(dig $resolver +short DNSKEY "$domain") printf " ipv4:\n%s\n" "$([[ -n "$ipv4" ]] && echo "$ipv4" | sed 's/^/ /' || echo " n/a")" printf " ipv6:\n%s\n" "$([[ -n "$ipv6" ]] && echo "$ipv6" | sed 's/^/ /' || echo " n/a")" printf " cname:\n %s\n" "${cname:-none detected}" printf " nameservers:\n%s\n" "$(dig $resolver +short NS "$domain" | sed 's/^/ /')" printf " mail exchangers:\n%s\n" "$([[ -n "$mx" ]] && echo "$mx" | sed 's/^/ /' || echo " none detected")" printf " caa records:\n%s\n" "$([[ -n "$caa" ]] && echo "$caa" | sed 's/^/ /' || echo " none detected")" printf " security txt:\n%s\n" "$([[ -n "$txt" ]] && echo "$txt" | sed 's/^/ /' || echo " none detected")" printf " dnssec: %s\n" "$([[ -n "$dnskey" ]] && echo "enabled" || echo "not detected")" # REVERSE LOOKUP # checks ptr records for resolved v4 and v6 addresses section_header "REVERSE DNS" if [[ -n "$ipv4" || -n "$ipv6" ]]; then for ip in $ipv4 $ipv6; do ptr=$(dig $resolver +short -x "$ip") printf " %s -> %s\n" "$ip" "${ptr:-n/a}" done else printf " n/a\n" fi # HTTP PERFORMANCE # tests routing responses and redirection chains section_header "HTTP ROUTING" http_status=$(timeout "$timeout_val" curl -A "$ua" -sL -o /dev/null -w "%{http_code}" "http://$domain" || echo "timeout") https_status=$(timeout "$timeout_val" curl -A "$ua" -sL -o /dev/null -w "%{http_code}" "https://$domain" 2>/dev/null || echo "timeout") redirects=$(timeout "$timeout_val" curl -A "$ua" -sI -L "http://$domain" 2>/dev/null | grep -i "^location:" | sed 's/[Ll]ocation: //g' | tr -d '\r') printf " http status: %s\n" "$http_status" printf " https status: %s\n" "$https_status" printf " redirects:\n%s\n" "$([[ -n "$redirects" ]] && echo "$redirects" | sed 's/^/ -> /' || echo " none")" # ENCRYPTION AUDIT # pulls subject, issuer, and expiration from the tls certificate section_header "TLS CERTIFICATE" tls=$(timeout "$timeout_val" openssl s_client -connect "$domain:443" -servername "$domain" -showcerts /dev/null) expiry=$(echo "$tls" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) subject_issuer=$(echo "$tls" | grep -E "subject=|issuer=" | sed 's/^[ \t]*//') printf " subject/issuer:\n%s\n" "$([[ -n "$subject_issuer" ]] && echo "$subject_issuer" | sed 's/^/ /' || echo " unknown")" printf " expiry date: %s\n" "${expiry:-unknown}" # SECURITY HEADERS # evaluates presence of critical web security directives section_header "SECURITY POSTURE" headers=$(timeout "$timeout_val" curl -A "$ua" -sI "https://$domain" 2>/dev/null | tr -d '\r') for h in "strict-transport-security" "content-security-policy" "x-frame-options" "x-content-type-options"; do val=$(echo "$headers" | grep -i "^$h:" | cut -d: -f2- | xargs) if [[ -n "$val" ]]; then printf " %-25s ${green}present${nc}\n" "$h" else printf " %-25s ${yellow}missing${nc}\n" "$h" fi done # INFRASTRUCTURE PROBE # checks common critical ports on the primary resolved ip, results sorted after concurrent scan section_header "PORT SCAN" ports=(21 22 25 53 80 443 3306 8080 8443) target_ip=$(echo "$ipv4" | head -n1) if [[ -n "$target_ip" ]]; then scan_tmp=$(mktemp) exec 3>&2 exec 2>/dev/null for port in "${ports[@]}"; do (timeout 1 bash -c "/dev/null && echo "$port" >> "$scan_tmp") & done wait exec 2>&3 if [[ -s "$scan_tmp" ]]; then sort -n "$scan_tmp" | while read -r port; do printf " port %-5s ${green}open${nc}\n" "$port" done else printf " no open ports detected\n" fi rm -f "$scan_tmp" else printf " ${red}no ipv4 address for scanning.${nc}\n" fi # ASSET ENUMERATION # queries certificate transparency logs for child infrastructure via crt.sh json endpoint section_header "SUBDOMAINS" subdomains=$(timeout 15 curl -s "https://crt.sh/?q=%25.$domain&output=json" | jq -r '.[].name_value' 2>/dev/null | tr ',' '\n' | sed 's/\*\.//' | sort -u | head -n 15) [[ -n "$subdomains" ]] && echo "$subdomains" | sed 's/^/ /' || echo " enumeration failed or none found" # REGISTRY AUDIT # extracts baseline domain registration metadata section_header "WHOIS DATA" whois_data=$(timeout "$timeout_val" whois "$domain" 2>/dev/null | grep -Ei "^registrar:|^creation date:|^updated date:" | tr -s ' ' | head -n 5) [[ -n "$whois_data" ]] && echo "$whois_data" | sed 's/^/ /' || echo " extraction limited" # RISK SCORE # calculates a basic hazard rating based on missing protections and open ports section_header "RISK SUMMARY" risk=0 [[ ! "$https_status" =~ ^(2|3) ]] && ((risk += 2)) echo "$headers" | grep -qi "^strict-transport-security:" || ((risk += 1)) echo "$headers" | grep -qi "^content-security-policy:" || ((risk += 1)) [[ -z "$dnskey" ]] && ((risk += 1)) timeout 1 bash -c "/dev/null && ((risk += 1)) timeout 1 bash -c "/dev/null && ((risk += 2)) if [ $risk -le 1 ]; then posture_color=$green posture_text="low risk" else posture_color=$red posture_text="risk identified" fi printf " domain: %s\n" "$domain" printf " ip: %s\n" "${target_ip:-n/a}" printf " posture: ${posture_color}%s${nc} (score: %d)\n\n" "$posture_text" "$risk"