Back to Blog

Subdomain Takeover: The Silent Attack That Hijacks Your Brand (and How to Stop It)

Subdomain takeovers let attackers serve malicious content under your trusted domain — capturing credentials, running phishing attacks, or bypassing cookie security. Learn how to detect vulnerable subdomains and automate monitoring before attackers find them first.

Published

8 min read

Reading time

Subdomain Takeover: The Silent Attack That Hijacks Your Brand (and How to Stop It)

It starts with a CNAME record that was never cleaned up.

Six months ago, your team spun up a marketing landing page on a Heroku dyno, pointed campaign.yourdomain.com at myapp-marketing.herokuapp.com, and ran the campaign. When the campaign ended, you deleted the Heroku app. But no one deleted the CNAME record.

Now myapp-marketing.herokuapp.com is available for anyone to register. Someone registers it, sets up a fake page that looks like your login screen, and suddenly campaign.yourdomain.com — a subdomain carrying your trusted domain's cookies and certificate — is serving credentials-harvesting content.

This is a subdomain takeover. It is not theoretical. Uber, Shopify, GitHub, and dozens of major companies have had subdomain takeovers reported against them through bug bounty programs. The fix is straightforward once you know where to look. The danger is that most teams do not know where to look.


How Subdomain Takeovers Work

sequenceDiagram
    participant DNS as Your DNS
    participant CNAME as CNAME Target
    participant Attacker as Attacker

    Note over DNS,CNAME: 6 months ago
    DNS->>CNAME: campaign.yourdomain.com → stale.herokuapp.com
    Note over CNAME: App was deleted, but CNAME remains

    Note over DNS,CNAME: Today
    Attacker->>CNAME: Register stale.herokuapp.com
    CNAME-->>Attacker: Ownership = Attacker
    DNS->>CNAME: Users visit campaign.yourdomain.com
    CNAME-->>Attacker: Attacker's content served under yourdomain.com

The attack succeeds because:

  1. Your DNS still points to the provider's namespace
  2. The specific target (Heroku slug name, S3 bucket, GitHub Pages repo, etc.) is now unclaimed
  3. The attacker claims it at the provider level
  4. Your DNS resolves correctly to their content

The Most Vulnerable Service Categories

Not all CNAME targets are equally vulnerable. The risk depends on whether the provider allows unclaimed names to be registered by new users:

Service Takeover Risk Notes
Heroku 🔴 High App names can be recycled and registered
GitHub Pages 🔴 High Deleted repos/users can be reclaimed
Amazon S3 🔴 High Bucket names are global namespace
AWS CloudFront 🟡 Medium Requires specific distribution IDs
Netlify 🔴 High Site names easily re-claimable
Vercel 🟡 Medium Better protections, but still possible
Azure Blob 🔴 High Storage account names are global
Fastly 🟡 Medium Requires specific configuration
Custom VPS 🟢 Low IP can be reassigned, harder to exploit

Step 1: Enumerate Your Attack Surface

You cannot protect subdomains you do not know exist. Start with a full enumeration:

# Using subfinder for passive DNS enumeration
subfinder -d yourdomain.com -all -o subdomains.txt

# Using crt.sh for certificate transparency log mining
curl -s "https://crt.sh/?q=%25.yourdomain.com&output=json" | \
  jq -r '.[].name_value' | sort -u > ct-subdomains.txt

# Combine and deduplicate
cat subdomains.txt ct-subdomains.txt | sort -u > all-subdomains.txt

echo "Total subdomains found: $(wc -l < all-subdomains.txt)"

For most companies, this enumeration reveals far more subdomains than the team was aware of — legacy staging environments, old campaign pages, forgotten API subdomains.


Step 2: Check for Dangling CNAME Records

A dangling CNAME is a DNS record pointing to a target that no longer exists or responds:

#!/bin/bash
# check-dangling-cnames.sh

while IFS= read -r subdomain; do
  # Get CNAME target
  cname=$(dig CNAME "$subdomain" +short)

  if [ -n "$cname" ]; then
    # Check if the CNAME target responds
    http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "https://$subdomain")

    if [ "$http_code" = "000" ] || [ "$http_code" = "404" ]; then
      echo "⚠️  POTENTIAL DANGLING CNAME: $subdomain → $cname (HTTP: $http_code)"
    fi
  fi
done < all-subdomains.txt

Step 3: Automated Detection with Cross-Reference

More sophisticated detection cross-references your CNAME targets against known vulnerable service fingerprints:

// scripts/check-subdomain-takeover.ts
interface TakeoverCandidate {
  subdomain: string;
  cname: string;
  service: string;
  fingerprint: string;
  severity: 'critical' | 'high' | 'medium';
}

