Dangling Dns on Aws
How Dangling DNS Manifests in AWS
In AWS, a dangling DNS record occurs when a Route 53 record (usually a CNAME or ALIAS) continues to point to a resource that has been deleted — such as an Elastic Load Balancer, an API Gateway stage, or an S3‑hosted static website. Because the record still resolves, an attacker can claim the underlying AWS resource and serve arbitrary content under your domain.
A common pattern is seen when developers automate environment teardown but forget to clean up Route 53 entries. For example, a Python script using boto3 may create an ELB and a corresponding CNAME:
import boto3
elb = boto3.client('elbv2')
route53 = boto3.client('route53')
# Create load balancer
lb = elb.create_load_balancer(
Name='my-service-lb',
Subnets=['subnet-111aaa','subnet-222bbb'],
Type='application'
)
lb_arn = lb['LoadBalancers'][0]['LoadBalancerArn']
lb_dns = elb.describe_load_balancers(LoadBalancerArns=[lb_arn])['LoadBalancers'][0]['DNSName']
# Create Route 53 CNAME pointing to the LB
route53.change_resource_record_sets(
HostedZoneId='Z3ABCDEFGHIJKL',
ChangeBatch={
'Changes':[{
'Action':'CREATE',
'ResourceRecordSet':{
'Name':'api.example.com.',
'Type':'CNAME',
'TTL':300,
'ResourceRecords':[{'Value':lb_dns}]
}
}]
}
)
If the script later deletes the load balancer but omits the Route 53 change, the CNAME remains:
elb.delete_load_balancer(LoadBalancerArn=lb_arn)
# Missing: route53.change_resource_record_sets(...) with Action='DELETE'
The dangling CNAME now resolves to an AWS‑generated domain like my-service-lb-1234567890.us-east-1.elb.amazonaws.com. If an attacker creates a new load balancer in the same region and happens to get the same generated DNS name (possible when the naming pattern is predictable), they can hijack api.example.com. This mirrors real‑world subdomain takeover incidents such as CVE‑2020‑13942 (GitHub Pages) and has been observed in AWS environments where Elastic Load Balancer names follow a predictable pattern.
AWS‑Specific Detection
Detecting dangling DNS in AWS involves checking whether the value of a Route 53 record corresponds to an existing resource. You can do this manually with the AWS CLI or automate it as part of a CI pipeline.
Using the AWS CLI, list all CNAME/ALIAS records and verify each target:
aws route53 list-resource-record-sets --hosted-zone-id Z3ABCDEFGHIJKL \
--query "ResourceRecordSets[?Type==\(CNAME\|ALIAS\).Name]" --output text
For each record, resolve its value and compare against known AWS resource identifiers. A simple Python check:
import boto3
route53 = boto3.client('route53')
elbv2 = boto3.client('elbv2')
records = route53.list_resource_record_sets(HostedZoneId='Z3ABCDEFGHIJKL')['ResourceRecordSets']
for r in records:
if r['Type'] in ('CNAME','ALIAS'):
target = r['ResourceRecords'][0]['Value'] if 'ResourceRecords' in r r.get('AliasTarget',{}).get('DNSName')
# Strip trailing dot
target = target.rstrip('.')
# Check if target matches any ELB DNS name
lbs = elbv2.describe_load_balancers()['LoadBalancers']
if not any(lb['DNSName'].rstrip('.') == target for lb in lbs):
print(f'Dangling record: {r["Name"]} -> {target}')
middleBrick can help detect the exposure side of this issue. When you submit an API endpoint (e.g., https://api.example.com) to middleBrick, it performs unauthenticated black‑box testing of the attack surface. If the hostname resolves to a dangling record that points to a deletable AWS service, middleBrick’s “Inventory Management” and “Data Exposure” checks may flag the endpoint as risky because the underlying service could be claimed by an attacker. The scan does not need any AWS credentials; it works purely from the public DNS resolution and HTTP responses.
To integrate detection into your pipeline, add a step that runs the above script (or a similar Lambda‑based check) and fails the build if any dangling records are found. middleBrick’s GitHub Action can then be used to verify that the API exposed via those records does not present additional vulnerabilities.
AWS‑Specific Remediation
The safest way to eliminate dangling DNS is to ensure the lifecycle of a Route 53 record is tightly coupled to the lifecycle of the resource it points to. AWS provides native features that make this coupling straightforward.
1. **Use AWS CloudFormation or CDK** – define both the load balancer and its DNS record in the same template. When the stack is deleted, CloudFormation automatically removes the record.
# CloudFormation (YAML)
Resources:
MyLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: my-service-lb
Subnets: [subnet-111aaa, subnet-222bbb]
Type: application
MyDnsRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: Z3ABCDEFGHIJKL
Name: api.example.com.
Type: CNAME
TTL: '300'
ResourceRecords:
- !GetAtt MyLoadBalancer.DNSName
2. **Lambda‑based cleanup** – if you cannot use IaC, attach a cleanup function to the resource‑deletion event via AWS CloudWatch Events (EventBridge).
import boto3
def lambda_handler(event, context):
# Expect event from ELB deletion
lb_arn = event['detail']['LoadBalancerArn']
route53 = boto3.client('route53')
# Find any CNAME pointing to this LB and delete it
records = route53.list_resource_record_sets(HostedZoneId='Z3ABCDEFGHIJKL')['ResourceRecordSets']
elb = boto3.client('elbv2')
lb_dns = elb.describe_load_balancers(LoadBalancerArns=[lb_arn])['LoadBalancers'][0]['DNSName'].rstrip('.')
changes = []
for r in records:
if r['Type'] == 'CNAME':
target = r['ResourceRecords'][0]['Value'].rstrip('.')
if target == lb_dns:
changes.append({
'Action': 'DELETE',
'ResourceRecordSet': r
})
if changes:
route53.change_resource_record_sets(
HostedZoneId='Z3ABCDEFGHIJKL',
ChangeBatch={'Changes': changes}
)
return {'statusCode': 200}
3. **AWS Config rule** – create a custom rule that evaluates whether every Route 53 CNAME/ALIAS has a matching active resource (ELB, API Gateway, etc.). Non‑compliant resources trigger alerts via SNS, allowing rapid remediation.
By tying DNS record creation and deletion to the same management construct that provisions the underlying AWS service, you eliminate the window where a dangling record can be exploited. middleBrick’s continuous monitoring (available on the Pro plan) can periodically rescan your APIs and notify you if a previously safe endpoint becomes risky due to a newly introduced dangling DNS record.