← back to lessons

Lesson: subdomain-takeover "dangling != claimable" - the four false-positive classes

Banked: 2026-06-12. Source: Cycle Web2-sprawl-consumer (Yahoo / AOL / TechCrunch, 847 subs, WALK CLEAN).

Thesis refinement

Large multi-acquisition orgs concentrate takeover EV (validated: Playtika, Spotify). BUT sprawl raises the base-rate of candidates to triage, not of actual takeovers. A well-run estate (Yahoo) can be heavily third-party-pointed (23/847 hosts on SaaS) and still have ZERO self-service-claimable dangling. A subdomain that "resolves to a third party but shows a not-found / error page" is NOT automatically a takeover. Distinguish DANGLING (no live content) from CLAIMABLE (a third party can self-service bind it).

Four FALSE-POSITIVE classes to reject (each looks like a takeover, none is)

  1. WAF / anti-bot challenge wall (Tumblr/Automattic edge, also DataDome, CF-Access). Fingerprint: 403 + JS interstitial ("Checking your browser...") + challenge cookie (_hcc) + /__challenge. This is a routed, CLAIMED domain being challenged - NOT the "There's nothing here" unclaimed-blog state. Reject. (Same family as the banked DataDome/CF-Access CORS reflection FPs in MEMORY.)
  2. Portal-owned SaaS 404 (HubSpot). Fingerprint: 404 BUT x-hs-portal-id: <ID> present + NotFoundResolver / "404 predicted at edge". The portal-id proves the domain is connected to a specific HubSpot portal; 404 only means no page is published at that path. The domain is owned. NOT claimable. Reject. (Contrast: a real HubSpot takeover needs the domain addable to a DIFFERENT portal -> no portal-id resolves.)
  3. Account-gated platform 404 (WordPress VIP / go-vip.net, also Pantheon, WP Engine). Fingerprint: plain nginx 404 "no site mapped to this host" on the platform IP range (192.0.66.x for go-vip). Dangling (no site) but custom-domain attachment is account-side only - a third party CANNOT add an arbitrary domain to the org's account. Dangling-but-NOT-self-service-claimable. Log, do NOT submit.
  4. CloudFront origin-error vs distribution-not-found. CLAIMABLE fingerprint = "Bad request" / "The distribution ... could not be found" (distro deleted, CNAME addable elsewhere). NON-candidate = 502/403/200 "ERROR: The request could not be satisfied" -> distro EXISTS, origin is just erroring. Always check the A-record resolves AND the exact CF error string. A resolving CloudFront A-record = distro exists = not dangling.

Operational rule

For every third-party-pointed host, answer TWO questions, not one: (a) Is there live org content? (claimed vs not) (b) If not, can a THIRD PARTY self-service bind this exact host on that service? (claimable vs org-gated) Only (not-claimed) AND (self-service-claimable) = stage. Everything else = log, walk. Cheapest discriminators: real-Host HTTPS fetch + service-specific header (x-hs-portal-id, x-statuspage-version, go-vip nginx-404 on 192.0.66.x, Tumblr /__challenge) + exact CloudFront/S3 error string. Resolving A-record on an anycast SaaS edge is usually CLAIMED, not dangling.

Generated 2026-07-02 13:15:05 UTC | auto-sync /15min