The most surprising thing about Route 53 subdomain takeovers is how often they’re caused by a service you thought you’d deleted.

Let’s see this in action. Imagine you have app.example.com pointing to a Heroku app. You then delete the Heroku app, but forget to remove the Route 53 record. A malicious actor can then create a new Heroku app with the exact same name as your deleted one. Because Route 53 still thinks the old app is valid, it’ll direct traffic for app.example.com to the attacker’s new Heroku app. The attacker now controls your subdomain.

Here’s a simplified DNS resolution path. When a user types app.example.com into their browser:

  1. The browser asks a DNS resolver (like 8.8.8.8) for the IP address of app.example.com.
  2. The DNS resolver queries the root DNS servers, then the .com TLD servers, until it finds the authoritative name servers for example.com.
  3. The authoritative name servers for example.com are managed by Route 53.
  4. Route 53 looks up the record for app.example.com. If it finds a CNAME record pointing to something.heroku.com, it returns something.heroku.com to the resolver.
  5. The resolver then asks for the IP address of something.heroku.com.
  6. Heroku’s DNS returns the IP address of the app.
  7. The user’s browser connects to that IP address.

The vulnerability occurs at step 4. If the target of the CNAME (something.heroku.com in this example) no longer exists or is available for re-registration, an attacker can claim it.

The core problem is a dangling DNS record – a DNS record in Route 53 that points to an external resource that no longer exists or is no longer under your control. Route 53 itself is just doing its job: it’s faithfully returning the information it has. The breakdown happens in the target of that information.

To prevent this, you need a systematic approach to managing your DNS records and the resources they point to.

First, identify all your subdomains and their DNS records. A good starting point is to list all records in your Route 53 hosted zone.

aws route53 list-resource-record-sets --hosted-zone-id Z123ABCDEFG456 --query 'ResourceRecordSets[*].{Name:Name,Type:Type,AliasTarget:AliasTarget,ResourceRecords:ResourceRecords}' --output json

This command will give you a JSON output of all records. Look specifically for CNAME records and ALIAS records that point to external services.

For each CNAME or ALIAS record pointing to an external service (like *.amazonaws.com, *.cloudapp.net, *.azurewebsites.net, *.herokuapp.com, *.bitbucket.io, *.github.io, etc.), you must verify that the target resource actually exists and is still managed by you.

The most common culprits are SaaS platforms where you create a custom subdomain. If you delete the resource on the SaaS platform (e.g., a GitHub Pages site, a Heroku app, a Netlify site), you must also delete the corresponding DNS record in Route 53.

Here’s a common scenario: You have blog.example.com CNAME’d to yourusername.github.io. You then delete the GitHub Pages repository. The CNAME record in Route 53 remains. An attacker can fork your old repository, rename it to yourusername.github.io, and push some malicious content. Route 53 will happily point blog.example.com to the attacker’s new GitHub Pages site.

Diagnosis: Use dig or nslookup to check the resolution of a suspicious subdomain.

dig CNAME blog.example.com +short

If it returns something like yourusername.github.io, proceed to check if yourusername.github.io is still actively serving content under your control. Visit https://yourusername.github.io in your browser. If it shows unexpected content or an error indicating the repository is gone, you have a dangling record.

