← back to lessons

Lesson: Vercel DEPLOYMENT_NOT_FOUND is a fingerprint, not a confirmed takeover - the team-verification gate decides claimability

Banked: 2026-06-12 Source: Elastic (HackerOne public bounty) passive subdomain-takeover recon, lane = web2-sprawl-enterprise Class: subdomain-takeover / SaaS-dangling-domain triage

The pattern (new, banked)

A subdomain CNAME'd to cname.vercel-dns.com (or A to a Vercel edge IP) that returns: - port 80: 308 Permanent Redirect, server: Vercel (Vercel recognizes the Host) - port 443: HTTP 404, server: Vercel, x-vercel-error: DEPLOYMENT_NOT_FOUND - frequently an EXPIRED Let's Encrypt cert (Vercel stops renewing once the deployment is deleted -> expiry date marks roughly when it was abandoned)

is the Vercel dangling-domain FINGERPRINT. It is necessary but NOT sufficient for takeover.

The claimability gate (the actual decision boundary)

Vercel only lets a new project attach a custom domain if that hostname (and/or its apex) is NOT already verified to another Vercel team. Two sub-cases:

  1. CLAIMABLE: the deployment was deleted AND the domain/apex is no longer team-verified -> an attacker can add host as a custom domain to their own project and serve content. Real takeover.
  2. NOT CLAIMABLE: the deployment was deleted BUT the apex (e.g. elastic.dev) is still verified to the owner's Vercel team -> Vercel rejects "domain already in use." Same DEPLOYMENT_NOT_FOUND page, but ZERO takeover. This is the common false-positive.

DEPLOYMENT_NOT_FOUND alone CANNOT distinguish these two. The expired-cert + DEPLOYMENT_NOT_FOUND combo is suggestive of abandonment but does not resolve the apex-verification question.

Rule

Cross-reference to existing banked rules

Negative results banked this cycle (avoid re-walking)

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