DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

Subdomain Takeover Severity Comes From Security Context, Not the Exploit Mechanism

Subdomain Takeover Severity Comes From Security Context, Not the Exploit Mechanism

saostatic.uber.com pointed to an unregistered CloudFront distribution. The mechanism was mundane: a dangling CNAME, claimable in minutes. The impact was complete SSO bypass across all *.uber.com services. Most triage teams would have rated it differently if the subdomain were blog.uber.com. They would have been right, but for reasons most practitioners cannot articulate.

The takeover mechanism is identical regardless of the subdomain. Severity diverges based on the security context that subdomain carries: cookie scope, participation in OAuth whitelists, trust granted by CSP. Treating all dangling CNAMEs with the same rating is a triage failure that buries critical account takeover bugs under informational classifications.

Mechanism-based triage has a legitimate justification: CNAME fingerprint scanning scales to thousands of subdomains in minutes. Security context analysis does not scale the same way. The 4-question framework in section 6 addresses that tradeoff: each question takes under 2 minutes and applies individually to the scanner's high-confidence findings.

The Mechanism Is Identical. The Severity Is Not.

The EdOverflow/can-i-take-over-xyz repository documents over 50 services (GitHub Pages, Heroku, Fastly, azurewebsites.net, AWS S3, Shopify) where the takeover mechanism is the same: claim the unregistered external resource a CNAME points to. The fingerprint changes per provider. The exploit primitive does not.

Bug bounty data confirms the impact divergence. The same CloudFront fingerprint applied to auth.company.com yields Critical. Applied to docs.company.com it yields Informational or no-action. The difference is not in what the attacker does; it is in what the subdomain was trusted to do.

The table below shows the divergence directly:

Subdomain Mechanism Impact
auth.example.com Dangling CNAME (CloudFront) SSO bypass, account takeover
api.example.com Dangling CNAME (CloudFront) CORS bypass, data exfiltration
cdn.example.com Dangling CNAME (CloudFront) CSP bypass, script injection
blog.example.com Dangling CNAME (CloudFront) Phishing, defacement

The mechanism is identical across all 4 rows. The impact column is determined by security context, not the exploit.

Cookie Domain Scope: The First Question Triage Gets Wrong

Cookies with the Domain=.example.com attribute are sent by the browser to any subdomain of example.com, including subdomains taken over by an attacker. A takeover of any subdomain within that scope is equivalent to session theft on the main application, regardless of the subdomain's business function.

HackerOne #219205 (Arne Swinnen, April 2017) demonstrates this with surgical precision. saostatic.uber.com was a static CDN subdomain with no critical function of its own. The _csid session cookie was scoped to *.uber.com. By claiming the abandoned subdomain via CloudFront, Swinnen could exfiltrate the _csid of any user visiting any Uber property, resulting in authentication bypass on vault.uber.com, partners.uber.com, riders.uber.com, and all other *.uber.com services. The bounty was $5,000.

The USENIX Security '21 study (Squarcina, Tempesta, Veronese, Calzavara) quantified the scale of the problem: of 887 sites with 1,520 vulnerable subdomains analyzed in the top 50,000, 81% had cookie confidentiality issues exposed to related-domain attackers.

The practical check is immediate:

curl -sI https://app.target.com | grep -i set-cookie
Enter fullscreen mode Exit fullscreen mode

If Domain= includes a leading dot (.example.com), any taken subdomain results in session theft. The __Host- prefix (RFC 6265bis) is the only mitigation that fully prevents cross-subdomain transmission. Production adoption: negligible.

SameSite=Lax Does Not Protect What Practitioners Think It Does

SameSite=Lax protects against cross-site requests based on top-level navigation. It does not protect against same-site requests, and by the W3C definition of "same-site" (eTLD+1 -- effective Top-Level Domain plus one, the registrable domain, e.g., github.com, not api.github.com), a controlled subdomain of target.com is same-site with target.com.

PortSwigger Web Security Academy documents this explicitly: sub1.example.com and sub2.example.com share the same site (example.com). SameSite provides no protection between them. An attacker with control of a subdomain can make cross-origin but same-site requests and receive session cookies, with no SameSite policy violation.

USENIX '21 confirms this at scale. Of all affected sites analyzed, 99% had integrity issues via related-domain positioning: session fixation, CSRF, state manipulation. SameSite=Lax as a default in modern browsers does not change that number, because the protection operates at site granularity, not origin granularity. An attacker with a subdomain is already inside the protected granularity.

