← Back to Research

Risk-Projector Case Study: How One Forgotten Subdomain Becomes a Board-Level Number

The framing. A finding is not a risk. A finding is a fact: this subdomain resolves, that certificate is still trusted, this cookie is scoped too widely. Risk is what happens next: the chain that turns the fact into a number a board will act on. This is a walkthrough of the risk-projection reasoning we run, from one forgotten subdomain to a six-figure annualized band, and the two configuration lines that collapse it.

Most security tooling stops at the fact. The interesting work, and the work a customer pays for, is the projection: given this one exposure, what is the realistic blast radius, and how do we put a defensible number on it? This is a methodology piece. We walk a single, deliberately mundane exposure (a forgotten subdomain pointed at a decommissioned host, with a stale TLS certificate still trusted by clients) through the full risk-projector reasoning. No customer, no real names. The point is the shape of the reasoning, and the small calculations that keep it honest.

The exposure: one CNAME nobody owns anymore

Start with the fact. During deep DNS enumeration on a generic target we find legacy-app.example.com. It is a CNAME to a cloud bucket endpoint that was deprovisioned eighteen months ago. The DNS record was never cleaned up: a dangling record. Two things follow immediately, and they compound:

  1. Takeover surface. Because the backing resource is gone, an attacker who can claim that same bucket name inherits the hostname. They now serve content from a domain your users trust, your SSO redirect allowlist may still trust, and your wildcard certificate may still cover.
  2. Trust erosion at the TLS layer. The wildcard *.example.com certificate is still valid and still presented. A subdomain an attacker controls inherits a green padlock. The cert-validation trust model assumes the holder of a valid leaf is authorized; a dangling subdomain quietly breaks that assumption.

The second leg is the one teams under-weight. The entire point of a certificate is to bind identity to a name, and the industry has spent real engineering effort closing the gaps where that binding fails: the OpenSSL X.509 punycode certificate-verification flaws CVE-2022-3602 and CVE-2022-3786 are a reminder that “the cert verified” is a load-bearing assumption that has had buffer-overflow bugs sitting underneath it. When a valid cert covers a name an attacker controls, you don’t even need a verification bug; you’ve handed them the trust for free.

Step 1: establish the reachable consequence, not the theoretical one

The risk-projector’s first rule: project from what is reachable, not what is imaginable. A dangling subdomain is only interesting if something downstream trusts the hostname. So we enumerate the trust edges:

The middle one is the multiplier. A cookie set with Domain=.example.com is sent by the browser to every subdomain, including the one an attacker now controls. That is how a single forgotten DNS record becomes session interception for the primary application. The defensible posture is host-only cookies and the __Host- prefix, which the browser refuses to honor with a Domain attribute at all:

# Wrong: readable by any subdomain, including a hijacked one
Set-Cookie: session=...; Domain=.example.com; Secure; HttpOnly

# Right: host-only, cannot be sent to legacy-app.example.com
Set-Cookie: __Host-session=...; Path=/; Secure; HttpOnly; SameSite=Lax

If the cookie is host-only, the takeover leg cannot reach the primary session, and the projection collapses to “defacement of an unused name,” annoying, not material. If it is domain-scoped, the projection escalates to “session interception against authenticated users of the production app.” Same DNS fact, two orders of magnitude of difference in business impact. The reachability check is what decides which world you’re in.

Step 2: turn severity into a business number

CVSS is a technical severity score, not a business-impact score, and pretending otherwise is how risk registers end up full of “Highs” nobody can prioritize. The risk-projector keeps the two separate and maps between them explicitly. We use a small, auditable transform: take the CVSS base score, multiply by an asset-criticality weight and an exposure-likelihood factor, and express the result as an annualized loss expectancy band. The arithmetic is deliberately simple so a non-engineer can challenge any input:

