← back to lessons

Lesson: Fresh-program subdomain-takeover triage -- claimed-CNAME fingerprint table

Banked 2026-06-11 from Home Bargains (HackerOne homebargains) Web2 passive recon.

Context

Fresh paid HackerOne programs are pitched as "subdomain-takeover is the classic fresh-program win." Home Bargains surfaced 6 third-party CNAMEs (Campaign Monitor, Zendesk x2, Stripe, Pixieset x2) that LOOK like dangling-takeover candidates. ALL were CLAIMED. The win was absent. The value of the cycle was the fast, deterministic CLAIMED-vs-VULNERABLE triage, not a finding.

Rule: per-service "is this CNAME claimed?" fingerprints (use to triage in seconds, avoid false hope)

Service CNAME pattern CLAIMED signal (NOT vulnerable) VULNERABLE signal (takeover)
Zendesk *.zendesk.com response header x-zendesk-processed-host-header: <your-host> (host is registered) page says "this help center no longer exists" / no processed-host header
Campaign Monitor cname.createsend.com 302 -> /login, server: csw, active app "Trying to access your account?" / "Domain not configured" page
Stripe checkout hosted-checkout.stripecdn.com 204/200 via CloudFront, Stripe-managed n/a (Stripe rarely dangling)
Pixieset domain.pixieset.com normal site OR Cloudflare cf-mitigated: challenge (claimed tenant on CF) Pixieset "No such site / not found" body
AWS S3 (CNAME to bucket) returns <Code>AccessDenied</Code> XML, x-amz-bucket-region bucket EXISTS + private (correct) <Code>NoSuchBucket</Code> -> register the bucket name = takeover

Sub-rules

  1. A third-party CNAME that returns a SERVICE-SPECIFIC "registered/active" header (e.g. x-zendesk-processed-host-header, server: csw) = CLAIMED. Stop. Not a finding. Do not chase.
  2. S3-behind-CNAME: AccessDenied != NoSuchBucket. Only NoSuchBucket (or an NXDOMAIN bucket you can register) is takeover-able. AccessDenied = bucket exists and is private = correctly configured.
  3. Cloudflare managed challenge (cf-mitigated: challenge, "Just a moment", 403/503 with challenge CSP) on a third-party-fronted host means the tenant is CLAIMED and CF-protected -- it is NOT the provider's "unclaimed/404" page. Do not mistake the CF challenge for a takeover surface.

WAF-403 vs real-exposure discriminator (banked sub-lesson)

On Cloudflare-fronted hosts, .git/config / .env / swagger.json returning 403 is almost always a blanket WAF/host block, NOT an exposed file. Discriminate by BODY SIZE: a constant-size 403 body across many different paths (e.g. all 5472 bytes), distinct from genuine 404 sizes and the real app 200 size, proves it is a uniform block page, not file-level signal. If corporate site even 403s robots.txt, the whole path namespace is WAF-walled -> artifact probing yields nothing; move on.

Scope-authorization sub-lesson (reinforces prior session finding)

HackerOne program scope (/<handle> and /<handle>/scope_versions) is JS-rendered. Server-rendered fetch returns only the literal "HackerOne". WebSearch CONFIRMS program existence/bounty status but CANNOT extract the in-scope asset list. Therefore: no quotable active-test authorization from a plain fetch -> default to PASSIVE-ONLY per ROE. This is now twice-confirmed; treat H1 scope as non-quotable-by-fetch by default.

Compound takeaway

Fresh-program takeover wins are real but rare; the repeatable value is a deterministic CLAIMED-fingerprint table so each candidate is triaged in one request instead of being chased. A CLEAN passive sweep that correctly RULES OUT 6 candidates + 2 S3 buckets is a valid, honest cycle output.

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