Subdomain Enumeration¶
Subdomain enumeration means finding all valid subdomains for a target domain. It's one of the most important steps in recon. Every subdomain you find is a potential new attack surface - often revealing forgotten apps, dev environments, or APIs that are way less secure than the main site.
1. Introduction to Subdomain Enumeration¶
A company might have www.example.com as their main site, but behind the scenes, they could have hundreds of other subdomains, such as:
api.example.com(API servers)dev.example.com(Development environments)vpn.example.com(Remote access portals)jira.example.com(Internal ticketing systems)assets.example.com(Cloud storage)
The goal of subdomain enumeration is to create a comprehensive list of all these assets to map out the full scope of the target's web presence.
2. Core Concepts: Passive vs. Active Enumeration¶
You've got two main ways to find subdomains. Combine both for the best results.
Passive Enumeration¶
Passive techniques use third-party data sources that already crawled the internet. You don't send any packets to the target, so it's completely stealthy - they'll never know you're looking.
Passive Sources: - Search Engines: Google, Bing, etc., index subdomains as they crawl the web. - Public DNS Datasets: Services like VirusTotal, SecurityTrails, and DNSDB collect and store historical DNS records. - Certificate Transparency (CT) Logs: Every SSL/TLS certificate issued is logged in a public database. These logs contain domain and subdomain names. - Web Archives: Services like the Wayback Machine store URLs that contain subdomains.
Active Enumeration¶
Active techniques mean you're hitting the target's DNS servers directly. You'll sometimes find subdomains that passive sources missed, but it's noisy - the target might notice you're probing around.
Active Techniques: - Brute-Forcing: Using a wordlist of common subdomain names (e.g., www, api, dev) and making a DNS query for each one. - DNS Zone Transfer (AXFR): A request to a DNS server for its entire zone file (a list of all its DNS records). This is rarely allowed but provides a complete list if successful.
3. Passive Enumeration Tools¶
Start with passive enumeration - it's fast and stealthy. Perfect for getting a baseline before you make any noise.
subfinder¶
subfinder is fast and powerful. It queries dozens of passive sources at the same time. This is usually your first tool.
Installation:
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
Usage:
# Basic passive scan
subfinder -d example.com
# Save output to a file
subfinder -d example.com -o subdomains.txt
# Use all available sources (slower but more comprehensive)
subfinder -d example.com -all
# Exclude sources if they are rate-limiting you
subfinder -d example.com -exclude-sources archiveis,zoomeye
amass¶
OWASP's amass is the most comprehensive attack surface mapping tool out there. It combines passive sources, active techniques, and web scraping to build a complete map of the target's infrastructure.
Installation:
go install -v github.com/owasp-amass/amass/v4/...@master
Usage (Passive Mode):
# Perform a passive enumeration
amass enum -passive -d example.com
# Use a configuration file with API keys for better results
amass enum -passive -d example.com -config config.ini
4. Active Enumeration Tools¶
After passive scanning, use active techniques to catch anything you missed.
ffuf (for Brute-Forcing)¶
While known for web fuzzing, ffuf is also an excellent DNS brute-forcer.
# Brute-force subdomains using a wordlist
# -w: Wordlist of subdomain names
# -H: Host header with FUZZ as the placeholder for the subdomain
# -u: A dummy URL (required by ffuf)
# -fs: Filter out responses of a certain size (to handle wildcards)
ffuf -w /path/to/subdomains.txt -H "Host: FUZZ.example.com" -u http://example.com -fs 0
dnsrecon¶
A versatile DNS enumeration script that can perform brute-forcing, zone transfers, and more.
# Attempt a zone transfer
dnsrecon -d example.com -t axfr
# Brute-force using a built-in wordlist
dnsrecon -d example.com -t brt
# Use a custom wordlist
dnsrecon -d example.com -D /path/to/subdomains.txt -t brt
5. The Complete Workflow¶
A robust workflow combines multiple tools and techniques.
- Passive First: Run
subfinderandamass -passiveto get a large baseline list of subdomains.subfinder -d example.com -o subfinder.txt amass enum -passive -d example.com -o amass_passive.txt - Active Brute-Force: Use a high-quality wordlist to brute-force for more subdomains.
ffuf -w wordlist.txt -H "Host: FUZZ.example.com" -u http://example.com -o ffuf.json - Combine and Unify: Merge the results from all tools into a single, sorted, unique list.
cat subfinder.txt amass_passive.txt | sort -u > all_subdomains.txt - Resolve and Validate: Use a tool like
dnsxorhttpxto check which of the discovered subdomains are actually live and have web servers.cat all_subdomains.txt | dnsx -silent -resp -a -cname
6. Advanced Techniques and Evasion Methods¶
Stealthy Subdomain Enumeration¶
Rate-Limited Passive Enumeration:
# Slow, stealthy subfinder with rate limiting
subfinder -d example.com -rate-limit 100 -timeout 30 -o stealth_subs.txt
# Amass with controlled timing
amass enum -passive -d example.com -max-dns-queries 500 -timeout 30
# Custom Python script for timed enumeration
import subprocess
import time
import random
def stealth_enumeration(domain):
sources = ['subfinder', 'amass', 'assetfinder']
results = set()
for tool in sources:
try:
cmd = [tool, '-d', domain, '-silent']
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
results.update(result.stdout.splitlines())
time.sleep(random.uniform(10, 30)) # Random delay between tools
except:
continue
return sorted(results)
DNS over HTTPS/TLS for Anonymity:
# Using dnsx with DoH resolvers
cat domains.txt | dnsx -silent -a -resp -doh https://cloudflare-dns.com/dns-query
# MassDNS with custom DoH resolvers
massdns -r doh-resolvers.txt -t A -o S -w output.txt subdomains.txt
# Python DoH implementation for stealth scanning
import requests
import json
def doh_subdomain_scan(domain, wordlist):
doh_url = "https://cloudflare-dns.com/dns-query"
found_subs = []
for sub in wordlist:
target = f"{sub}.{domain}"
params = {"name": target, "type": "A"}
headers = {"accept": "application/dns-json"}
try:
response = requests.get(doh_url, params=params, headers=headers, timeout=5)
data = response.json()
if data.get('Answer'):
found_subs.append(target)
except:
pass
time.sleep(0.1) # Rate limiting
return found_subs
Advanced Permutation Techniques¶
AI-Powered Subdomain Generation:
# Using machine learning for pattern recognition
python3 -c "
import re
from transformers import pipeline
# Load pre-trained model
generator = pipeline('text-generation', model='gpt2')
def generate_subdomains(base_domain, existing_subs, num_samples=20):
prompt = f\"Subdomain patterns for {base_domain}: {', '.join(existing_subs[:5])}\"
generated = generator(prompt, max_length=100, num_return_sequences=num_samples)
new_subs = []
for result in generated:
text = result['generated_text']
# Extract potential subdomains using advanced pattern matching
patterns = [
r'\b([a-z0-9][a-z0-9-]{1,61}[a-z0-9])\.',
r'\b(dev|staging|test|prod|api|internal)-[a-z0-9-]+\.',
r'\b[a-z]+[0-9]{2,}\.[a-z]+\.'
]
for pattern in patterns:
matches = re.findall(pattern, text)
new_subs.extend([f'{match}.{base_domain}' for match in matches])
return list(set(new_subs))
# Example usage
existing = ['api.example.com', 'dev.example.com', 'staging.example.com']
new = generate_subdomains('example.com', existing)
print('Generated:', new)
"
# Context-aware permutation with gotator
gotator -sub existing_subs.txt -perm advanced_patterns.txt -depth 3 -numbers 1000 -md -advance -o permutations.txt
# Multi-level combinatorial generation
altdns -i discovered_subs.txt -o permutations.txt -w patterns.txt -r -s 3
Time-Based Evasion Patterns:
# Randomized timing for active scanning
python3 -c "
import dns.resolver
import time
import random
def randomized_dns_scan(domain, wordlist):
resolver = dns.resolver.Resolver()
resolver.nameservers = ['8.8.8.8', '1.1.1.1', '9.9.9.9']
found = []
for subdomain in wordlist:
target = f'{subdomain}.{domain}'
try:
answers = resolver.resolve(target, 'A')
found.append(target)
print(f'[+] {target} -> {answers[0]}')
except:
pass
# Randomized delay: 0.5-3 seconds with occasional longer pauses
delay = random.uniform(0.5, 3.0)
if random.random() < 0.1: # 10% chance of longer delay
delay += random.uniform(5, 15)
time.sleep(delay)
return found
"
Advanced Certificate Transparency Analysis¶
Real-time CT Monitoring with Evasion:
# Custom CT monitoring with rotating user agents
python3 -c "
import requests
import json
import time
from datetime import datetime, timedelta
def stealth_ct_monitoring(domain, hours=24):
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]
cutoff = datetime.now() - timedelta(hours=hours)
certificates = []
for i in range(0, 1000, 100): # Pagination with delays
url = f'https://crt.sh/?q={domain}&output=json&start={i}'
headers = {'User-Agent': random.choice(user_agents)}
try:
response = requests.get(url, headers=headers, timeout=10)
data = response.json()
for cert in data:
not_before = datetime.strptime(cert['not_before'], '%Y-%m-%dT%H:%M:%S')
if not_before > cutoff:
certificates.extend(cert['name_value'].split('\\n'))
time.sleep(random.uniform(2, 5)) # Avoid rate limiting
except:
time.sleep(10) # Longer delay on error
return list(set(certificates))
subdomains = stealth_ct_monitoring('example.com', 48)
print(f'Found {len(subdomains)} subdomains from CT logs')
"
# Certificate fingerprint correlation
openssl x509 -in cert.pem -noout -fingerprint -sha256 | awk -F= '{print $2}' | tr -d ':'
Advanced API Enumeration with OPSEC¶
Stealthy API Integration:
# Rotating API endpoints and keys
python3 -c "
import requests
import time
import random
def multi_api_subdomain_scan(domain):
apis = [
{
'url': 'https://api.securitytrails.com/v1/domain/{}/subdomains',
'key': 'securitytrails_key',
'header': 'APIKEY'
},
{
'url': 'https://api.shodan.io/dns/domain/{}?key={}',
'key': 'shodan_key',
'param': True
}
]
all_subs = set()
for api in apis:
try:
if api.get('param'):
url = api['url'].format(domain, api['key'])
response = requests.get(url, timeout=15)
else:
url = api['url'].format(domain)
headers = {api['header']: api['key']}
response = requests.get(url, headers=headers, timeout=15)
if response.status_code == 200:
data = response.json()
# Extract subdomains based on API response format
if 'subdomains' in data:
all_subs.update([f'{sub}.{domain}' for sub in data['subdomains']])
elif 'data' in data:
all_subs.update([item['value'] for item in data['data']])
time.sleep(random.uniform(5, 15)) # Avoid API rate limits
except:
time.sleep(30) # Longer delay on failure
return sorted(all_subs)
"
Machine Learning for Subdomain Prioritization¶
AI-Based Target Prioritization:
# Python script for ML-powered subdomain ranking
python3 -c "
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
import numpy as np
class SubdomainPrioritizer:
def __init__(self):
# Pre-trained patterns for high-value subdomains
self.high_value_patterns = [
'admin', 'api', 'dev', 'staging', 'test', 'prod', 'internal',
'secure', 'auth', 'login', 'dashboard', 'console', 'manager'
]
self.vectorizer = TfidfVectorizer(ngram_range=(1, 2), max_features=1000)
self.classifier = RandomForestClassifier(n_estimators=100)
def extract_features(self, subdomain):
features = {}
# Pattern-based features
features['length'] = len(subdomain)
features['has_digits'] = any(char.isdigit() for char in subdomain)
features['has_hyphens'] = '-' in subdomain
features['has_underscores'] = '_' in subdomain
# High-value keyword matches
for pattern in self.high_value_patterns:
features[f'contains_{pattern}'] = pattern in subdomain
# Structural features
parts = subdomain.split('.')
features['subdomain_depth'] = len(parts) - 1
features['is_third_level'] = len(parts) >= 3
return features
def prioritize(self, subdomains):
features = [self.extract_features(sub) for sub in subdomains]
X = np.array([list(f.values()) for f in features])
# Predict probabilities (simplified)
scores = np.random.rand(len(subdomains)) # Replace with actual model
prioritized = sorted(zip(subdomains, scores), key=lambda x: x[1], reverse=True)
return [sub for sub, score in prioritized if score > 0.7]
# Usage
prioritizer = SubdomainPrioritizer()
subdomains = ['admin.example.com', 'api.dev.example.com', 'test.staging.example.com', 'www.example.com']
high_priority = prioritizer.prioritize(subdomains)
print('High priority targets:', high_priority)
"
# Integration with httpx for validation
cat subdomains.txt | httpx -silent -status-code -title -tech-detect -json | jq -r '. \| select(.status_code == 200) \| .url' > live_targets.txt
Advanced Defensive Evasion Techniques¶
Traffic Obfuscation Methods:
# DNS query randomization
python3 -c "
import dns.resolver
import random
import time
def obfuscated_dns_queries(domain, wordlist):
resolvers = [
'8.8.8.8', '1.1.1.1', '9.9.9.9', '208.67.222.222',
'64.6.64.6', '8.26.56.26', '84.200.69.80', '8.8.4.4'
]
query_types = ['A', 'AAAA', 'CNAME', 'MX', 'TXT', 'NS']
found = []
for subdomain in wordlist:
target = f'{subdomain}.{domain}'
resolver = random.choice(resolvers)
qtype = random.choice(query_types)
try:
dns.resolver.Resolver().resolve(target, qtype)
found.append(target)
except:
pass
time.sleep(random.uniform(0.3, 1.5))
return found
"
# Geographic distribution of queries
#!/bin/bash
# Multi-region DNS resolution
REGIONS=("us-east" "eu-west" "ap-southeast" "sa-east" "af-south")
for region in "${REGIONS[@]}"; do
dig @${region}.resolver.example.com target.example.com +short
sleep $((RANDOM % 5 + 1))
done
real world Case Study: Financial Institution Evasion¶
Scenario: Red team engagement against a major bank with advanced DNS monitoring and threat detection.
Evasion Techniques Applied: 1. Time-Based Obfuscation: Randomized delays between queries (0.3-5 seconds) 2. Source Diversification: Used 15+ different DNS resolvers including DoH endpoints 3. Protocol Mixing: Combined traditional DNS with DNS-over-HTTPS queries 4. Query Randomization: Varied query types (A, AAAA, CNAME, MX) to avoid pattern detection 5. Geographic Distribution: Queries originated from multiple cloud regions
Advanced Discoveries: - Hidden API Infrastructure: Discovered secure-banking-api.finance.com through CT log differential analysis - Legacy Systems: Found old-core-banking.internal.finance.com via historical DNS analysis - Development Environments: Located dev-ci-cd.finance.com through AI-generated permutations - Third-Party Integrations: Identified multiple vulnerable third-party services via SPF record analysis
Impact: Despite sophisticated defensive measures, the team discovered 28 previously unknown subdomains, leading to multiple critical findings including remote code execution and unauthorized data access.
7. Defensive Countermeasures¶
Advanced DNS Monitoring:
# Suricata rules for subdomain enumeration detection
alert dns any any -> any any (msg:"Subdomain Enumeration Pattern"; \
dns.query; pcre:"/^[a-z0-9-]{1,20}\.(dev|test|staging|api|admin)\./"; \
threshold: type threshold, track by_src, count 10, seconds 60; \
sid:1000101; rev:1;)
alert dns any any -> any any (msg:"Rapid DNS Query Pattern"; \
dns.query; threshold: type threshold, track by_src, count 50, seconds 30; \
sid:1000102; rev:1;)
# Zeek script for behavioral analysis
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
{
local query_pattern = /^[a-z0-9-]{1,20}\.[a-z0-9-]{1,20}\.[a-z]+\.[a-z]+$/;
if (query_pattern in query) {
NOTICE([$note=DNS::DeepSubdomainEnum,
$msg=fmt("Deep subdomain enumeration: %s", query),
$conn=c]);
}
}
Proactive Defense Strategies: - Implement DNS query rate limiting with adaptive thresholds - Deploy DNS firewall with threat intelligence integration - Use machine learning for anomalous DNS pattern detection - Implement DNSSEC to prevent cache poisoning and spoofing - Regular security audits of DNS configurations and records - Threat hunting based on DNS query analytics
Zero Trust DNS Architecture: - Micro-segmentation of DNS infrastructure - Mutual TLS authentication for all DNS transactions - Continuous verification of DNS resolver integrity - Encrypted DNS protocols (DoH, DoT) enforcement - Behavioral biometrics for DNS query analysis
8. Quick Reference Table¶
| Tool/Technique | Primary Use | Example Command |
|---|---|---|
subfinder | Stealthy passive enumeration | subfinder -d example.com -rate-limit 50 |
amass | Comprehensive surface mapping | amass enum -passive -d example.com -timeout 30 |
dnsx | Fast DNS resolution with DoH | cat subs.txt \| dnsx -doh -a -resp |
shuffledns | MassDNS wrapper with permutation | shuffledns -d example.com -w wordlist.txt |
gotator | Advanced subdomain permutation | gotator -sub subs.txt -perm patterns.txt |
ctfr | Certificate Transparency enumeration | ctfr -d example.com -o ct_subs.txt |
dnsrecon | DNS reconnaissance toolkit | dnsrecon -d example.com -t brt --threads 2 |
Advanced Tool Combinations:
# Stealthy enumeration pipeline
subfinder -d example.com -silent -rate-limit 100 | \
dnsx -silent -a -resp -doh https://dns.google/dns-query | \
httpx -silent -status-code -title -tech-detect
# AI-powered subdomain generation
python3 generate_subs.py example.com | \
massdns -r resolvers.txt -t A -o S -w output.txt
# Real-time monitoring with evasion
certstream --monitor --url example.com | \
jq -r '.data.leaf_cert.all_domains[]' | \
grep "example.com" | sort -u
Performance Optimization: - Use connection pooling for DNS queries - Implement query caching to reduce external lookups - Geographic distribution of resolver queries - Adaptive rate limiting based on target response - Parallel processing with controlled concurrency
Legal and Ethical Considerations: - Ensure proper authorization for all enumeration activities - Respect rate limits and terms of service - Avoid causing service disruption - Handle discovered information responsibly - Follow responsible disclosure procedures