dnsenum

dnsenum runs a full DNS pass against one domain in a single command: it pulls the A, NS, and MX records, tries a zone transfer against every name server, brute forces subdomains from a wordlist, and can sweep the netranges it finds. Reach for it in the recon phase when the first question is “what does this domain actually expose?” and you would rather not type six separate dig commands. Only enumerate domains you own or are explicitly authorized to test.

Mental model: dnsenum [options] DOMAIN. The bare domain is the target; every capability is one flag away.

New to DNS? Core concepts
  • A / AAAA record: the IPv4 / IPv6 address of a host; dnsenum lists these as “Host’s addresses”
  • NS record: the authoritative name servers for the zone; dnsenum tries an AXFR against each one
  • MX record: the mail exchangers for the domain, useful for in-scope mail-path testing
  • PTR record: the reverse mapping from IP back to a name, produced by the reverse/netrange sweep
  • Zone transfer (AXFR): asking a name server for the entire zone at once, dig AXFR DOMAIN @NS
  • Resolver: the DNS server you query (--dnsserver); different resolvers can answer differently in split-horizon setups
  • Subdomain brute force: guessing names from a wordlist (-f); it only finds names that are in the list
  • Wordlist: the candidate name list you feed to -f (SecLists is the usual source)
When to reach for dnsenum (and when not)

Reach for it to get records, name servers, mail exchangers, and a zone-transfer check on a single in-scope domain in one command, to confirm your own zone does not leak its contents via AXFR, or as an early recon pass that maps the DNS surface before deeper testing.

Reach for something else when you only need one record type (dig DOMAIN MX +short is faster and cleaner), when you need fast or large-scale subdomain brute forcing (gobuster dns mode, ffuf, amass), when passive or certificate-log discovery would do (amass, subfinder), or when you want structured JSON/CSV output and current maintenance (dnsrecon).

Install, update, verify#

sudo apt install -y dnsenum                    # Debian / Ubuntu / Kali
sudo apt install --only-upgrade -y dnsenum     # update

# No distro package? clone dnsenum2 and install its Perl modules
git clone https://github.com/SparrowOchon/dnsenum2.git && cd dnsenum2
sudo cpan Net::IP Net::DNS Net::Netmask String::Random \
  XML::Writer Net::Whois::IP HTML::Parser WWW::Mechanize

command -v dnsenum      # confirm it is on PATH
dnsenum --help          # version banner + option groups (the confirmation)

dnsenum is a Perl script, so Perl plus those modules are the only hard requirement; the apt package pulls them in for you. On the git checkout there is nothing on PATH, so run it as perl dnsenum.pl. There is no self-test: a clean --help listing is your “it works”.

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
--enumShortcut for --threads 5 -s 15 -w-oWrite XML output (overwrites each run)
--dnsserverResolver to query-sMax names scraped from search
-f / --fileSubdomain wordlist to brute-pSearch pages to scrape
--threadsParallel query workers-w / --whoiswhois netranges, then PTR-sweep them
--noreverseSkip the reverse/netrange sweep-rRecurse into found subdomains
-t / --timeoutQuery timeout in seconds--subfileWrite valid subdomains to a file
-uAppend valid subs into the -f file--privateKeep private IPs (DOMAIN_ips.txt)
-e / --excludeDrop PTR names matching a regexp-vVerbose progress and errors

Cheat sheet#

Every block starts simple and adds one flag at a time, so you can stop at the line that does the job.

# Build up a run: stop at the line you need
dnsenum DOMAIN                                 # 1. records + NS + MX + AXFR + reverse sweep
dnsenum --noreverse DOMAIN                     # 2. + drop the slow reverse/netrange sweep
dnsenum --noreverse -o scans/d.xml DOMAIN      # 3. + archive XML for your notes
dnsenum --dnsserver RESOLVER --noreverse -o scans/d.xml DOMAIN   # 4. + a chosen resolver

# Record + zone-transfer triage (no brute force)
dnsenum --noreverse DOMAIN                     # A/AAAA, NS, MX, one AXFR try per name server
dnsenum --enum DOMAIN                          # default profile: threads 5, scrape 15, whois
dnsenum --noreverse -s 0 -p 0 DOMAIN           # records only, no search-engine scraping

