#!/usr/bin/env bash # enumerate_subdomains # -------------------- # Enumera subdominios de un dominio objetivo usando un diccionario integrado de # ~100 subdominios comunes. Detecta tanto registros A (IP directa) como CNAME. # Opcionalmente guarda el resultado en un archivo. # # USO (directo): # enumerate_subdomains [archivo_salida] # enumerate_subdomains example.com # enumerate_subdomains example.com /tmp/resultado.txt # # Depende de: dig (dnsutils) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../shell/bash_colors.sh" source "$SCRIPT_DIR/../shell/bash_log.sh" bash_colors bash_log_init # ─── Diccionario integrado ───────────────────────────────────────────────────── _SUBDOMAIN_WORDLIST=( www mail ftp api dev admin vpn ssh git gitlab github jenkins ci cd staging prod test demo beta alpha app web portal intranet extranet remote desktop files cdn static assets media img images upload db database mysql postgres redis mongo smtp pop imap webmail mx ns1 ns2 dns autodiscover autoconfig crm erp shop store payment backup old legacy v1 v2 v3 internal corp office support helpdesk wiki docs doc status monitor grafana kibana elastic search registry docker k8s kubernetes auth sso login oauth api2 mobile ) # ─── Funciones puras ────────────────────────────────────────────────────────── _sub_is_valid_domain() { [[ -n "$1" && "$1" =~ ^[a-zA-Z0-9._-]+\.[a-zA-Z]{2,}$ ]] } _sub_build_fqdn() { local sub="$1" local domain="$2" echo "${sub}.${domain}" } # ─── Funciones de efecto ────────────────────────────────────────────────────── _sub_resolve_a() { local fqdn="$1" dig +short A "$fqdn" 2>/dev/null | grep -E '^[0-9]+\.' | head -1 || true } _sub_resolve_cname() { local fqdn="$1" dig +short CNAME "$fqdn" 2>/dev/null | head -1 || true } # ─── Punto de entrada ───────────────────────────────────────────────────────── enumerate_subdomains() { local domain="$1" local output_file="${2:-}" if [[ -z "$domain" ]]; then error "enumerate_subdomains: se requiere un dominio como argumento" >&2 return 1 fi if ! _sub_is_valid_domain "$domain"; then error "enumerate_subdomains: dominio no válido: '$domain'" >&2 return 1 fi if ! command -v dig &>/dev/null; then error "enumerate_subdomains: 'dig' no está instalado (sudo apt install dnsutils)" >&2 return 1 fi local total="${#_SUBDOMAIN_WORDLIST[@]}" local found=0 local checked=0 info "Probando ${total} subdominios en ${domain}..." echo "" if [[ -n "$output_file" ]]; then echo "# Subdominios encontrados en ${domain} -- $(date)" > "$output_file" fi for sub in "${_SUBDOMAIN_WORDLIST[@]}"; do local fqdn fqdn="$(_sub_build_fqdn "$sub" "$domain")" checked=$((checked + 1)) local ip ip="$(_sub_resolve_a "$fqdn")" if [[ -n "$ip" ]]; then echo -e " ${GREEN}[ok]${NC} ${fqdn} -> ${CYAN}${ip}${NC}" [[ -n "$output_file" ]] && echo "${fqdn} -> ${ip}" >> "$output_file" found=$((found + 1)) else local cname cname="$(_sub_resolve_cname "$fqdn")" if [[ -n "$cname" ]]; then echo -e " ${YELLOW}[cn]${NC} ${fqdn} -> CNAME: ${cname}" [[ -n "$output_file" ]] && echo "${fqdn} -> CNAME: ${cname}" >> "$output_file" found=$((found + 1)) fi fi # Progreso cada 20 subdominios if (( checked % 20 == 0 )); then echo -e " ${DIM_GRAY}[${checked}/${total} probados, ${found} encontrados]${NC}" fi done echo "" echo -e "${PURPLE}════════════════════════════════════════════════════════════${NC}" if [[ $found -eq 0 ]]; then info "No se encontraron subdominios en el diccionario" else success "Total encontrados: ${found} de ${total} probados" [[ -n "$output_file" ]] && info "Resultado guardado en: ${output_file}" fi echo -e "${PURPLE}════════════════════════════════════════════════════════════${NC}" } # Ejecutar si se llama directamente if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then enumerate_subdomains "$@" fi