The practical implication is direct. A CSRF token combined with SameSite=Lax provides no additional protection against attackers who control a subdomain. Any subdomain takeover impact analysis that cites "SameSite mitigates" without qualifying "except from controlled subdomains" is wrong.

OAuth redirect_uri: When a Takeover Becomes an Account Takeover

OAuth authorization servers that accept redirect_uri by pattern-match (*.example.com or https://example.com/*) turn any subdomain takeover within that pattern into an authorization code theft vector. The attacker does not need to compromise the main application. They only need 1 subdomain that matches the pattern.

HackerOne #405100 documents the complete redirect_uri chain. saostatic.uber.com was in the set of valid domains for the Uber SSO redirect_uri. With the subdomain taken, the attacker initiates a legitimate OAuth flow as the victim, the authorization code is delivered to the controlled endpoint, and the flow completes with the victim's token without any interaction from them. Redirect URIs with pattern-match allow authorization code theft without any additional subdomain takeover; with the takeover already executed, the attacker controls the receiving endpoint and exploitation is immediate, with no dependency on user interaction beyond the normal OAuth flow.

The OWASP OAuth Security Cheat Sheet is explicit on prevention: exact-match on redirect_uri, never wildcard or pattern. Most OAuth implementations in production audited through bug bounty programs violate this recommendation. The check is straightforward:

# Check auto-discovery configuration
curl -s https://target.com/.well-known/oauth-authorization-server | jq '.redirect_uris_supported'
Enter fullscreen mode Exit fullscreen mode

Any *.yourdomain.com entry in the IdP panel is a line of credit for the next takeover.

CSP and CORS: How an "Informational" Finding Becomes a Policy Bypass

CSP headers with subdomain wildcards (script-src *.example.com) and CORS policies that allowlist subdomain origins are completely nullified by any takeover within the allowed scope. The takeover primitive that appeared to be "planting a phishing page" becomes script injection in the context of the main application.

USENIX '21 quantifies the CSP impact precisely. Related-domain attackers achieve CSP bypass on 139 additional domains compared to external cross-site attackers. For CORS, the increase in exploitation capability is +54%. The same taken subdomain that only yielded phishing for an external attacker becomes arbitrary script execution for an attacker with a related-domain position.

The CORS mechanism is direct. If the main application responds with Access-Control-Allow-Credentials: true and the taken subdomain is on the allowlist, the attacker can make credentialed cross-origin requests to the API and receive responses with the victim's data. CDN subdomains are frequently on CORS and CSP allowlists. CDN subdomains are the most common takeover targets due to the widespread adoption of services like CloudFront and Fastly.

# Check main application CSP
curl -sI https://app.target.com | grep -i content-security-policy

# Check CORS
curl -sI -H "Origin: https://cdn.target.com" https://api.target.com | grep -i access-control
Enter fullscreen mode Exit fullscreen mode

If cdn.target.com is on the CSP or CORS allowlist and has a dangling CNAME, the rating is not Informational. It is High.

A Severity Framework That Triage Teams Can Actually Use

Correct classification starts with 4 questions, in this order. Each "yes" raises the severity floor, not the ceiling.

Question 1: Is the subdomain within the session cookie scope? Check Domain=.example.com in responses from the main application and all authentication subdomains.

Question 2: Is the subdomain, or the pattern it belongs to, in any OAuth redirect_uri whitelist? Check the IdP configuration and auto-discovery endpoint.

Question 3: Is the subdomain on any CSP (script-src, img-src, connect-src) or CORS allowlist of the main application? Check headers in the main app response.

Question 4: Does the subdomain have an MX record pointing to an unregistered third-party email service? Email takeover enables password reset via the controlled domain.

Based on the answers, the severity matrix is:

Tier Typical subdomains Reason
Critical auth., sso., login., account. In session cookie scope by design; almost certainly in OAuth allowlist
High api., cdn., static.* Likely in CORS and CSP allowlists; may share cookie scope
Medium app., portal., dashboard.* Context-dependent; audit Domain attr and OAuth config before rating
Low blog., docs., status.* Minimal security relationships, unless verified misconfiguration

Tools like the subdomain_enum from intel.mago.team (MAGO team tool) automate the enumeration of dangling DNS records; the 4-question framework determines remediation priority by actual severity.

The classic triage mistake is assigning severity based on the subdomain name without verifying the actual configuration. saostatic.uber.com looks like Tier Medium by name. Its cookie configuration placed it in Critical. That gap is where $5,000 bugs stay buried as Informational.

Removing a dangling CNAME is trivial. Knowing which ones are emergencies and which are informational is the problem this framework solves.

Top comments (0)