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:
- 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.
- Trust erosion at the TLS layer. The wildcard
*.example.comcertificate 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:
- Is the subdomain in any OAuth/OIDC
redirect_uriallowlist? (Takeover → token theft.) - Is the session cookie scoped to the parent domain (
Domain=.example.com) rather than host-only? (Takeover → session interception across the whole estate.) - Does any first-party JavaScript
fetch()the subdomain? (Takeover → script injection into trusted pages.)
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
- NVD: CVE-2022-3602 (OpenSSL X.509 punycode certificate-verification overflow)
- NVD: CVE-2022-3786 (OpenSSL X.509 punycode certificate-verification overflow)
- MDN: Set-Cookie cookie prefixes (__Host- / __Secure-)
- FIRST: Common Vulnerability Scoring System (CVSS)
- Celvex Sentry: how the scanner catches this
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