Common Causes and Fixes:

  1. Deleted SaaS Resources: You deleted an app on Heroku, a site on Netlify, a repository on GitHub Pages, etc., but left the Route 53 record.

    • Diagnosis: Check the Route 53 record. If it’s a CNAME to a known SaaS provider (e.g., *.herokuapp.com, *.netlify.app, *.github.io), visit the target URL (e.g., your-app-name.herokuapp.com). If it returns an error or "not found," the record is dangling.
    • Fix: Delete the dangling CNAME record from your Route 53 hosted zone.
      • Example command to delete a record:
        aws route53 change-resource-record-sets --hosted-zone-id Z123ABCDEFG456 --change-batch '{
            "Changes": [
                {
                    "Action": "DELETE",
                    "ResourceRecordSet": {
                        "Name": "blog.example.com.",
                        "Type": "CNAME",
                        "TTL": 300,
                        "ResourceRecords": [
                            {
                                "Value": "yourusername.github.io."
                            }
                        ]
                    }
                }
            ]
        }'
        
      • Why it works: Removing the CNAME record prevents Route 53 from trying to resolve a non-existent or attacker-controlled endpoint.
  2. Unused Aliases to AWS Resources: You have an ALIAS record pointing to an AWS resource (like an S3 bucket, CloudFront distribution, or ELB) that you’ve since deleted. Route 53 ALIAS records are AWS-specific and can point to other AWS resources.

    • Diagnosis: If an ALIAS record points to an AWS resource, verify that resource still exists within your AWS account. Check the DNSName and HostedZoneId in the AliasTarget object for the record.
    • Fix: Delete the ALIAS record in Route 53.
      • Example command to delete an ALIAS record pointing to an S3 bucket:
        aws route53 change-resource-record-sets --hosted-zone-id Z123ABCDEFG456 --change-batch '{
            "Changes": [
                {
                    "Action": "DELETE",
                    "ResourceRecordSet": {
                        "Name": "static.example.com.",
                        "Type": "A",
                        "AliasTarget": {
                            "HostedZoneId": "Z3AQBSTGFYJSTF",
                            "DNSName": "my-static-bucket.s3-website-us-east-1.amazonaws.com.",
                            "EvaluateTargetHealth": false
                        }
                    }
                }
            ]
        }'
        
      • Why it works: Deleting the ALIAS record removes the reference to the now-defunct AWS resource.
  3. Wildcard Subdomains with External Targets: A wildcard record like *.example.com CNAME’d to an external service. If that external service is deleted, any subdomain not explicitly defined will be vulnerable.

    • Diagnosis: Check for wildcard CNAME records (e.g., *.example.com CNAME external.service.com.). Then, check if external.service.com is still valid and under your control.
    • Fix: Either delete the wildcard CNAME record entirely if it’s not needed, or change it to point to a valid, actively managed resource (e.g., a default page hosted on S3 or CloudFront). If you absolutely need a wildcard, ensure the target is robust and you monitor its status.
      • Why it works: The wildcard is the most dangerous. Removing it eliminates the broad attack surface.
  4. Expired Domains Used for DNS: You might have used a DNS record pointing to a domain that you later let expire. The registrar could then reassign that domain.

    • Diagnosis: Examine CNAME targets. If a target looks like a domain that might have expired (e.g., old-company-domain.com), check its registration status using a WHOIS lookup.
    • Fix: Delete the CNAME record in Route 53. If you need that domain, re-register it and update the DNS.
      • Why it works: It severs the link to the potentially compromised or reassigned domain.
  5. Misconfigured Third-Party Integrations: Sometimes, third-party services automatically create DNS records for you. If you disable or remove that integration without cleaning up Route 53, the record can become dangling.

    • Diagnosis: Review CNAME or ALIAS targets. If they point to services you’ve recently disconnected or uninstalled, investigate those specific records.
    • Fix: Delete the associated Route 53 records.
      • Why it works: It removes the stray pointer from your DNS zone.
  6. Incomplete Decommissioning: When shutting down services or applications, failing to audit and clean up all associated DNS records is a common oversight. This includes records for load balancers, CDNs, application instances, or static file hosting.

    • Diagnosis: Perform a full audit of your Route 53 zone against your inventory of active services. Any DNS record pointing to a decommissioned service is a risk.
    • Fix: Delete all identified dangling records.
      • Why it works: Ensures your DNS zone only contains records for actively managed resources.

When you delete a CNAME record in Route 53, it’s immediately gone. However, DNS resolvers worldwide cache records for a certain period based on their TTL (Time To Live). So, even after deletion, it might take some time for the change to propagate globally.

The next error you’ll hit after fixing subdomain takeovers is likely a NXDOMAIN (Non-Existent Domain) error for a subdomain you intended to remove, or perhaps a SERVFAIL if a different external service you point to has an issue.

Want structured learning?

Take the full Route53 course →