Back to Articles

Dome: The Python Reconnaissance Swiss Army Knife for Subdomain Enumeration

[ View on GitHub ]

Dome: The Python Reconnaissance Swiss Army Knife for Subdomain Enumeration

Hook

While most subdomain enumeration tools force you to choose between staying passive or going active, Dome lets you walk both paths simultaneously—aggregating data from 21 OSINT sources while bruteforcing DNS in parallel, all in a single Python script under 2000 lines.

Context

Subdomain enumeration is the cornerstone of modern reconnaissance, whether you're hunting bug bounties, conducting penetration tests, or mapping attack surfaces for red team engagements. Before tools like Dome emerged, security researchers faced a fragmented workflow: run one tool to scrape certificate transparency logs, another to query DNS databases, a third to bruteforce subdomains, and yet another to scan for open ports. This meant managing multiple dependencies, wrestling with inconsistent output formats, and stitching together results manually.

The problem intensified as organizations adopted microservices architectures and cloud platforms, spawning sprawling subdomain hierarchies. A single company might expose hundreds of subdomains across development, staging, and production environments—each representing a potential entry point. Traditional tools either focused on passive collection (scraping public data sources without touching the target) or active enumeration (DNS queries and bruteforcing), but rarely both. Dome was built to collapse this workflow into a unified interface that security professionals could deploy in minutes, not hours.

Technical Insight

Dome's architecture revolves around a dual-mode enumeration engine that orchestrates passive OSINT collection and active DNS probing through a centralized controller. The passive reconnaissance module implements a provider-based pattern where each OSINT source—from AlienVault's OTX API to crt.sh certificate transparency logs—is abstracted as an independent collector. This design allows the tool to fail gracefully when individual sources timeout or rate-limit, continuing to aggregate from available providers.

Here's how Dome structures its passive enumeration workflow:

# Simplified representation of Dome's OSINT aggregation
class PassiveEnum:
    def __init__(self, domain, config):
        self.domain = domain
        self.sources = [
            AlienVault(config.get('alienvault_key')),
            CertSpotter(config.get('certspotter_key')),
            VirusTotal(config.get('virustotal_key')),
            CrtSh(),  # No API key needed
            ThreatCrowd(),
            # ... 16+ more sources
        ]
        self.results = set()
    
    def enumerate(self):
        for source in self.sources:
            try:
                subdomains = source.query(self.domain)
                self.results.update(subdomains)
            except APIKeyExpired:
                print(f"[!] {source.name} API key invalid")
            except Timeout:
                print(f"[!] {source.name} timed out")
        return self.results

This provider abstraction means adding new OSINT sources requires implementing a single query() method—no modifications to core logic. The tool automatically deduplicates results across all sources, which is critical when multiple services index the same certificate transparency logs or DNS databases.

On the active enumeration side, Dome implements three distinct strategies: validation of passively discovered subdomains, wordlist-based bruteforcing, and pure alphabetic permutation (generating aa.example.com through zzz.example.com). The wordlist mode supports custom dictionaries or falls back to a built-in list, while the bruteforce mode is intentionally limited to three-character combinations to prevent runaway execution times. The DNS resolution engine uses Python's dnspython library with configurable resolvers:

# Active DNS resolution with customizable resolvers
import dns.resolver

def resolve_subdomains(subdomains, resolver_ip='8.8.8.8'):
    resolver = dns.resolver.Resolver()
    resolver.nameservers = [resolver_ip]
    resolver.timeout = 2
    resolver.lifetime = 2
    
    valid_subdomains = []
    for subdomain in subdomains:
        try:
            answers = resolver.resolve(subdomain, 'A')
            valid_subdomains.append({
                'subdomain': subdomain,
                'ips': [str(rdata) for rdata in answers]
            })
        except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
            pass  # Subdomain doesn't exist
    
    return valid_subdomains

One of Dome's most valuable features is wildcard detection, which prevents false positives when enumerating domains that resolve random subdomains to catch-all servers (common with cloud providers). The tool queries several non-existent random subdomains and checks if they all resolve to the same IP address. If so, it flags the domain as using wildcards and filters subsequent results accordingly.

The port scanning capability integrates Nmap-style service detection without requiring Nmap as a dependency. Dome maintains preset port profiles (Top 100, Top 1000, Top Web) and performs TCP connection tests using Python's socket library with configurable timeouts. This means you can enumerate subdomains and identify exposed services like SSH (22), HTTP (80/443), or database ports (3306, 5432) in a single execution:

# Integrated port scanning after subdomain discovery
import socket

def scan_ports(ip, ports=[80, 443, 22, 3306], timeout=1):
    open_ports = []
    for port in ports:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(timeout)
        result = sock.connect_ex((ip, port))
        if result == 0:
            open_ports.append(port)
        sock.close()
    return open_ports

The multi-threading implementation uses Python's concurrent.futures.ThreadPoolExecutor to parallelize both DNS resolution and port scanning, with configurable thread counts to balance speed against network politeness. Results export to multiple formats (TXT, JSON, HTML), with the JSON output particularly useful for feeding into subsequent automation pipelines or visualization tools.

Gotcha

Dome's three-character bruteforce limitation (aa-zzz) means you'll miss longer subdomain patterns like "prod-api-v2" or "customer-portal" that are common in enterprise environments. While the wordlist mode partially compensates, you're dependent on your dictionary's comprehensiveness—and the built-in list may not cover organization-specific naming conventions. If you're enumerating a large corporation with deep subdomain hierarchies, Dome won't recursively discover third-level subdomains (like api.staging.corp.example.com from staging.corp.example.com), forcing you to run multiple passes manually.

The OSINT effectiveness is constrained by API key management and rate limits. Many of Dome's 21 sources require free API registrations, and some providers (VirusTotal, SecurityTrails) impose strict hourly quotas. The tool doesn't implement intelligent rate limiting or key rotation, so you might exhaust quotas mid-scan. Additionally, the project hasn't seen significant updates since 2020, meaning newer OSINT sources like Chaos (ProjectDiscovery's subdomain dataset) aren't integrated. The port scanning, while convenient, lacks service version detection and advanced fingerprinting—you'll know port 80 is open but not whether it's running Apache 2.4.41 or Nginx 1.18. For production penetration tests requiring detailed service enumeration, you'll still need dedicated port scanners.

Verdict

Use Dome if you need a batteries-included reconnaissance tool for bug bounty programs or time-boxed penetration tests where consolidating passive OSINT, DNS bruteforcing, and basic port scanning into one command saves critical workflow overhead. It excels when you're enumerating moderately-sized targets (100-5000 subdomains) and value having all reconnaissance data in unified JSON output for downstream processing. The dual-mode operation makes it ideal for scenarios where you start passive to map the landscape, then selectively go active on interesting findings. Skip it if you're enumerating massive attack surfaces requiring recursive subdomain discovery, need cutting-edge OSINT source coverage (Subfinder integrates 50+ sources and receives active maintenance), or require enterprise-grade features like automatic API key rotation and distributed scanning. Also look elsewhere if performance is critical—Go-based alternatives like Subfinder and Amass significantly outpace Python implementations when processing tens of thousands of domains. Consider Dome as your reconnaissance Swiss Army knife for rapid assessments, but keep specialized tools in your arsenal for deep engagements.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/cybersecurity/v4d1-dome.svg)](https://starlog.is/api/badge-click/cybersecurity/v4d1-dome)