# Risk projection: CVSS -> annualized business-impact band (illustrative)
def project_risk(cvss_base, asset_value_usd, exposure_factor, annual_rate):
    """
    cvss_base       : 0.0-10.0 technical severity (NVD base score)
    asset_value_usd : replacement/at-risk value of the trust the asset carries
    exposure_factor : 0.0-1.0 share of that value at risk if exploited (SLE driver)
    annual_rate     : expected exploit events per year (ARO)
    """
    severity_weight = cvss_base / 10.0                  # normalize to 0-1
    single_loss = asset_value_usd * exposure_factor * severity_weight
    annualized = single_loss * annual_rate              # ALE = SLE x ARO
    return round(single_loss), round(annualized)

# Dangling subdomain WITH domain-scoped session cookie:
sle, ale = project_risk(
    cvss_base=8.1,               # session interception over network, high impact
    asset_value_usd=2_000_000,   # at-risk value of authenticated-user trust
    exposure_factor=0.35,        # fraction realistically compromised per event
    annual_rate=0.4,             # one credible attempt every ~2.5 years
)
print(sle, ale)   # -> 567000 226800

# Same DNS fact, but cookies are host-only (takeover cannot reach session):
sle2, ale2 = project_risk(8.1, 2_000_000, 0.02, 0.4)
print(sle2, ale2)  # -> 32400 12960

The numbers are illustrative, but the structure is the deliverable. The exposure factor (the single input most sensitive to the cookie-scoping decision) collapses the annualized figure from a low-six-figure band to a low-five-figure one, roughly a seventeen-fold reduction. That delta is the business case for the one-line cookie fix, expressed in the currency a board uses. The projector’s job is not to be precisely right; it is to be transparently arguable, so every input can be challenged and re-weighted in review.

Step 3: make it detectable, then prove the fix

A projection that cannot be re-measured is a guess. So each leg of the chain gets a detection query that runs on every continuous-monitoring scan, and the projector re-runs after remediation to show the band actually moved.

# Leg 1: dangling-record detector. Resolves but backing resource is gone
dig +short legacy-app.example.com CNAME
#  -> some-bucket.cloud-provider.example   (claimable? check ownership)

# Leg 2: cookie-scope detector. Is the session cookie domain-wide?
curl -sI https://app.example.com/login \
  | grep -i '^set-cookie:' | grep -i 'Domain=\.'
#  Any matching line is a domain-wide cookie. Takeover can reach the primary session.

The detection is evidence-first by construction. We do not mint a finding because a subdomain might be takeover-able; we mint it when the backing resource is provably unclaimed-by-you and the cookie is provably domain-scoped, so the chain is real. A dangling record whose cookie is host-only is reported as a hygiene item with a near-zero band, not a critical, because the projection says so, and we can show the work.

What the case study generalizes to

The mundane exposure is the point. Nobody loses sleep over a leftover CNAME. The risk-projector exists to show, with arithmetic anyone can audit, that a leftover CNAME plus a domain-scoped cookie plus a still-trusted wildcard cert is a session-interception chain with a six-figure annualized band, and that two configuration lines collapse it to a rounding error. That is the difference between a scanner that hands you a list of facts and a platform that hands you a prioritized, re-measurable, defensible number.

How Celvex Sentry tests for this

Our continuous-monitoring coverage runs deep DNS enumeration to surface forgotten subdomains, checks each one for the dangling/takeover condition, and diffs the trust edges (redirect allowlists, cookie scope, first-party fetches) that decide whether a takeover is material. When the chain is real, we mint a Proof Capsule with the projected business-impact band, the detection queries above, and a remediation that names the cheapest leg to cut. After you fix it, we re-run the projection and show the band moved, never on a vibe, always on re-measured evidence.

Pen-testers hand you a PDF once a year; Celvex Sentry finds the forgotten doors every week, projects the blast radius honestly, and proves the fix moved the number.

Sources

Get your exposure check: full report in 4-24 hours

Full report in 4-24 hours. Real assessment on production-grade infrastructure. Paying customers get priority capacity.

Queue My Assessment