A virtual private cloud (VPC) can use DNS resources from another account without owning them. The source account retains control of their records or forwarding destinations, so an attacker controlling that account can change the target’s private DNS answers without another target-side configuration change. Workloads can then receive a false destination or fail to resolve a service at all. DNS control alone does not bypass TLS validation or application authentication.

Three association types

flowchart LR
    A([Source account]) --> P[Route 53 Profile]
    A --> F[Shared Resolver rule]
    A --> Z[Private hosted zone]
    P --> V[Target VPC association]
    F --> V
    Z --> V
    V --> R[VPC Resolver]
    R --> D([Source-controlled DNS result])
    class A,D principal
    class P,F,Z,V,R awsResource
Three cross-account association types leave the source account in control of DNS data consumed by the target VPC.
Implementation Target-side action Source-controlled state Association containment action
Route 53 Profile AssociateProfile Profile resources and private-zone records DisassociateProfile
Shared Resolver rule AssociateResolverRule Forwarding rule, outbound endpoint, and target IPs DisassociateResolverRule
Direct private hosted zone AssociateVPCWithHostedZone Hosted-zone records DisassociateVPCFromHostedZone, subject to last-VPC and service-owned-zone restrictions.[1]

Route 53 Profile associations

A Route 53 Profile packages private hosted zones, Resolver rules, and other DNS settings for association with VPCs.[2][3]

Run the Route 53 Profile proof of concept

This example assumes the source account owns a private hosted zone and a Profile. The target accepts the RAM share when the accounts are not using automatic acceptance through AWS Organizations.

aws route53profiles associate-resource-to-profile \
  --name shared-private-zone \
  --profile-id "$PROFILE_ID" \
  --resource-arn "arn:aws:route53:::hostedzone/$ZONE_ID"

aws ram create-resource-share \
  --name shared-dns-profile \
  --resource-arns "$PROFILE_ARN" \
  --principals "$TARGET_ACCOUNT_ID"

AWS_PROFILE=target aws route53profiles associate-profile \
  --name shared-dns-profile \
  --profile-id "$PROFILE_ID" \
  --resource-id "$TARGET_VPC_ID"

aws route53 change-resource-record-sets \
  --hosted-zone-id "$ZONE_ID" \
  --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"packages.corp.example","Type":"A","TTL":60,"ResourceRecords":[{"Value":"192.0.2.40"}]}}]}'

The final command requests a source-account record change without another AssociateProfile call. Propagation, resolution precedence, and caching determine when a client can receive the changed answer; the accepted request alone does not prove that happened.[2:1][4]

Shared Resolver forwarding rules

A shared Resolver rule provides a narrower form of the same control by forwarding one domain through an outbound Resolver endpoint. After the rule owner shares it through RAM and another account associates it with a VPC, UpdateResolverRule can replace the outbound endpoint or target IPs.[5][6]

A central networking account shares a Resolver rule for corp.example with an application account through AWS Resource Access Manager (RAM), and the application account associates it with a production VPC so workloads can resolve internal services through the networking account’s outbound Resolver endpoint.

After gaining a networking-account role with UpdateResolverRule, the attacker changes the rule’s target IP address to a DNS server under their control. The configuration change can redirect matching production queries without a new API call in the application account; whether clients receive or use the changed answers requires separate evidence.

Run the shared Resolver rule proof of concept

Save the later source-side update as resolver-update.json:

{
  "ResolverRuleId": "<SHARED_RULE_ID>",
  "Config": {
    "Name": "shared-corp-forwarder",
    "ResolverEndpointId": "<SOURCE_OUTBOUND_ENDPOINT_ID>",
    "TargetIps": [
      {
        "Ip": "192.0.2.53",
        "Port": 53
      }
    ]
  }
}

Share and associate the existing rule, then update its forwarding target from the source account:

aws ram create-resource-share \
  --name shared-resolver-rule \
  --resource-arns "$RESOLVER_RULE_ARN" \
  --principals "$TARGET_ACCOUNT_ID"

AWS_PROFILE=target aws route53resolver associate-resolver-rule \
  --name shared-corp-forwarder \
  --resolver-rule-id "$SHARED_RULE_ID" \
  --vpc-id "$TARGET_VPC_ID"

aws route53resolver update-resolver-rule \
  --cli-input-json file://resolver-update.json

A rule status of COMPLETE indicates configuration completion, not that a workload sent a query or used an answer. Matching queries depend on resolution precedence, caching, and network reachability from the outbound endpoint to the DNS server.[7][8]

Direct private hosted-zone associations

By contrast, a direct private hosted-zone association uses a one-time authorization: the zone owner authorizes a specific VPC, and the VPC owner then performs the association. Deleting the authorization afterward does not remove the active association.[9]

Run the private hosted-zone proof of concept

The source account authorizes the target VPC, the target account associates it, and the source later changes a private record:

aws route53 create-vpc-association-authorization \
  --hosted-zone-id "$ZONE_ID" \
  --vpc "VPCRegion=$AWS_REGION,VPCId=$TARGET_VPC_ID"