# Choose the resolver
dnsenum --dnsserver RESOLVER DOMAIN            # ask the box's own name server (split-horizon)
dnsenum --dnsserver 1.1.1.1 DOMAIN             # ask a known-good public resolver

# Subdomain brute force (only when in scope)
dnsenum -f WORDLIST DOMAIN                     # brute names from a wordlist
dnsenum -f WORDLIST --threads 10 DOMAIN        # + more parallel workers
dnsenum -f WORDLIST -r DOMAIN                  # + recurse into found subdomains
dnsenum -f WORDLIST --subfile hosts.txt DOMAIN # + write valid subs to their own file
dnsenum -f WORDLIST -u a -o out.xml DOMAIN     # + append valid names back into WORDLIST

# Zone transfer
dnsenum --noreverse DOMAIN | tee scans/axfr.txt   # dnsenum's AXFR attempt, saved
dig AXFR DOMAIN @RESOLVER                       # confirm it directly (the proof)

# Reverse / netrange (the slow, loud part)
dnsenum -w DOMAIN                               # whois netranges, then PTR-sweep them
dnsenum -w --private DOMAIN                     # also keep private IPs (DOMAIN_ips.txt)
dnsenum -e '^unused' -w DOMAIN                  # drop junk PTR names by regexp

# Save and tune
dnsenum -o "scans/dnsenum-$(date +%F-%H%M).xml" DOMAIN   # timestamped XML, no overwrite
dnsenum -t 5 DOMAIN                             # shorter timeout on a slow link
dnsenum -o scans/d.xml DOMAIN 2>&1 | tee notes/d.txt     # XML + readable transcript

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Enumerate a domain fast (records, NS, MX, AXFR)#

dnsenum --noreverse DOMAIN -o scans/quick.xml

Returns the host’s A/AAAA addresses, the NS records, the MX records, and one zone-transfer attempt per name server (almost always refused). --noreverse skips the slow PTR/netrange sweep, so this is the triage view you run first.

Run the sensible default profile#

dnsenum --enum DOMAIN -o scans/enum.xml

--enum expands to --threads 5 -s 15 -w: the same record sections plus a little search scraping and a whois netrange pass. Good all-round default, but note the -w means it will try the slower netrange work.

Query a specific resolver (split-horizon / internal DNS)#

dnsenum --dnsserver RESOLVER --noreverse DOMAIN -o scans/internal.xml

Sends every query to the resolver you name instead of the system default. Point it at the box’s own name server when an internal zone answers differently from the public internet.

Test for a zone transfer and confirm it#

dnsenum --noreverse DOMAIN | tee scans/axfr.txt
dig AXFR DOMAIN @RESOLVER

dnsenum tries an AXFR against each NS and prints the result. “Transfer failed” is the normal outcome; a full zone dump is a real finding, so always reproduce it with dig AXFR before you trust or report it.

Brute force subdomains from a wordlist#

dnsenum --dnsserver RESOLVER -f WORDLIST DOMAIN -o scans/brute.xml

Adds a “Brute forcing” section that lists only the wordlist names that actually resolve. It finds nothing that is not in WORDLIST, so list choice is everything. Only run this when brute forcing is in scope.

Speed up the brute force with more threads#

dnsenum -f WORDLIST --threads 10 DOMAIN -o scans/brute.xml

--threads sets how many queries run in parallel. More threads finish a big list faster but hammer the resolver; back off on a fragile lab name server that starts dropping answers.

Recurse into discovered subdomains#

dnsenum -r -f WORDLIST --threads 10 DOMAIN -o scans/recursive.xml

-r re-runs the brute force against each subdomain that has its own NS record, so you catch names one level deeper. It multiplies the query count, so keep the wordlist and scope tight.

Save discovered subdomains to their own file#

dnsenum -f WORDLIST --subfile scans/subs.txt DOMAIN -o scans/brute.xml

--subfile writes just the valid subdomains to a clean file while -o keeps the full XML. Handy when you want a plain host list to feed straight into the next tool.

Grow your wordlist from valid hits#

dnsenum -f WORDLIST -u a DOMAIN -o scans/brute.xml

-u a appends every validated name (from all sources) back into the -f wordlist, so the next run starts warmer. The letters a|g|r|z pick which sources feed the update (all, scraping, reverse, zone transfer).

Sweep netranges with whois and reverse lookups#

dnsenum -w DOMAIN -o scans/netrange.xml

