Poppy and the Public Suffix Pandemonium: A DNS Boundary Bedtime Story
Featuring a security engineer who reads DNS specs for fun, boundaries that browsers respect (unlike humans), and the ghost of a marketing campaign that refuses to die.
TL;DR: The Public Suffix List isn’t just for cookies—it’s a treasure map to your dangling CNAMEs. Find them before someone else does.
# Pull Amazon-related PSL entries, then hunt CNAMEs ending in them
psl -d amazonaws.com | tee aws-psl.txt
cat subs.txt | dnsx -cn | rg -f aws-psl.txt | tee cnames-into-aws.txt
Use it: https://github.com/cybercdh/psl
Poppy Suffix didn’t choose the DNS life; the DNS life chose her when she accidentally read RFC 6265 during what was supposed to be a lunch break. Three hours later, she emerged with an unsettling understanding of cookie boundaries and the realization that most of the internet was held together with CNAME records and wishful thinking.
Her coffee mug (Gerald V, artisanal, slightly pretentious) witnessed the moment of revelation: “Gerald, what if I told you that browsers know exactly where ownership boundaries are, and we’re just… not using that information?”
Gerald V, being handcrafted pottery, offered no response but somehow conveyed deep skepticism.
The Public Suffix List (PSL) is the internet’s most underappreciated bouncer. It tells browsers things like: “You cannot set a cookie for all of .com, you absolute muppet” and “github.io is a boundary—everything before it belongs to users, not GitHub.” This second fact is where things get spicy.
Poppy discovered that her company had 47 CNAMEs pointing to various platform-as-a-service providers. Normal, right? Except 12 of them pointed to resources that no longer existed. Like archaeological ruins, but instead of ancient pottery, it was spring-sale-2019.herokuapp.com still bravely CNAMEd from deals.company.com.
Poppy ran my psl tool, which fetches and filters the PSL. The PSL has entries like:
github.io
herokuapp.com
pages.dev
vercel.app
s3-website-us-east-1.amazonaws.com
s3-website-eu-west-1.amazonaws.com
cloudfront.net
Each of these is a boundary. If your subdomain CNAMEs to something inside one of these suffixes and the backing resource is gone, anyone who can claim that resource gets to answer for your subdomain.
Copy me:
# 1) Enumerate subdomains
assetfinder -subs-only example.com | tee subs.txt
# 2) Get PSL slices for providers you actually use
psl -d github.io > gh.txt
psl -d amazonaws.com > aws.txt
psl -d herokuapp.com > heroku.txt
# 3) Find CNAME targets that point into those suffixes
cat subs.txt | dnsx -cn | tee cnames.txt
rg -f <(cat gh.txt aws.txt heroku.txt) cnames.txt | tee interesting.txt
# 4) Check if the resource exists (provider-specific heuristics)
# S3 website buckets (existence):
awk '/s3-website-/{print $1"\t"$3}' interesting.txt | while read host target; do
b=$(echo "$target" | sed -E 's/\.(s3-website-[^.]+\.amazonaws\.com)$/,/\1/; s/\.s3-website-[^.]+\.amazonaws\.com$//')
# naive: try HTTP and look for NoSuchBucket
code=$(curl -sS -o - -w '%{http_code}' "http://$target" | rg -q "NoSuchBucket" && echo NSB || echo $code)
echo -e "$host\t$target\t$code"
done | tee s3-triage.txt
# GitHub Pages (gh.io) takeover sniff (404/Project not found is a known pattern):
awk '/github\.io/{print $1"\t"$3}' interesting.txt | while read host target; do
code=$(curl -sS -o - -w '%{http_code}' "https://$target" | rg -q "There isn't a GitHub Pages site here|Create a repository" && echo MAYBE || echo $code)
echo -e "$host\t$target\t$code"
done | tee ghp-triage.txt
You get the idea: PSL → provider suffixes → your CNAMEs into those suffixes → provider‑specific existence checks. Poppy’s teabag had barely unfurled and she already had a short list of “this used to exist” candidates.
The investigation that followed was equal parts archaeology and horror movie. Poppy’s findings read like a digital estate sale inventory:
The Graveyard Shift:
blog-2018.company.com→company-blog-old.github.io(404 since 2020)promo.company.com→summer-deals.herokuapp.com(Heroku: “There’s nothing here, yet.”)cdn-legacy.company.com→company-assets.s3-website-us-east-1.amazonaws.com(NoSuchBucket)partners.company.com→partner-portal.azurewebsites.net(This web app has been stopped)
“Gerald,” Poppy said, holding the mug like a therapy animal, “we’re one bored teenager away from hosting a phishing site on our own subdomain.”
The beautiful (terrifying?) thing about the PSL is that it tells you exactly where the danger zones are. If github.io is a public suffix, then anything-before-it.github.io is user-controlled territory. Your CNAME is just a redirect sign. If the destination doesn’t exist, anyone can build a new destination and your sign will happily point to it.
Poppy didn’t just find problems; she found patterns. Every abandoned marketing campaign, every “temporary” demo site, every proof-of-concept that became production-ish—they all left CNAMEs behind like breadcrumbs for future attackers.
Why PSL helps your mental model (and your tea schedule):
- Cookie scope: You cannot set cookies on a public suffix. If you try to scope your session cookie to
github.io“for convenience,” the browser will say “no” and you will invent new swear words. Good. - Ownership boundary: The label below a public suffix is someone’s space. If your subdomain points into that space (CNAME), ensure you still own the thing there.
- Hunting strategy: Start from suffixes your org uses; PSL saves you guessing the list.
Provider check sketches (be nice; don’t hammer; make tea):
# CloudFront: 403/404 are normal; "Bad domain" patterns are interesting
awk '/cloudfront\.net/{print $1"\t"$3}' interesting.txt | while read host target; do
code=$(curl -sS -o - -w '%{http_code}' "https://$host" | rg -q "ERROR|Bad request" && echo MAYBE || echo $code)
echo -e "$host\t$target\t$code"
done | tee cf-triage.txt
Bonus: a tiny Go snippet that uses the publicsuffix library to ask “is this a registrable eTLD+1, and what’s the boundary?” It pairs well with a psl prefetch cache.
package main
import (
"fmt"
"golang.org/x/net/publicsuffix"
)
func main(){
for _, h := range []string{"example.github.io","cdn.cloudfront.net","foo.s3-website-us-east-1.amazonaws.com"} {
etld1, _ := publicsuffix.EffectiveTLDPlusOne(h)
sfx, _ := publicsuffix.PublicSuffix(h)
fmt.Printf("host=%s etld+1=%s suffix=%s\n", h, etld1, sfx)
}
}
Report snippet (bug bounty/responsible disclosure):
Title: CNAME to provider public-suffix with unclaimed/deleted resource (potential takeover)
Affected: events.example.com
Target: events-2019.s3-website-eu-west-1.amazonaws.com
Evidence:
- PSL shows s3-website-eu-west-1.amazonaws.com is a public suffix boundary
- HTTP response indicates "NoSuchBucket" (resource no longer exists)
Impact: An attacker could create the bucket "events-2019" and serve arbitrary content at events.example.com
Actions taken: No resource creation or claim attempted; discovery only
Recommended fix: Remove/replace the CNAME or recreate and control the backing resource; add monitoring for CNAMEs into public-suffix providers
The cleanup took a week. Not because it was technically complex, but because each dead CNAME had a story. Marketing didn’t know that campaign site was gone. DevOps thought someone else owned that subdomain. That partner portal? “Wait, we have a partner portal?”
By Friday, Poppy had:
- Removed 12 dangling CNAMEs (each a potential takeover)
- Documented 23 more that were “probably fine but please check”
- Implemented a monthly scan using psl + dnsx
- Created a Slack channel called #dns-ghosts where CNAMEs go to be acknowledged before deletion
The last message to Gerald V, before closing the laptop: “We’ve drawn the boundaries. We’ve honored the ghosts. We’ve prevented the subdomain takeover apocalypse of 2024.”
Gerald V maintained artistic silence, but it felt approving.
The PSL isn’t just a list—it’s a map of the internet’s property lines. Once you see them, you can’t unsee them. You’ll spot dangling CNAMEs like typos in a love letter. You’ll understand why that cookie isn’t working. You’ll finally get why something.github.io and github.io/something live in completely different universes.
Your CNAMEs are probably pointing to dead things right now. The PSL knows where they are. Poppy knows how to find them. Gerald V is judging you, ceramically.
Go check. Before someone else does.