AWS_PROFILE=target aws route53 associate-vpc-with-hosted-zone \
  --hosted-zone-id "$ZONE_ID" \
  --vpc "VPCRegion=$AWS_REGION,VPCId=$TARGET_VPC_ID"

aws route53 delete-vpc-association-authorization \
  --hosted-zone-id "$ZONE_ID" \
  --vpc "VPCRegion=$AWS_REGION,VPCId=$TARGET_VPC_ID"

aws route53 change-resource-record-sets \
  --hosted-zone-id "$ZONE_ID" \
  --change-batch '{"Changes":[{"Action":"UPSERT","ResourceRecordSet":{"Name":"api.corp.example","Type":"A","TTL":60,"ResourceRecords":[{"Value":"192.0.2.80"}]}}]}'

Removing the authorization prevents a future reassociation without new authorization, but does not remove the existing association. Either the hosted-zone owner or the VPC owner can request DisassociateVPCFromHostedZone. The API cannot remove a zone’s last VPC association or disassociate a zone owned by an AWS service, so a submitted request is not an unconditional containment guarantee.[9:1][1:1]

Resolution precedence and limits

These routes affect clients whose resolution path uses the associated VPC’s Resolver configuration, including hybrid clients querying through an inbound Resolver endpoint, not only workloads inside the VPC. Public authoritative DNS remains unchanged. Private hosted-zone resolution also depends on enableDnsSupport and enableDnsHostnames being enabled on the VPC.[10][11]

Once these associations are present, Route 53 applies its normal precedence rules: Resolver forwarding rules take priority over private hosted zones for the same domain, while the most specific domain wins among overlapping private zones. If the matching private zone lacks the requested record, Route 53 returns NXDOMAIN (non-existent domain) instead of falling back to public DNS.[11:1]

Profiles introduce one additional rule: when a directly associated private hosted zone and a Profile-provided zone use the same domain, the directly associated zone wins.[12]

Application-layer controls still constrain DNS manipulation because Transport Layer Security (TLS) validation, mutual TLS, signed artifacts, and application credentials can block an impersonated endpoint. However, plaintext protocols and clients that trust only the hostname remain exposed to redirection, while false or missing answers can still deny access.

Following source-side DNS changes

Detection needs both account views: the target account records association activity, while the source account records later changes to records, endpoints, or forwarding targets. Source-change correlation requires access to the DNS-owner account’s CloudTrail logs, with the relevant service and Region coverage. An organization trail covers its member accounts, not an unrelated external owner; a resource share does not establish this logging coverage. Without owner logs, consumer-side inventory and DNS observations provide partial evidence, not a complete change history or source-actor attribution.[13][14]

These management events identify association and configuration requests. Check errors and resulting service state before claiming completion; they are not DNS-query or application-activity evidence.[13:1]

Event source Event name Detection value
route53profiles.amazonaws.com AssociateProfile, AssociateResourceToProfile Expected API event names for VPC and Profile-resource association changes; corroborate with current inventory.[15][16]
route53resolver.amazonaws.com AssociateResolverRule, UpdateResolverRule Connects the target VPC to a shared rule and records later target changes.
route53.amazonaws.com CreateVPCAssociationAuthorization, AssociateVPCWithHostedZone, DisassociateVPCFromHostedZone Creates or removes a direct cross-account private-zone relationship.
ram.amazonaws.com CreateResourceShare, AcceptResourceShareInvitation Establishes Profile or Resolver rule sharing between accounts.
route53.amazonaws.com ChangeResourceRecordSets Changes a private-zone answer consumed through a Profile or direct association.[17]

Retain the source identity, hosted zone, name, type, value, and change status from each record change. The documented raw CloudTrail event uses tTL casing. A successful request with PENDING status establishes acceptance, not completed propagation; INSYNC establishes propagation to Route 53 DNS servers, not an observed client answer or application connection.[13:2][4:1]

{
  "eventSource": "route53.amazonaws.com",
  "eventName": "ChangeResourceRecordSets",
  "userIdentity": {
    "arn": "<source identity ARN>"
  },
  "requestParameters": {
    "hostedZoneId": "<private hosted zone ID>",
    "changeBatch": {
      "changes": [
        {
          "action": "UPSERT",
          "resourceRecordSet": {
            "name": "<private name>",
            "type": "A",
            "tTL": 60,
            "resourceRecords": [
              { "value": "<new address>" }
            ]
          }
        }
      ]
    }
  },
  "responseElements": {
    "changeInfo": { "status": "PENDING" }
  }
}

For each VPC, maintain current-state relationships to its Profile owner and resources, associated Resolver rule owner and targets, and directly associated private-zone owner. These relationships can persist for months, so a short correlation window is insufficient. Reconstruct changes by event time, tolerate late or out-of-order events, and retain the relationship through its association and sharing lifecycle rather than expiring it after an arbitrary quiet period.

