OWASP Secure Coding Cheat Sheets: Subdomain Takeover Prevention Cheat Sheet

# Subdomain Takeover Prevention Cheat Sheet

## Introduction

Subdomain takeover is a vulnerability that occurs when a DNS record (typically a CNAME) points to a cloud resource or third-party service that has been deprovisioned or no longer exists. An attacker can claim the orphaned resource and serve arbitrary content on the victim's subdomain.

This vulnerability is consistently among the most reported findings in bug bounty programs. Despite being well understood, it remains prevalent because it is fundamentally an operational problem: teams create DNS records when spinning up services but rarely have processes to clean them up during decommissioning.

The impact extends far beyond serving a defacement page. An attacker controlling a subdomain can:

- **Steal session cookies** scoped to the parent domain (e.g., cookies set on `.example.com` are sent to `attacker-controlled.example.com`)
- **Bypass Content Security Policy** rules that trust wildcard subdomains (`*.example.com`)
- **Host convincing phishing pages** on a domain the organization's users and customers already trust
- **Compromise OAuth and SSO flows** that whitelist the subdomain as a valid redirect URI
- **Obtain valid TLS certificates** for the subdomain from Certificate Authorities that use HTTP or email-based domain validation
- **Receive email** addressed to the subdomain if MX records are involved, enabling password resets or account verification on third-party services

This cheat sheet provides practical guidance for developers, DevOps engineers, and infrastructure teams to prevent subdomain takeovers, detect dangling records before attackers do, and respond effectively when they are discovered.

## How Subdomain Takeover Works

### The Basic Mechanism

1. An organization creates a DNS record: `blog.example.com CNAME example-blog.herokuapp.com`
2. The Heroku app serves content on `blog.example.com`
3. Months later, the team decommissions the Heroku app but forgets to remove the DNS record
4. The CNAME still points to `example-blog.herokuapp.com`, which no longer exists
5. An attacker creates a new Heroku app with the name `example-blog` and claims the hostname
6. The attacker now controls what is served on `blog.example.com`

### Why It Keeps Happening

The root cause is almost always a disconnect between infrastructure provisioning and DNS management:

- **Cloud resources are temporary, DNS records are persistent.** Teams spin up and tear down services frequently, but DNS records tend to accumulate unless explicitly managed.
- **Different teams own different parts.** The team that created the cloud resource may not have access to DNS management, and the team that manages DNS may not know the resource was removed.
- **No automated link between resources and DNS.** Most organizations have no mechanism to detect when a DNS target stops existing.
- **Shadow IT and forgotten proof-of-concepts.** Developers create temporary subdomains for testing or demos and never clean them up.
- **Mergers, acquisitions, and reorganizations.** DNS zones inherited from acquired companies are often poorly inventoried, and the original infrastructure owners are no longer available.

### Record Types at Risk

- **CNAME records** are the most common vector. If the canonical name resolves to a service that can be claimed, takeover is possible.
- **A records** pointing to released IP addresses can be vulnerable if the IP is reassigned and the attacker obtains it. This is common in cloud environments where elastic IPs are released back to the provider's pool.
- **NS records** delegating a subdomain to a third-party DNS provider are particularly dangerous. If the account at the DNS provider is closed, anyone who creates a new account can potentially claim the delegated zone and control all records under that subdomain.
- **MX records** pointing to deprovisioned mail services can allow an attacker to receive email for the subdomain. Beyond intercepting password reset emails, this enables a more severe attack: most Certificate Authorities accept email-based domain validation (DV) using addresses like `admin@subdomain.example.com`. An attacker controlling MX records can complete DV challenges and obtain legitimate TLS certificates for the subdomain, enabling transparent HTTPS phishing or man-in-the-middle attacks.

## Cloud Provider Vulnerability Reference

Not all cloud services are equally vulnerable. The key factor is whether the service allows a new customer to claim a previously used hostname or resource name. This table is a snapshot; cloud providers continuously update their policies, so always verify current behavior against the community-maintained [can-i-take-over-xyz](https://github.com/EdOverflow/can-i-take-over-xyz) repository.

### High Risk: Takeover Possible When Resource Is Removed

| Provider/Service | Vulnerable Resource | Indicator (CNAME Target) | Takeover Mechanism |
|---|---|---|---|
| AWS S3 (Website Hosting) | S3 bucket | `*.s3.amazonaws.com`, `*.s3-website-*.amazonaws.com` | Bucket names are globally unique across all AWS accounts. If a bucket is deleted, any AWS account can recreate it with the same name and serve content on the CNAME. |
| AWS Elastic Beanstalk | Environment | `*.elasticbeanstalk.com` | Environment CNAMEs are globally unique and released on environment termination. An attacker can create a new environment with the same CNAME prefix. |
| Azure App Service | Web App | `*.azurewebsites.net` | App names are globally unique. After deletion, any Azure tenant can create a new app with the same name. Azure offers a [domain verification mechanism](https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-custom-domain) to mitigate this; see Prevention Strategies. |
| Azure Traffic Manager | Profile | `*.trafficmanager.net` | Profile names are globally unique and reclaimable after deletion. |
| Azure CDN | Endpoint | `*.azureedge.net` | Endpoint names are globally unique. A deleted endpoint name can be registered by another tenant. |
| GitHub Pages (Custom Domains) | Custom domain on repository | `*.github.io` | The primary risk is with custom domain configurations. When a repository using a custom domain is deleted or made private, the custom domain association is removed and an attacker can configure the same custom domain on their own GitHub Pages repository. Note: claiming the default `username.github.io` subdomain requires registering that GitHub username, which is not possible while the original account exists. |
| Heroku | App | `*.herokuapp.com` | App names are globally unique and released on app deletion. Any Heroku account can claim the name. |
| Shopify | Store | `shops.myshopify.com` | Custom domain associations can be claimed by any Shopify store. |
| Netlify | Site | `*.netlify.app`, `*.netlify.com` | Site names are reclaimable after deletion. |
| Fastly | CDN service | `*.fastly.net`, `*.global.ssl.fastly.net` | An attacker with a Fastly account can add the vi