Back to Articles

DNS Zone Transfers at Scale: How AXFR Scans a Million Domains in Minutes

[ View on GitHub ]

DNS Zone Transfers at Scale: How AXFR Scans a Million Domains in Minutes

Hook

In 2023, security researchers discovered over 5,000 actively vulnerable DNS servers leaking complete zone files through AXFR transfers—including government and Fortune 500 domains. The attack vector is 30 years old, yet remains persistently exploitable.

Context

DNS zone transfers (AXFR) were designed in the 1980s as a mechanism for primary DNS servers to replicate their entire zone database to secondary servers. The protocol assumes trust: when a secondary server requests a zone transfer, the primary sends every DNS record it knows about—A records, MX records, CNAMEs, internal hostnames, everything. This was fine when the internet was a small academic network where everyone knew each other.

The problem? Many administrators still configure authoritative DNS servers to allow AXFR requests from any IP address, not just their designated secondaries. For attackers, this is reconnaissance gold: a single AXFR query can reveal your entire network topology, internal server names, IP addresses, mail infrastructure, and subdomain structure—information that would otherwise require hours of subdomain brute-forcing and OSINT work. Tools like averagesecurityguy's AXFR exist to help security researchers quantify how widespread this misconfiguration remains across the internet's millions of domains.

Technical Insight

dnspython Library

Parallel Workers

NS Records

Success

Fail/Timeout

Domain List File

GNU Parallel Executor

Python AXFR Script

DNS Resolver

Zone Transfer Attempts

.axfr Output Files

Silent Skip

stats.py Aggregator

Markdown Reports

System architecture — auto-generated

The AXFR tool's architecture is refreshingly simple: it's essentially a 100-line Python script that wraps the dnspython library in a parallelization strategy. The genius isn't in complex code—it's in recognizing that DNS zone transfer testing is an embarrassingly parallel problem and choosing the right tool for the job.

Here's the core zone transfer logic:

import dns.resolver
import dns.zone
import dns.query

def check_axfr(domain):
    try:
        answers = dns.resolver.resolve(domain, 'NS')
        for nameserver in answers:
            ns = str(nameserver.target)
            try:
                zone = dns.zone.from_xfr(
                    dns.query.xfr(ns, domain, lifetime=5.0)
                )
                # Successful transfer - write to file
                with open(f'{domain}.axfr', 'w') as f:
                    f.write(f'Domain: {domain}\n')
                    f.write(f'Nameserver: {ns}\n')
                    f.write(zone.to_text())
            except Exception:
                pass  # Nameserver refused or timed out
    except Exception:
        pass  # Domain has no NS records

The script queries each domain for its authoritative nameservers (NS records), then attempts an AXFR request against each one. The lifetime=5.0 parameter is a crucial architectural decision: it kills slow transfers after 5 seconds, trading completeness for speed. When you're scanning a million domains, waiting 30+ seconds for stragglers would turn a 2-hour job into a multi-day ordeal.

But the real architectural insight is what AXFR doesn't do: it doesn't implement its own threading, multiprocessing, or async I/O. Instead, it delegates parallelism entirely to GNU Parallel:

cat top-1m.csv | parallel -j 100 --pipe -N 1000 \
  python3 axfr.py

This single command spawns 100 concurrent Python processes, each receiving 1,000 domains via stdin. GNU Parallel handles process management, load balancing, and job distribution—capabilities that would require hundreds of lines of Python multiprocessing code and careful attention to GIL contention, queue management, and signal handling. By outsourcing parallelism to a battle-tested Unix utility, the tool stays simple and maintainable.

The output architecture is equally pragmatic: each successful zone transfer writes to a separate .axfr file named after the domain. No databases, no JSON parsing, no complicated aggregation logic. This file-per-result approach means you can:

  • Resume interrupted scans (skip domains that already have .axfr files)
  • Use standard Unix tools like grep, wc -l, and find to analyze results
  • Safely kill and restart the parallel scan without corrupting a central database
  • Easily share individual results without extracting from a monolithic data structure

The companion stats.py script leverages this design, using simple file globbing to aggregate results:

import glob

vulnerable_domains = glob.glob('*.axfr')
print(f'Total vulnerable: {len(vulnerable_domains)}')

for axfr_file in vulnerable_domains:
    with open(axfr_file) as f:
        records = len(f.readlines())
        print(f'{axfr_file}: {records} DNS records leaked')

This Unix philosophy approach—small tools that do one thing well, file-based communication, composability—makes AXFR far more flexible than a monolithic scanner with a built-in database would be.

Gotcha

The 5-second timeout is both AXFR's strength and its Achilles heel. While it enables rapid scanning, legitimate zone transfers from geographically distant or slow DNS servers can easily exceed this threshold. I've seen authoritative nameservers on overloaded shared hosting take 10-15 seconds to complete valid AXFR responses. The tool will report these as failures, giving you false negatives—domains that are actually vulnerable but got filtered out due to performance constraints. If you're conducting a thorough penetration test rather than broad research, you'll want to manually retry interesting domains with longer timeouts.

The aggressive parallelism strategy also has consequences. Spawning 100+ concurrent processes means potentially hammering DNS infrastructure with thousands of queries per second. While AXFR queries themselves are legitimate DNS protocol operations, this volume will trigger rate limiting on many DNS providers, get your IP address flagged by intrusion detection systems, or even result in temporary bans from recursive resolvers. There's no built-in rate limiting, no random delays, no stealth mode. This is a research tool designed for speed, not operational security. If you're scanning domains you don't own without explicit authorization, you're likely violating computer fraud laws in most jurisdictions—and you'll be noisy enough to get caught.

Verdict

Use if: You're conducting authorized security research on DNS misconfigurations at scale, performing penetration tests where you need to quickly identify zone transfer vulnerabilities across a client's domain portfolio, or teaching DNS security concepts and want a clean, readable example of the attack vector. The tool's simplicity makes it perfect for understanding the fundamentals before reaching for more complex enumeration frameworks. Skip if: You need stealth capabilities for red team operations (the parallelism is too aggressive), you're working in production environments where false negatives matter (the timeout is too strict), or you want an all-in-one DNS reconnaissance tool with subdomain brute-forcing, DNSSEC analysis, and other features—in those cases, reach for dnsrecon or fierce instead. This is a specialized research instrument, not a general-purpose scanner.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/averagesecurityguy-axfr.svg)](https://starlog.is/api/badge-click/developer-tools/averagesecurityguy-axfr)