Back to Articles

Building an Async Subdomain Takeover Scanner: Inside autoSubTakeover's DNS Pipeline

[ View on GitHub ]

Building an Async Subdomain Takeover Scanner: Inside autoSubTakeover's DNS Pipeline

Hook

Every month, security researchers claim thousands of dollars in bug bounties by finding orphaned DNS records pointing to services that attackers can claim. The race to find these vulnerabilities comes down to who can check CNAMEs faster.

Context

Subdomain takeover vulnerabilities emerge when an organization's DNS records point to external services they no longer control. The classic scenario: a company creates blog.example.com with a CNAME pointing to their-blog.github.io, later abandons the GitHub Pages site without removing the DNS record, and an attacker registers the same GitHub Pages username to serve malicious content from what appears to be the company's legitimate subdomain. These vulnerabilities have affected major organizations including Uber, Shopify, and Microsoft.

Traditional subdomain enumeration tools focus on discovery—finding what subdomains exist—but leave the takeover analysis as a manual afterthought. Security researchers would export lists of thousands of subdomains, then painstakingly check CNAME records one by one. autoSubTakeover emerged to automate this second phase: rapidly processing subdomain lists to identify CNAME records pointing outside the target's infrastructure, flagging the specific cases where takeover might be possible. It's a reconnaissance multiplier designed for bug bounty hunters who need to triage large attack surfaces efficiently.

Technical Insight

Async Event Loop

Yes

No

No - Out of Scope

Yes - In Scope

Yes

No

Subdomain Wordlist

Async DNS Resolver

CNAME Record Found?

Extract CNAME Target

Skip

Target in Scope?

Check Domain Status

Domain Expired?

Report Vulnerability

System architecture — auto-generated

autoSubTakeover's architecture centers on Python's asyncio event loop to parallelize DNS queries that would otherwise block sequentially. The tool uses aiodns, a wrapper around c-ares (a C library for asynchronous DNS requests), allowing it to fire off hundreds of CNAME lookups concurrently without spawning threads or processes.

The core workflow starts with reading a subdomain wordlist and querying each for CNAME records. Here's the essential pattern the tool follows:

import aiodns
import asyncio

async def check_cname(resolver, subdomain, scope_domains):
    try:
        # Query for CNAME record
        result = await resolver.query(subdomain, 'CNAME')
        cname_target = result.cname
        
        # Check if CNAME points outside our scope
        in_scope = any(scope in cname_target for scope in scope_domains)
        
        if not in_scope:
            print(f"[!] Potential takeover: {subdomain} -> {cname_target}")
            return (subdomain, cname_target)
    except aiodns.error.DNSError:
        # No CNAME record or DNS error
        pass
    return None

async def scan_subdomains(subdomain_list, scope_domains):
    resolver = aiodns.DNSResolver()
    tasks = [check_cname(resolver, sub, scope_domains) 
             for sub in subdomain_list]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return [r for r in results if r is not None]

The scope comparison logic is where autoSubTakeover adds its value. When blog.victim.com resolves to old-account.github.io instead of anything containing victim.com, that's a red flag. The tool doesn't just find CNAMEs—it identifies the specific subset that cross organizational boundaries.

A second layer of detection checks whether the target domain in the CNAME chain has expired or is available for registration. The tool queries WHOIS data or attempts DNS resolution on the pointed-to domain itself. If attacker.s3.amazonaws.com doesn't resolve, or if abandoned-startup.com shows as an available domain registration, the subdomain takeover opportunity becomes more concrete. This expiration checking transforms the output from "interesting CNAME" to "likely exploitable misconfiguration."

The asynchronous architecture makes a measurable difference at scale. Testing 10,000 subdomains synchronously might take 30-45 minutes with standard DNS timeout values. With aiodns running 100+ concurrent queries, that same scan completes in 3-5 minutes. The event loop efficiently handles the I/O wait time inherent in DNS queries—while waiting for one nameserver response, it's already sent dozens of other requests.

One implementation detail worth noting: the tool uses Click for CLI argument parsing, which provides a clean interface for specifying scope domains and subdomain input files. The scope definition accepts multiple domains or patterns, allowing researchers to define their target organization's infrastructure broadly:

python autosubtakeover.py -l subdomains.txt -s victim.com -s victim-cloud.net

This scope flexibility matters because modern organizations use multiple domains, cloud provider namespaces, and CDN configurations. A CNAME pointing to victim-cdn.cloudfront.net might be perfectly legitimate, while one pointing to competitor-service.herokuapp.com definitely isn't.

Gotcha

The most significant limitation is that autoSubTakeover identifies candidates but doesn't verify exploitability. Finding a CNAME to an external service is step one; confirming you can actually claim that service is step two. Different platforms have different takeover mechanics—GitHub Pages requires specific repository naming, AWS S3 buckets need to match exact names, Heroku apps must be available for registration. autoSubTakeover doesn't include platform-specific fingerprinting to distinguish between "points to external service" and "points to external service that's actually claimable." You'll get false positives from legitimate third-party integrations and CDNs that are working as intended.

The dependency situation is concerning for production security tooling. The repository pins Tornado 6.0.3 from 2019 and hasn't seen significant updates recently. Python's async ecosystem has evolved considerably since then, and older dependencies may contain unpatched vulnerabilities or compatibility issues with Python 3.10+. For security practitioners who need to justify their toolchain to compliance teams, this staleness is a blocker. You're also limited to CNAME-based detection—subdomain takeovers can occur through A records pointing to deallocated IP addresses or NS records pointing to unregistered nameservers, vectors this tool doesn't address.

Verdict

Use if: You're processing large subdomain lists during bug bounty reconnaissance and need a fast first-pass filter to identify suspicious CNAME records pointing outside your target's infrastructure. It's particularly valuable when you're already comfortable with manual verification and just need to reduce a 50,000-subdomain list down to 200 interesting candidates worth investigating. The async performance makes it practical for wide-scope assessments where you're checking multiple organizations or large attack surfaces. Skip if: You need comprehensive subdomain takeover detection with platform-specific verification, up-to-date tooling with maintained dependencies, or you're working in environments where dependency auditing is mandatory. Modern alternatives like subjack or nuclei's takeover templates provide more complete detection with active verification. Also skip if you're new to subdomain takeover hunting—you'll get better results learning the fundamentals with tools that explain why something is vulnerable, not just that it might be.

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