HIGH dangling dnsaws

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.

Frequently Asked Questions

How does middleBrick detect a dangling DNS issue without AWS credentials?
middleBrick performs unauthenticated black‑box testing of the submitted URL. It resolves the hostname and checks the HTTP response for signs that the underlying AWS service (e.g., an ELB or API Gateway) is missing or misbehaving. If the hostname points to a resource that can be reclaimed, the scan flags the endpoint under its Inventory Management and Data Exposure checks, providing remediation guidance without needing any IAM keys.
What AWS‑native approach guarantees that a Route 53 record is removed when its target resource is deleted?
Defining both the resource and its DNS record in the same AWS CloudFormation (or CDK) stack ensures deletion propagation. When the stack is removed, CloudFormation automatically deletes the Route 53 record, eliminating the window for a dangling DNS entry.