Introduction
Before a threat actor runs a single exploit, they enumerate. They want to know every subdomain you own, every IP range you control, every service you're running on a port that wasn't in your documentation. The subdomain enumeration phase alone regularly turns up assets the target organization had no idea were still live.
In 2022, a bug bounty researcher found a fully functional internal Jenkins instance on a subdomain of a publicly traded company. The instance had no authentication. It had been running for roughly three years. Nobody had flagged it in any internal audit because nobody had enumerated subdomains from the outside. The researcher found it in under ten minutes using CT log data.
This post explains exactly how subdomain discovery works at a technical level: what data sources are used, how each method works mechanically, what an attacker does with the results, and how to run the same process against your own infrastructure before someone else does.
Why subdomains matter more than the apex domain
Most organizations harden their apex domain. The main website gets a WAF, regular penetration testing, security headers reviewed, and TLS configuration locked down. The subdomains do not get the same treatment.
Every subdomain is a separate attack surface. A subdomain running an old version of WordPress that was set up for a campaign two years ago has its own vulnerabilities, its own credential exposure risk, and its own potential CNAME takeover vector. It shares your brand and your cookies but not necessarily your security controls.
The more active a CI/CD pipeline, the more subdomains accumulate. Preview environments, branch deployments, staging tiers, regional instances, internal tooling exposed over HTTPS: all of these generate DNS records. Some get cleaned up when the work is done. A significant portion stay live indefinitely with no clear owner.
Method 1: certificate transparency log enumeration
Certificate transparency (CT) logs are the highest-yield subdomain discovery source available. Every TLS certificate issued by a publicly trusted CA must be logged to at least two CT logs before browsers will trust it. This is a public, append-only record of every certificate ever issued for your domain.
The certificate's Subject Alternative Names (SANs) field lists every domain the certificate covers. A single certificate issued for a load balancer might cover api.example.com, api-v2.example.com, api-staging.example.com, and api-internal.example.com all at once. That single CT log entry reveals four subdomains.
The key tool for querying this data is crt.sh, which aggregates CT logs from Google, Cloudflare, DigiCert, and others into a searchable interface.
-
Basic CT log query — Returns all certificates ever issued for subdomains of example.com, including expired and revoked certificates. Expired certificates are useful because the subdomain may still be live even if the certificate was replaced.
curl -s 'https://crt.sh/?q=%.example.com&output=json' | jq -r '.[].name_value' | sort -u -
Wildcard handling — Wildcard certificates like *.staging.example.com confirm the staging subdomain tier exists, but not the individual names under it. You need DNS brute-force to enumerate what lives under a wildcard.
curl -s 'https://crt.sh/?q=%.example.com&output=json' | jq -r '.[].name_value' | grep '\*' | sort -u -
Filtering to live subdomains only — CT logs include years of historical data. Pipe the results through a DNS resolver to identify which subdomains currently have active DNS records.
curl -s 'https://crt.sh/?q=%.example.com&output=json' | jq -r '.[].name_value' | sort -u | dnsx -silent
Method 2: DNS brute-force enumeration
CT logs only surface subdomains that were issued TLS certificates. A subdomain running only HTTP, or accessible only by IP, or using a self-signed certificate, won't appear in CT logs at all. DNS brute-force fills this gap by iterating a wordlist of common subdomain names against the target zone and checking which ones resolve.
The wordlist quality matters more than the speed of the tool. A wordlist of 100 names will miss most real subdomains. Production wordlists used by security researchers contain millions of entries, derived from real-world DNS data across millions of domains.
-
Basic brute-force with puredns — puredns is the current standard for high-speed DNS brute-force among subdomain enumeration tools. It handles wildcard filtering automatically, which prevents false positives on domains that return a response for every query regardless of the subdomain name.
puredns bruteforce wordlist.txt example.com --resolvers resolvers.txt -
Wildcard detection and how filtering actually works — Some domains configure a wildcard DNS record (*.example.com) that resolves every subdomain query to the same IP. Without filtering, brute-force returns thousands of false positives. puredns detects wildcards by first querying a randomly generated subdomain name (e.g. xk2m9q4z.example.com). If that resolves, a wildcard is in place. puredns records the wildcard response signature and filters any brute-force result that matches it, leaving only genuine resolutions in the output.
dig xk2m9q4z.example.com A +short ; If this returns an IP, a wildcard DNS record is configured ; puredns handles this filtering automatically during bruteforce runs -
Comprehensive enumeration with amass — amass is widely used by security teams and red teamers for subdomain enumeration. It combines passive source queries, DNS brute-force, and certificate scraping into a single workflow, with support for output to graph databases for tracking changes over time.
amass enum -d example.com -o amass_results.txt amass enum -passive -d example.com -o amass_passive.txt -
Permutation scanning — Once you have a base list of known subdomains, generate permutations. If api.example.com exists, check api-v2, api-staging, api-prod, api-dev, api-internal. Tools like gotator automate this.
gotator -sub known_subdomains.txt -perm permutations.txt | puredns resolve --resolvers resolvers.txt -
Recommended wordlists — SecLists (danielmiessler/SecLists on GitHub) contains the dns/subdomains-top1million-110000.txt file used as a baseline. For deeper coverage, the assetnote.io wordlists derived from real internet DNS data cover significantly more ground and are updated regularly.
Method 3: passive DNS and OSINT sources
Passive DNS databases record historical DNS resolutions. When a resolver anywhere in the world resolves api-old.example.com to an IP address, that resolution gets logged. Aggregators like Farsight DNSDB, SecurityTrails, and VirusTotal maintain these logs going back years.
This source catches subdomains that no longer have active DNS records but may still have live backends. A subdomain whose DNS record was deleted last month may still be routing traffic if a load balancer or reverse proxy hasn't been updated. It also catches subdomains that were only briefly active during an incident or a deploy test and never made it into any CT log.
OSINT sources beyond passive DNS include search engine indexed pages (Google dorking for site:example.com), web archive crawl data from the Wayback Machine, GitHub repositories where developers hardcoded internal subdomain names in config files, and Shodan or Censys queries against your known IP ranges.
-
SecurityTrails API query — Returns all subdomains in SecurityTrails' passive DNS database for the target domain. Requires a free API key.
curl -s 'https://api.securitytrails.com/v1/domain/example.com/subdomains' -H 'APIKEY: YOUR_KEY' | jq -r '.subdomains[]' -
VirusTotal passive DNS — VirusTotal's API returns historical DNS data from multiple passive DNS sources. Useful for cross-referencing results.
curl -s 'https://www.virustotal.com/vtapi/v2/domain/report?apikey=KEY&domain=example.com' | jq -r '.subdomains[]' -
GitHub dorking for subdomains — Developers frequently hardcode internal subdomain names in configuration files, CI/CD scripts, and README files. Search GitHub for your domain name to find these.
site:github.com "example.com" "staging" OR "internal" OR "dev" -
Wayback Machine enumeration — The CDX API returns all URLs the Wayback Machine has ever crawled under a domain, which surfaces subdomains from archived pages.
curl -s 'http://web.archive.org/cdx/search/cdx?url=*.example.com&output=json&fl=original&collapse=urlkey' | jq -r '.[][0]' | grep -oP '[a-z0-9-]+\.example\.com' | sort -u
How attackers combine all three methods
Professional recon pipelines chain all three sources together and deduplicate the output. Running any single method in isolation leaves gaps: CT logs miss non-HTTPS assets, passive DNS misses recently created subdomains, and brute-force misses names outside the wordlist.
A typical workflow: pull CT log data from crt.sh first since it takes seconds and gives a strong baseline. Run passive DNS queries against SecurityTrails, VirusTotal, and similar sources in parallel to catch historical subdomains CT logs don't cover. Take the combined output, deduplicate, and run through a DNS resolver to confirm which are currently live. From the confirmed live set, run permutation scanning to generate naming variants based on observed patterns. Finally, run a brute-force sweep for anything the passive sources missed.
The full pipeline can be automated with Subfinder, which wraps over 40 passive sources into a single command, combined with puredns for active resolution and brute-force.
-
Full automated pipeline with Subfinder — Subfinder queries over 40 passive sources simultaneously including SecurityTrails, VirusTotal, Censys, Shodan, and more. Pipe to dnsx for live resolution.
subfinder -d example.com -silent | dnsx -silent -a -resp -
Combining passive and active results — Merge results from all sources, deduplicate, then resolve the full list to confirm what's live.
cat ct_results.txt passive_dns.txt brute_results.txt amass_results.txt | sort -u | dnsx -silent -a -resp > confirmed_live.txt -
Scanning confirmed subdomains for open ports and tech — Once subdomain enumeration is complete, scan each confirmed live subdomain for open ports and running services.
cat confirmed_live.txt | httpx -silent -tech-detect -status-code -title
What the vulnerable configuration looks like
The most common subdomain vulnerability found after enumeration is subdomain takeover via a dangling CNAME. When a subdomain's CNAME points to a third-party service that has since been deprovisioned, the DNS record stays but the target no longer exists.
-
Vulnerable: dangling CNAME to deprovisioned Heroku app — The CNAME record still exists in DNS. The target Heroku application has been deleted. Anyone can register a new Heroku app with the same name and serve content from your subdomain.
; Vulnerable DNS zone old-app.example.com. IN CNAME example-app-12345.herokuapp.com. ; What dig returns $ dig old-app.example.com CNAME ;; ANSWER SECTION: old-app.example.com. 300 IN CNAME example-app-12345.herokuapp.com. ; What curl returns (Heroku's unclaimed app page) $ curl -I https://old-app.example.com HTTP/2 404 X-Heroku-Error: No such app -
Secure: CNAME removed or pointed to live resource — When deprovisioning a service, remove the DNS record at the same time. If the subdomain still needs to exist, point it to a resource you control.
; Secure: record removed entirely ; old-app.example.com no longer exists in DNS ; Or: record updated to point to new resource old-app.example.com. IN CNAME new-service.example.com.
How to detect subdomain takeover candidates in your environment
Manual detection starts with enumerating all CNAME records in your DNS zone and checking whether each target is still claimed. Tools like nuclei have dedicated templates for this, covering over 50 services including Heroku, GitHub Pages, Fastly, Azure, AWS S3, Shopify, and others.
-
Check all CNAME records in your zone — List every CNAME record and manually verify each target is still a live, claimed resource.
dig axfr example.com @ns1.example.com | grep CNAME -
Automated takeover detection with nuclei — nuclei's subdomain-takeover template set checks for the specific error responses and fingerprints that indicate an unclaimed resource on each major platform.
cat confirmed_live.txt | nuclei -t takeovers/ -silent -
Check for Heroku-specific takeover — The specific fingerprint for an unclaimed Heroku app in the response body.
curl -s https://old-app.example.com | grep -i 'no such app\|herokucdn'
Remediation: what to fix and when to stop enumerating
Subdomain takeover remediation is straightforward once you've found the asset. The fix is one of three actions depending on whether the subdomain still has a purpose.
A common question that comes up during enumeration: when do you stop? For a defensive assessment of your own infrastructure, the practical answer is when your three sources (CT logs, passive DNS, and brute-force) are producing no new results that survive DNS resolution. Diminishing returns set in quickly beyond that point. For ongoing monitoring, the more useful approach is running continuous enumeration on a schedule rather than trying to achieve completeness in a single pass, since new subdomains get created constantly.
-
Remove the dangling DNS record — If the service is deprovisioned and the subdomain is no longer needed, delete the DNS record entirely. This is the cleanest fix and eliminates the risk completely.
; Remove from your DNS provider ; Delete the record: old-app.example.com CNAME example-app-12345.herokuapp.com -
Reclaim the resource — If the subdomain still needs to be active, re-provision the target resource on the third-party platform so the CNAME resolves to something you control. Update or redirect as needed.
; Reclaim the Heroku app by registering the same app name ; Then redirect or serve a placeholder -
Add DNS CAA records to limit certificate issuance — CAA records restrict which certificate authorities can issue certificates for your domain, limiting the damage if a takeover does occur. This does not prevent takeover but limits one exploitation path.
example.com. IN CAA 0 issue "letsencrypt.org" example.com. IN CAA 0 issuewild ";"
Real-world incidents
Subdomain takeover via dangling CNAMEs has been documented across bug bounty programs at Uber, Amazon, Microsoft, and dozens of other major companies. Detectify published early research documenting the attack class in 2014, and it gained wide industry attention through 2015 and 2016 as researchers began systematically cataloguing vulnerable third-party services. The vulnerability remains active today because DNS cleanup is operationally difficult at scale.
Multiple subdomain takeovers on microsoft.com subdomains have been reported through Microsoft's bug bounty program, with several involving Azure-hosted endpoints where the CNAME pointed to a deprovisioned Azure service. In each case, a researcher was able to register a new resource at the unclaimed target and serve arbitrary content from the microsoft.com subdomain, enabling potential phishing, cookie theft, and session hijacking under the microsoft.com domain.
Across all documented cases, the compromised subdomain shares the same profile: a service deployed and then forgotten, with the DNS record outliving the service itself by months or years, invisible to security reviews because no one was running subdomain enumeration from the outside.
Key takeaways
- CT log enumeration is the fastest and highest-yield starting point for subdomain discovery. Run it first against any domain you're assessing, including your own.
- DNS brute-force with a quality wordlist catches subdomains that never received a TLS certificate. Pairing it with wildcard detection prevents false positives from flooding your results.
- Passive DNS sources surface historical subdomains that are no longer in active DNS but may still be routing traffic. These are the assets most likely to have been forgotten by the team responsible for them.
- Subdomain takeover via dangling CNAME is consistently exploitable once found. Finding it takes enumeration skill; fixing it takes a DNS record deletion or a 10-minute resource reclaim.
- A full enumeration pipeline chains CT logs, passive DNS, amass, and brute-force, deduplicates, resolves live hosts, then scans for services. Running only one source leaves real gaps.
- Continuous subdomain monitoring catches new assets between manual audits. A new subdomain created today can be misconfigured, forgotten, and exploited before the next quarterly review.
Frequently asked questions
- Does subdomain enumeration require any special permissions or authorization?
- CT log queries and passive DNS lookups are entirely passive and use publicly available data. Active DNS brute-force sends queries to public resolvers. Neither method involves touching the target's infrastructure directly. However, you should only run active enumeration against domains you own or have explicit written permission to test. Unauthorized testing creates legal exposure regardless of the technique.
- How many subdomains should I expect to find on a typical company domain?
- It varies significantly by organization size and age of the domain. A small SaaS company might have 20 to 50 subdomains. A mid-size company with multiple products, regional deployments, and a few years of CI/CD history commonly has 200 to 500. Large enterprises with multiple acquisitions regularly have several thousand across all their domains. CT logs typically surface 60 to 80 percent of the real total.
- What's the difference between subdomain enumeration and subdomain takeover?
- Subdomain enumeration is the reconnaissance step: finding what subdomains exist. Subdomain takeover is a specific vulnerability where a discovered subdomain's CNAME points to a deprovisioned resource that can be claimed. Enumeration enables takeover detection, but not every subdomain found through enumeration is vulnerable to takeover.
- Can wildcard DNS records block subdomain brute-force?
- Wildcard DNS records return a valid response for any subdomain query, which makes brute-force results unreliable without wildcard detection. Modern tools like puredns detect the wildcard by querying a randomly generated subdomain name, capturing that response signature, then filtering any brute-force result that matches it. Only genuine resolutions that differ from the wildcard signature make it into the final output. Wildcard records do not affect CT log enumeration or passive DNS discovery.
See your subdomain inventory before attackers do
SurfaceGuard runs continuous subdomain enumeration using CT log data, passive DNS sources, and brute-force across every domain you monitor. New subdomains that appear since your last scan trigger an alert immediately, and every discovered subdomain is scanned for takeover candidates, open services, and misconfigurations automatically.