aws-route53-dangling-alias-takeover
A dangling alias or CNAME record pointing to a deprovisioned AWS resource enables subdomain takeover - an attacker (unauthenticated, from the internet) claims the deprovisioned resource and serves content on the hijacked name without any AWS IAM permission required.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?zone) ==
DNS
?zone.provider_type == 'AWS::Route53::HostedZone'
?zone.properties.has_dangling_alias == true
?target_resource.state == 'deprovisioned' or ?target_resource is not found in inventory
?zone.properties.dangling_record_types contains 'A (alias)' OR ?zone.properties.dangling_record_types contains 'AAAA (alias)' OR ?zone.properties.dangling_record_types contains 'CNAME'
emit
| source type | DNS |
|---|---|
| target type | AnonymousIdentity |
| source | ?zone |
| target | public-internet |
| conditions | service_state |
| state logic | CONDITIONAL(service_state) — the dangling record exists (confirmed by inventory cross-reference showing the alias target is absent from collected resources) but actual exploitability depends on whether the target resource type is claimable: S3 static website bucket (same-name, same-region) -> ACTIVE if bucket is unclaimed; CloudFront distribution CNAME -> ACTIVE if no current distribution claims the alternate domain; API Gateway custom domain -> CONDITIONAL (requires ownership verification via DNS TXT record as of 2022, which may not be present on the attacker's domain); ELB/ALB DNS name -> CONDITIONAL (random suffix, hard to claim); EIP -> CONDITIONAL until confirmed released. Never BLOCKED (no IAM permission exists to prevent an external attacker from claiming an unclaimed resource). |
Narrative
Hosted zone {zone.name} contains a dangling alias/CNAME record pointing to a deprovisioned AWS resource. An attacker who claims that resource (e.g. creates a new S3 bucket with the same name, or a new CloudFront distribution using the same alternate domain) will serve content and/or capture traffic under the hijacked subdomain - no Route 53 permission required.
Raw rule rules/derived/aws/route53.yaml
id: aws-route53-dangling-alias-takeover
emits: ExposedToInternet
description: "A dangling alias or CNAME record pointing to a deprovisioned AWS resource enables subdomain\
\ takeover \u2014 an attacker (unauthenticated, from the internet) claims the deprovisioned resource\
\ and serves content on the hijacked name without any AWS IAM permission required."
applies_to:
- aws
match:
- - zone: null
- ContainsResourceReference
- target_resource: null
where:
- node_type(?zone) == DNS
- ?zone.provider_type == 'AWS::Route53::HostedZone'
- ?zone.properties.has_dangling_alias == true
- ?target_resource.state == 'deprovisioned' or ?target_resource is not found in inventory
- ?zone.properties.dangling_record_types contains 'A (alias)' OR ?zone.properties.dangling_record_types
contains 'AAAA (alias)' OR ?zone.properties.dangling_record_types contains 'CNAME'
emit:
source_type: DNS
target_type: AnonymousIdentity
source: ?zone
target: public-internet
permissions: []
conditions:
- service_state
state_logic: "CONDITIONAL(service_state) \u2014 the dangling record exists (confirmed by inventory cross-reference\
\ showing the alias target is absent from collected resources) but actual exploitability depends on\
\ whether the target resource type is claimable: S3 static website bucket (same-name, same-region)\
\ -> ACTIVE if bucket is unclaimed; CloudFront distribution CNAME -> ACTIVE if no current distribution\
\ claims the alternate domain; API Gateway custom domain -> CONDITIONAL (requires ownership verification\
\ via DNS TXT record as of 2022, which may not be present on the attacker's domain); ELB/ALB DNS name\
\ -> CONDITIONAL (random suffix, hard to claim); EIP -> CONDITIONAL until confirmed released. Never\
\ BLOCKED (no IAM permission exists to prevent an external attacker from claiming an unclaimed resource)."
confidence: 0.8
derived_from:
- ?zone ContainsResourceReference ?target_resource (dangling alias/CNAME)
false_positive_note: "This edge requires CROSS-REFERENCING the alias/CNAME target against live resource\
\ inventory (ELB, CloudFront, S3, API GW). Do NOT emit based solely on a record existing \u2014 the\
\ target must be confirmed absent from the account's collected resources. Claimability varies by resource\
\ type (see state_logic above). ELB DNS names with random suffixes are generally NOT claimable by\
\ re-creating an ELB \u2014 downgrade those to CONDITIONAL or POTENTIAL. No IAM permission is required\
\ for this attack path, so BLOCKED state is not applicable. This edge does not yield CanEnterAccount\
\ directly \u2014 it enables traffic capture on the hijacked name, which is a pre-condition for out-of-band\
\ credential theft. Cite can-control.yaml: Controls roll-up is NOT appropriate here."
narrative: "Hosted zone {zone.name} contains a dangling alias/CNAME record pointing to a deprovisioned\
\ AWS resource. An attacker who claims that resource (e.g. creates a new S3 bucket with the same name,\
\ or a new CloudFront distribution using the same alternate domain) will serve content and/or capture\
\ traffic under the hijacked subdomain \u2014 no Route 53 permission required."