HIGH dangling dnsdigitalocean

Dangling Dns on Digitalocean

How Dangling Dns Manifests in Digitalocean

Dangling DNS in DigitalOcean occurs when DNS records point to resources that no longer exist or are no longer controlled by the owner. This creates a significant security risk where attackers can claim abandoned resources and intercept traffic intended for legitimate services.

In DigitalOcean's infrastructure, dangling DNS commonly appears in these specific scenarios:

  • Load Balancer Recycling: When a Load Balancer is destroyed but DNS records aren't updated, attackers can create a new Load Balancer with the same IP and intercept traffic
  • App Platform De-provisioning: DigitalOcean App Platform assigns random subdomains (app-name.app-name-.ondigitalocean.app) that may be reassigned when apps are deleted
  • Spaces Object Storage: When Spaces buckets are deleted, their endpoints become available for others to claim
  • Kubernetes Service Exposure: LoadBalancer services in DOKS can expose public IPs that get recycled

Here's a common dangling DNS scenario in DigitalOcean:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	// Attacker creates a new app after original is deleted
	app, err := appplatform.CreateApp(&appplatform.CreateAppRequest{
		Name: "vulnerable-app",
		Region: "nyc",
		Spec: appplatform.AppSpec{
			ServiceSpec: &appplatform.ServiceSpec{
				GitRepo: &appplatform.GitRepo{
					RepoCloneURL: "https://github.com/attacker/vulnerable-app.git",
					Branch: "main",
				},
			},
		},
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating app: %v\n", err)
		return
	}
	
	fmt.Printf("App created at: %s\n", app.AppURI)
	
	// Original app's DNS might still point here
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// Capture credentials, session tokens, etc.
		w.Write([]byte("Service unavailable"))
	})
	
	http.ListenAndServe(":8080", nil)
}

The DigitalOcean API doesn't automatically clean up DNS records when resources are destroyed, making this a persistent vulnerability. Attackers can monitor for recently deleted resources and rapidly provision new ones to claim the IP addresses or subdomains.

Digitalocean-Specific Detection

Detecting dangling DNS in DigitalOcean requires both passive scanning and active verification. Here are DigitalOcean-specific detection methods:

DigitalOcean API Resource Monitoring

Use the DigitalOcean API to track resource lifecycle and identify potential dangling references:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/digitalocean/godo"
)

func checkDanglingResources(ctx context.Context, client *godo.Client) {
	// Check for recently deleted resources that might have dangling DNS
	opt := &godo.ListOptions{PerPage: 100}
	
	// Check Load Balancers
	lbs, _, err := client.LoadBalancers.List(ctx, opt)
	if err != nil {
		log.Fatal(err)
	}
	
	for _, lb := range lbs {
		// Check if Load Balancer has DNS records pointing to it
		if lb.Status == "active" {
			// Verify DNS records exist for this IP
			records, err := client.Domains.ListRecords(ctx, lb.Region, &godo.ListOptions{})
			if err != nil {
				continue
			}
			
			for _, record := range records {
				if record.Type == "A" || record.Type == "CNAME" {
					// Check if record points to this Load Balancer
					if record.Data == lb.IP || record.Data == lb.ID {
						log.Printf("Potential dangling DNS: %s -> %s\n", record.Name, record.Data)
					}
				}
			}
		}
	}
}

Using middleBrick for Automated Detection

middleBrick's black-box scanning approach is particularly effective for detecting dangling DNS in DigitalOcean environments. The scanner tests the unauthenticated attack surface without requiring credentials:

# Install middleBrick CLI
npm install -g middlebrick

# Scan a DigitalOcean App Platform URL
middlebrick scan myapp-nyc.ondigitalocean.app

# Scan with specific focus on DigitalOcean services
middlebrick scan --service digitalocean myapp-nyc.ondigitalocean.app

middleBrick performs these DigitalOcean-specific checks:

Check TypeDigitalOcean ContextDetection Method
DNS ResolutionApp Platform subdomainsResolves myapp-nyc.ondigitalocean.app to current IP
Service IdentificationDigitalOcean-specific headersLooks for DO-specific server headers and response patterns
Resource AvailabilityLoad Balancer recyclingAttempts to claim recently freed IPs

Automated Monitoring Script

Set up continuous monitoring for dangling DNS in your DigitalOcean account:

#!/bin/bash

# Monitor for dangling DNS in DigitalOcean
# Run this as a cron job to detect issues

DIGITALOCEAN_TOKEN="your-token-here"

check_dangling() {
	# Get list of all domains
	curl -s -X GET "https://api.digitalocean.com/v2/domains" \
		-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
		-H "Content-Type: application/json" > domains.json
	
	# Check each domain for potentially dangling records
	for domain in $(jq -r '.[].name' domains.json); do
		echo "Checking $domain for dangling records..."
		
		# Get all records for this domain
		curl -s -X GET "https://api.digitalocean.com/v2/domains/$domain/records" \
			-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
			-H "Content-Type: application/json" > records.json
		
		# Check for records pointing to potentially recycled resources
		for record in $(jq -r '.domain_records[] | select(.type == "A" or .type == "CNAME") | @base64' records.json); do
			_jq() {
				echo ${record} | base64 --decode | jq -r ${1}
			}
			
			record_name=$(_jq '.name')
			record_data=$(_jq '.data')
			record_type=$(_jq '.type')
			
			echo "Checking: $record_type $record_name -> $record_data"
		done
	done
}