// Fingerprints of vulnerable provider error pages
const TAKEOVER_FINGERPRINTS = [
  {
    service: 'Heroku',
    cnamePattern: /\.herokuapp\.com$/,
    errorFingerprint: 'There is no app configured at that hostname',
    severity: 'critical' as const,
  },
  {
    service: 'GitHub Pages',
    cnamePattern: /\.github\.io$/,
    errorFingerprint: "There isn't a GitHub Pages site here",
    severity: 'critical' as const,
  },
  {
    service: 'Netlify',
    cnamePattern: /\.netlify\.app$/,
    errorFingerprint: 'Not Found - Request ID',
    severity: 'critical' as const,
  },
  {
    service: 'Amazon S3',
    cnamePattern: /\.s3\.amazonaws\.com$/,
    errorFingerprint: 'NoSuchBucket',
    severity: 'high' as const,
  },
  {
    service: 'Zendesk',
    cnamePattern: /\.zendesk\.com$/,
    errorFingerprint: 'Help Center Closed',
    severity: 'high' as const,
  },
];

async function checkForTakeover(subdomain: string): Promise<TakeoverCandidate | null> {
  const cname = await resolveCNAME(subdomain);
  if (!cname) return null;

  for (const fp of TAKEOVER_FINGERPRINTS) {
    if (!fp.cnamePattern.test(cname)) continue;

    try {
      const response = await fetch(`https://${subdomain}`, {
        signal: AbortSignal.timeout(10_000),
      });
      const body = await response.text();

      if (body.includes(fp.errorFingerprint)) {
        return {
          subdomain,
          cname,
          service: fp.service,
          fingerprint: fp.errorFingerprint,
          severity: fp.severity,
        };
      }
    } catch (error) {
      // Connection refused / DNS NXDOMAIN can also indicate dangling record
    }
  }
  return null;
}

Step 4: CI/CD Integration

Run subdomain takeover checks as part of your security CI pipeline:

# .github/workflows/subdomain-security.yml
name: Subdomain Takeover Check
on:
  schedule:
    - cron: '0 6 * * *' # Daily at 6 AM
  push:
    paths:
      - 'infra/dns/**' # Also run when DNS config changes

jobs:
  subdomain-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install subfinder
        run: |
          go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest

      - name: Enumerate subdomains
        run: |
          subfinder -d ${{ vars.DOMAIN }} -all -o subdomains.txt
          echo "Found $(wc -l < subdomains.txt) subdomains"

      - name: Check for takeover candidates
        run: npx ts-node scripts/check-subdomain-takeover.ts subdomains.txt

      - name: Alert on findings
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          text: '🚨 CRITICAL: Subdomain takeover candidates detected!'
          channel: '#security-alerts'

Step 5: Immediate Remediation When Found

If you discover a vulnerable subdomain, act immediately:

PRIORITY 1 (do in the next hour):
  → Add a placeholder record at the CNAME target if possible
  → Remove the dangling CNAME record from DNS

PRIORITY 2 (do in the next day):
  → Audit ALL CNAME records and clean up stale ones
  → Check for cookies with Domain=.yourdomain.com that could be stolen
  → Rotate any session tokens that could have been captured

PRIORITY 3 (do this week):
  → Implement DNS record lifecycle process (cleanup on service deprovisioning)
  → Add subdomain enumeration to your CI/CD pipeline
  → Consider using DNS CAA records to restrict certificate issuance

Preventing Future Takeovers: Process Changes

The technical fix (removing dangling CNAMEs) is only half the solution. The process fix prevents new ones from appearing:

flowchart LR
    A[Provision new subdomain] --> B[Document in DNS registry]
    B --> C[Deploy service]
    C --> D[Service lifecycle]
    D --> E{Service\nbeing retired?}
    E -->|Yes| F[Decommission checklist]
    F --> G[Remove CNAME record FIRST]
    G --> H[Then delete cloud resource]
    H --> I[Update DNS registry]
    E -->|No| D

The key insight: always remove the DNS record before removing the cloud resource. In the reverse order, there is a window of vulnerability between resource deletion and DNS cleanup.


Monitoring Your Subdomains with ScanlyApp

ScanlyApp's continuous scan capability can be configured to monitor your public-facing subdomains for unexpected content changes — including the signature error pages that indicate a takeover has occurred or is imminent.

By scheduling scans against your subdomain inventory, you get an early warning system: if legacy.yourdomain.com suddenly starts returning a GitHub Pages "site not found" page instead of its expected content, you will be alerted before attackers capitalize on it.

Set up subdomain monitoring today: Try ScanlyApp free and schedule scans against your subdomain inventory to detect content changes and takeover indicators.

Related articles: Also see a complete web security testing foundation to build on, automating dynamic security scans alongside subdomain monitoring, and other critical misconfigurations that leave your site exposed.


Summary: The Subdomain Takeover Defense Matrix

Layer Action Frequency
Enumeration Run subfinder + crt.sh Weekly
Fingerprint check Check error pages against known patterns Daily
DNS audit Review all CNAME records Monthly
Process Cleanup checklist on deprovisioning Every time
Monitoring Automated scan of subdomain inventory Daily
Alerting Slack/PagerDuty on detection Real-time

Subdomain takeovers are entirely preventable. The combination of regular enumeration, automated fingerprint checking, and a disciplined deprovisioning process eliminates the risk in practice. The question is simply whether you invest 4 hours to build the monitoring, or whether you discover the vulnerability through a bug bounty report — or worse, a security incident.

Further Reading

Monitor your subdomains continuously for takeover risk: Try ScanlyApp free and schedule automated subdomain health scans that alert you before misconfigurations become exploits.

Related Posts