When owner logs show an accepted record or rule update for a domain used by that VPC, check configuration status and available Resolver query logs for observed answers. Logging must have been enabled with usable retention. Resolver logs only unique queries, not queries answered from its cache, so missing entries do not prove no resolution or use. Query logs can also arrive late. Use workload or connection evidence separately before claiming application activity.[4:2][8:1][18]

Compare changes against approved DNS-owner accounts, Profile IDs, rule IDs, hosted-zone domains, outbound endpoints, forwarding target ranges, and record-management identities. Record type, time to live (TTL), new address range, query name, response code, and the source identity’s history with that resource provide context for review. Complete inventory also requires the Profiles resource-association APIs: ListHostedZonesByVPC excludes zones supplied through Profiles.[9:2]

Restricting external DNS ownership

  • Restrict association actions to approved automation, using each service’s actual authorization dimensions:
    • route53profiles:AssociateProfile supports route53profiles:ResourceIds to constrain VPC IDs but has no resource-level scope. Use approved sharing and automation governance for source-Profile selection; ResourceArns is not an AssociateProfile condition.[19]
    • route53resolver:AssociateResolverRule supports the resolver-rule resource type, but not a VPC condition key. Scope approved rules in IAM and govern VPC selection through approved automation.[20]
    • route53:AssociateVPCWithHostedZone supports hosted-zone resource scope and route53:VPCs conditions.[21]
  • Accept RAM shares only from designated networking or DNS-owner accounts.
  • Inventory the owner account, share, associated VPCs, domain names, forwarding targets, and attached resources for every external DNS relationship.
  • Alert on source-side changes to records, Resolver target IPs, or Profile resources used by production VPCs where owner-account logs are available. Track missing external-owner visibility as a detection limitation.
  • Enable Resolver query logging for VPCs where private answer changes or NXDOMAIN responses affect security, retaining enough history for the DNS relationship’s investigative needs and allowing for cache-hit omissions.[8:2]
  • Require application-layer authentication so control of DNS does not become trusted endpoint impersonation.
  • During containment, remove the VPC association with the applicable disassociation API and verify completed state, observing private-zone last-VPC and service-owned-zone restrictions. Removing a one-time hosted-zone authorization does not remove the active private-zone association.[9:3][1:2]
  • Distinguish RAM unsharing from one-time zone authorization removal: unsharing a Profile removes its configurations from associated VPCs, which revert to VPC-specific configuration; deleting or unsharing a Resolver rule has the DNS-processing effect of disassociation, with remaining rules taking over. Verify the resulting share and association graph, including overlapping local DNS configuration.[3:1][5:1]
  • Association removal does not purge answers already cached by resolvers or clients. Check post-containment answers and workload behavior, and extend investigation for cached-answer use or credible later activity rather than treating configuration removal as the end of exposure.[8:3]

References


  1. Amazon Route 53 API Reference, DisassociateVPCFromHostedZone. ↩︎ ↩︎ ↩︎

  2. Amazon Route 53 Developer Guide, What are Route 53 Profiles?. ↩︎ ↩︎

  3. Amazon Route 53 Developer Guide, Sharing Route 53 Profiles. ↩︎ ↩︎

  4. Amazon Route 53 API Reference, GetChange. ↩︎ ↩︎ ↩︎

  5. Amazon Route 53 Developer Guide, Sharing Resolver rules. ↩︎ ↩︎

  6. Amazon Route 53 API Reference, UpdateResolverRule. ↩︎

  7. Amazon Route 53 API Reference, ResolverRule. ↩︎

  8. Amazon Route 53 Developer Guide, Resolver query logging. ↩︎ ↩︎ ↩︎ ↩︎

  9. Amazon Route 53 Developer Guide, Associating a VPC and private hosted zone in different accounts. ↩︎ ↩︎ ↩︎ ↩︎

  10. Amazon Route 53 Developer Guide, Forwarding inbound DNS queries to your VPCs. ↩︎

  11. Amazon Route 53 Developer Guide, Private hosted-zone considerations. ↩︎ ↩︎

  12. Amazon Route 53 Developer Guide, Route 53 Profile priority. ↩︎

  13. Amazon Route 53 Developer Guide, Logging Amazon Route 53 API calls using AWS CloudTrail. ↩︎ ↩︎ ↩︎

  14. AWS CloudTrail User Guide, Creating a trail for an organization. ↩︎

  15. Amazon EventBridge User Guide, Amazon Route 53 Profiles events. ↩︎

  16. detection.wiki, Route 53 Profiles CloudTrail event catalog, AssociateProfile. ↩︎

  17. detection.wiki, Amazon Route 53 CloudTrail event catalog, ChangeResourceRecordSets. ↩︎

  18. Amazon Route 53 Developer Guide, Values that appear in Resolver query logs. ↩︎

  19. AWS Service Authorization Reference, Actions, resources, and condition keys for Amazon Route 53 Profiles. ↩︎

  20. AWS Service Authorization Reference, Actions, resources, and condition keys for Amazon Route 53 Resolver. ↩︎

  21. AWS Service Authorization Reference, Actions, resources, and condition keys for Amazon Route 53. ↩︎