-w runs whois on the found addresses to derive class-C netranges, then PTR-scans them for extra hostnames. It is the richest pass and also the slowest and loudest; use it only when you actually want reverse data.

Skip the slow reverse sweep#

dnsenum --noreverse DOMAIN -o scans/records.xml

--noreverse turns off the reverse/netrange work entirely. Reach for it whenever you only need records, NS, MX, and the AXFR check and do not want to wait on PTR scanning.

Save XML output for notes and tooling#

dnsenum -o "scans/dnsenum-$(date +%F-%H%M).xml" DOMAIN 2>&1 | tee notes/dnsenum.txt

-o writes MagicTree-importable XML but overwrites the file each run, so timestamp the name. Piping through tee keeps a readable transcript alongside the structured file.

Pull a clean host list from the output#

grep -Eo '[a-z0-9.-]+\.DOMAIN' scans/brute.xml | sort -u | tee notes/hosts.txt

Extracts every hostname under the target domain from the XML and dedupes it, so the useful names are not buried in the transcript. This list feeds nmap, gobuster, or ffuf next.

Cut noise on a fragile lab resolver#

dnsenum --dnsserver RESOLVER --noreverse -t 5 --threads 3 DOMAIN

Lower --threads and a short -t timeout keep a small or overloaded resolver from dropping queries and handing you false negatives. Slower, but the answers you get are trustworthy.

Reading the output#

Output sectionWhat it isDo next
Host’s addressesA/AAAA of the bare domainFirst targets; feed the IPs to nmap
Name Servers (NS)The authoritative servers, each gets an AXFR tryRead the AXFR line under each
Mail (MX) ServersThe mail exchangersMail-path checks if in scope
Trying Zone TransferThe AXFR attempt result“failed” is normal; a full dump is a finding, confirm with dig
Brute forcingWordlist names that resolvedAdd to targets; no hits is not “no subdomains”
network ranges / reversewhois netranges plus PTR namesExtra hosts, but this part is slow and loud

No brute-force hits means only that none of your wordlist names resolved, not that the domain has no subdomains. Different resolvers returning different answers is normal in split-horizon DNS, so note which resolver produced which result.

Troubleshooting#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall, or run perl dnsenum.pl from the git copy
Can't locate Net/DNS.pmMissing Perl modulesudo cpan Net::DNS (and any other named module)
Run hangs foreverResolver unreachable or filtering youSet --dnsserver RESOLVER; lower -t timeout
No records returnedDomain does not resolveVerify the name, try dig DOMAIN @RESOLVER
Zone transfer always failsServer refuses AXFR (normal)Expected; only a full dump is a finding
Brute force finds nothingWordlist too small or wrongUse a larger in-scope WORDLIST
Reverse sweep takes foreverwhois netrange PTR scan is slowAdd --noreverse
Flags differ from a tutorialdnsenum vs dnsenum2 versionsCheck dnsenum --help; Kali ships dnsenum2

Gotchas#

  • Search-engine scraping is mostly dead. -s/-p rarely returns names anymore, so lean on -f with a real wordlist and treat any scraped name as a bonus.
  • --enum quietly turns on whois (-w). It is not just “fast defaults”: it can kick off a slow netrange and reverse sweep, so pair it with --noreverse when you only want records.
  • A dnsenum AXFR “success” can lie. Reproduce every zone dump with dig AXFR DOMAIN @RESOLVER before you trust or report it.
  • Threads hit the resolver, not the domain. High --threads hammers whatever --dnsserver you set; a fragile lab resolver silently drops queries and you read the loss as “no subdomains”.
  • -o overwrites. The XML path is truncated on every run, so timestamp the filename or you lose the last scan.
  • Brute forcing is loud. A wordlist run is a burst of NXDOMAIN queries and the AXFR tries are logged as AXFR requests, both textbook enumeration signatures a defender alerts on.

Pairs with#

dig and host confirm any single finding, and dig AXFR DOMAIN @RESOLVER is the proof a zone transfer really works. Feed the A-record IPs straight into nmap for port scanning. For large or fast subdomain lists, gobuster (dns mode) and ffuf brute force far quicker than dnsenum’s Perl loop, while amass and subfinder add passive and certificate-log discovery dnsenum never sees. Reach for dnsrecon when you want the same scripted pass with JSON/CSV output and more active maintenance.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop