Here's a bug that costs an attacker nothing to find and costs you your domain's reputation.
- Someone on your team points a subdomain at a cloud service. One DNS line:
promo.yourcompany.com CNAME yourcompany.pages-host.io - Months later the cloud account gets deleted. The DNS record does not.
- That
CNAMEnow points at a name on someone else's platform that belongs to nobody — until a stranger registers it.
From that moment they publish whatever they like, on your subdomain, with your certificate and your reputation. It's called subdomain takeover, and the reason it keeps happening is that every step is something a competent team does on a normal day.
Let's find yours. Everything below runs locally, reads only public data, and takes about a minute.
Step 1: enumerate subdomains you've forgotten
You can't audit what you can't list, and the DNS zone in your provider's UI is only the records that still exist — not the historical ones you should be looking for.
The trick is certificate transparency logs: every TLS certificate ever issued is published to a public, append-only log. If a subdomain ever had HTTPS, it's in there. crt.sh makes that queryable:
curl -s "https://crt.sh/?q=%25.example.com&output=json" \
| jq -r '.[].name_value' \
| sed 's/^\*\.//' \
| sort -u
Expect surprises. This routinely surfaces staging hosts, an old marketing subdomain, and a service someone trialled in 2022.
Step 2: find the CNAMEs
Dangling records are almost always CNAMEs — a record pointing at a name on someone else's infrastructure, rather than an IP you control.
dig +short CNAME promo.example.com
Empty output means it isn't a CNAME, so it isn't this class of bug. Skip it.
Step 3: check whether the target still exists
This is the actual test. If the CNAME target resolves to nothing, the record is dangling:
dig +short A yourcompany.pages-host.io
dig +short CNAME yourcompany.pages-host.io
Both empty → nobody is answering for that name. On a platform that lets anyone register an unused project name, that's a claimable takeover.
Putting it together
#!/usr/bin/env bash
# Find dangling CNAME records on a domain.
# Usage: ./dangling.sh example.com
set -euo pipefail
domain="${1:?usage: $0 example.com}"
curl -s "https://crt.sh/?q=%25.${domain}&output=json" \
| jq -r '.[].name_value' \
| sed 's/^\*\.//' \
| sort -u \
| while read -r host; do
target=$(dig +short CNAME "$host" | sed 's/\.$//')
[ -z "$target" ] && continue
if [ -z "$(dig +short A "$target")" ] && [ -z "$(dig +short CNAME "$target")" ]; then
echo "DANGLING $host -> $target"
else
echo "ok $host -> $target"
fi
done
Needs curl, jq and dig. Output looks like:
ok www.example.com -> example.com
DANGLING promo.example.com -> example.pages-host.io
Anything on a DANGLING line is a live issue. Delete the DNS record — that's the whole fix. If you still need the subdomain, re-create the service first and point the record at something you actually control.
Why this deserves more attention than it gets
Two things make subdomain takeover nastier than its low profile suggests.
It defeats the one rule users are taught. "Check the URL before you enter your password" fails completely here, because the URL is real. A phishing page on your own subdomain passes the exact check people are trained to trust — and if any of your cookies are scoped to .yourcompany.com, they may be readable from it too.
Nobody is targeting you. Attackers run this exact scan across millions of domains continuously and harvest whatever falls out. Your forgotten subdomain isn't found because someone was hunting you; it's found because someone was hunting everyone.
The habit worth forming
Most security advice is about doing something. This one is about un-doing — the records, accounts and endpoints that were fine when created and became dangerous only through neglect.
Run the script. Then put a calendar reminder to run it again next quarter, because the zone you clean today grows a new loose end the next time someone needs a landing page in a hurry.
Disclosure: I work on Webcuris, which maps exposed and dangling subdomains as part of a scan. But the script above is the same check, and it's yours — no account needed.
Originally published at webcuris.com.
Top comments (0)