check_dangling

Digitalocean-Specific Remediation

Remediating dangling DNS in DigitalOcean requires both preventive measures and active cleanup procedures. Here are DigitalOcean-specific remediation strategies:

DigitalOcean API-Based Cleanup

Use the DigitalOcean API to programmatically clean up DNS records when resources are destroyed:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/digitalocean/godo"
)

// Clean up DNS records when a resource is destroyed
func cleanupDNSEntries(ctx context.Context, client *godo.Client, resourceID string) error {
	// Get resource information to identify associated DNS records
	resource, _, err := client.LoadBalancers.Get(ctx, resourceID)
	if err != nil {
		return err
	}
	
	// Find all domains that might have records pointing to this resource
	domains, _, err := client.Domains.List(ctx, &godo.ListOptions{PerPage: 100})
	if err != nil {
		return err
	}
	
	for _, domain := range domains {
		records, _, err := client.Domains.ListRecords(ctx, domain.Name, &godo.ListOptions{})
		if err != nil {
			continue
		}
		
		for _, record := range records {
			// Check if record points to this resource
			if record.Type == "A" && record.Data == resource.IP || 
			   record.Type == "CNAME" && record.Data == resource.ID {
				
				// Delete the dangling record
				_, err := client.Domains.DeleteRecord(ctx, domain.Name, record.ID)
				if err != nil {
					log.Printf("Failed to delete record %s: %v", record.Name, err)
				} else {
					log.Printf("Deleted dangling record: %s.%s", record.Name, domain.Name)
				}
			}
		}
	}
	
	return nil
}

// Call this when destroying a resource
func destroyLoadBalancer(ctx context.Context, client *godo.Client, lbID string) error {
	// First clean up associated DNS records
	err := cleanupDNSEntries(ctx, client, lbID)
	if err != nil {
		log.Printf("DNS cleanup error: %v", err)
	}
	
	// Then destroy the resource
	_, err = client.LoadBalancers.Delete(ctx, lbID)
	return err
}

DigitalOcean App Platform Safeguards

Implement safeguards when using DigitalOcean App Platform to prevent dangling DNS:

# Terraform configuration for App Platform with DNS management

resource "digitalocean_app" "example" {
	name = "my-secure-app"
	
	spec {
		service {
			git {
				repo_clone_url = "https://github.com/myorg/myapp.git"
				branch = "main"
			}
			
			dockerrun {
				github {
					repo = "myorg/myapp"
				}
			}
		}
	}
	
	// Enable automatic cleanup of DNS records
	lifecycle {
		prevent_destroy = false
		ignore_changes = [created_at, updated_at]
	}
}

resource "digitalocean_domain" "example" {
	name = "myapp.example.com"
}

resource "digitalocean_record" "app" {
	domain = digitalocean_domain.example.name
	name = ""
	type = "CNAME"
	value = digitalocean_app.example.app_uri
	
	// Add lifecycle policy to clean up when app is destroyed
	lifecycle {
		create_before_destroy = true
	}
}

# Custom script to monitor and clean dangling DNS

#!/bin/bash

# Monitor DigitalOcean App Platform for dangling DNS
monitor_app_platform() {
	# Get list of all apps
	curl -s -X GET "https://api.digitalocean.com/v2/apps" \
		-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" > apps.json
	
	# Check each app's DNS status
	for app in $(jq -r '.[].id' apps.json); do
		echo "Checking app: $app"
		
		# Get app details
		curl -s -X GET "https://api.digitalocean.com/v2/apps/$app" \
			-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" > app_details.json
		done
}

# Automated cleanup script
cleanup_dangling() {
	# Check for apps that were recently deleted
	# This would be part of a larger monitoring system
	find /var/log/digitalocean -name "app_deleted_*.log" -mtime -1 | while read log; do
		echo "Processing deleted app from: $log"
		# Extract app information and clean up DNS
	done
}

middleBrick Integration for Continuous Monitoring

Integrate middleBrick into your DigitalOcean workflow for ongoing dangling DNS detection:

# GitHub Action for DigitalOcean API security
name: DigitalOcean Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Run middleBrick scan
      run: |
        npm install -g middlebrick
        middlebrick scan myapp-nyc.ondigitalocean.app \
          --service digitalocean \
          --output json > security-report.json
    
    - name: Check for dangling DNS
      run: |
        # Parse middleBrick report for DNS issues
        ISSUES=$(jq '.findings[] | select(.category == "DNS" and .severity == "high")' security-report.json)
        if [ -n "$ISSUES" ]; then
          echo "Dangling DNS detected!"
          exit 1
        fi
    
    - name: Upload report
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: security-report
        path: security-report.json

This integration ensures that any dangling DNS issues in your DigitalOcean environment are caught early in the development lifecycle, preventing production security incidents.

Frequently Asked Questions

How can I tell if my DigitalOcean Load Balancer has dangling DNS records?
Check your DigitalOcean account's Networking > Domains section for A or CNAME records pointing to your Load Balancer's IP or ID. You can also use middleBrick's black-box scanning to detect if DNS records point to resources that no longer exist or are accessible to others.
Does DigitalOcean automatically clean up DNS records when I delete an App Platform application?
No, DigitalOcean does not automatically clean up DNS records when you delete an App Platform application. The platform assigns random subdomains (app-name.app-name-.ondigitalocean.app) that may be reassigned to other users. You must manually delete any custom DNS records pointing to deleted resources.