Subdomain enumeration is a critical reconnaissance phase in security assessments that involves systematically discovering all subdomains associated with a target domain. The primary value is attack surface expansion. While a target organization might have a well-secured main website at example.com, they mave have dozens or hundreds of subdomains that are less carefully maintained. Subdomains may also reveal an organization‘s structure and their technology stack. For example:
jenkins.example.com indicates they use Jenkins for CI/CDconfluence.example.com suggests Atlassian tools for documentationvpn.example.com shows VPN infrastructuretest-db.example.com might expose database instancesFrom an architectural perspective, subdomain enumeration reveals how an organization structures their digital infrastructure. It shows the gap between what they think they’re exposing (their main website) versus what they’re actually exposing (their entire digital footprint). This reconnaissance phase often uncovers the paths of least resistance that make the difference between a successful assessment and a surface-level scan. DNS enumeration can be dun with python and will be discussed in this blog.
The script we will write is decomposed into three primary functions: resolve_dnswhich handles DNS record queries,enumerate_subdomains which performs brute-force enumeration, andgenerate_wordlistwhich creates subdomain dictionaries. There is also a simple menu-driven CLI.
The two core functions resolve_dns and enumerate_subdomains will be described below:
The enumerate_subdomains function performs brute-force subdomain discovery by reading potential subdomain names from a wordlist file and testing each one against a target domain using DNS resolution. It constructs full subdomain names by prepending each wordlist entry to the target domain, then uses socket.gethostbyname() to check if the subdomain resolves to an IP address. The function returns a list of all successfully resolved subdomains while silently ignoring non-existent ones and printing found subdomains to the console.
The resolve_dns function performs comprehensive DNS record enumeration for a given domain by querying five different record types: A (IPv4 addresses), AAAA (IPv6 addresses), MX (mail exchangers), NS (name servers), and TXT (text records). It uses the dns.resolver library to systematically query each record type, gracefully handling exceptions by printing error messages when specific record types don't exist for the domain. The function returns a structured dictionary containing all discovered DNS records, making it useful for reconnaissance and domain analysis tasks.
The entire script can be found here:
The script’s limited extensibility stems from its hardcoded wordlist and single enumeration method, which restricts scalability compared to a more robust design supporting multiple enumeration techniques (brute-force, permutation, API sources), configurable wordlists, and various output formatters (JSON, CSV, XML). From a security testing perspective, while this represents a basic reconnaissance tool suitable for initial discovery phases, it lacks advanced features essential for comprehensive assessments, including concurrent processing for faster enumeration, rate limiting to avoid detection, certificate transparency log queries, search engine reconnaissance, and DNS zone transfer attempts. To address these limitations, the script would benefit from implementing a proper DNS resolver class with consistent error handling, adding concurrent processing using asyncio or threading, creating a configuration system for different enumeration strategies, implementing proper logging instead of print statements, and adding result persistence with multiple output formats. Overall, the script demonstrates good foundational thinking but requires more sophisticated architectural patterns for production use in security assessments.