gcp-clouddns-zone-network-binding-update

A principal holding dns.managedZones.update at the GCP project scope can modify the privateVisibilityConfig.networks list for a private managed zone, expanding which VPC networks can resolve the zone. This enables widening the blast radius of a private-zone DNS poisoning attack by adding attacker-controlled VPC networks to the zone's visibility scope (cross-project lateral movement via shared VPC).

derived gcp emits CanModifyConfiguration

match (effective permission)

{ "action": "dns.managedZones.update", "resource_scope": "projects/{project}" }

where

Effective permission includes dns.managedZones.update at the project (or ancestor folder/org) level The target managed zone has visibility == 'private' The binding is not blocked by an IAM deny policy or org policy constraint that restricts dns.managedZones.update

emit

source typeIdentity
target typeDNS
source<principal with effective dns.managedZones.update>
target<private Cloud DNS managed zone in the project>
permissionsdns.managedZones.update
state logicACTIVE when the principal's effective IAM policy at the project (or ancestor) scope includes dns.managedZones.update and no IAM deny policy or org policy constraint blocks it at any level. dns.managedZones.update is included in roles/dns.admin and roles/editor; emit for each such binding resolved to the project scope. CONDITIONAL(iam_permission) when an IAM condition expression is attached to the binding. BLOCKED when an IAM deny policy (iam.googleapis.com/deny) covers dns.managedZones.update for this principal at any scope. Only emit for PRIVATE zones; public zones cannot have their visibility scope modified (visibility is immutable once created).

Narrative

{principal.name} holds dns.managedZones.update at project {project.name} and can modify the network visibility configuration for private managed zone {zone.name}. This allows expanding or narrowing which VPC networks can resolve the zone by modifying privateVisibilityConfig.networks[]. An attacker can use this to inject the zone into attacker-controlled shared VPC networks or cross-project VPCs, extending the blast radius of any DNS record poisoning to additional VPCs. Combined with dns.changes.create, this enables full DNS hijack of services across network boundaries.

Raw rule rules/derived/gcp/clouddns.yaml

id: gcp-clouddns-zone-network-binding-update
emits: CanModifyConfiguration
description: A principal holding dns.managedZones.update at the GCP project scope can modify the privateVisibilityConfig.networks
  list for a private managed zone, expanding which VPC networks can resolve the zone. This enables widening
  the blast radius of a private-zone DNS poisoning attack by adding attacker-controlled VPC networks to
  the zone's visibility scope (cross-project lateral movement via shared VPC).
match_effective_permission:
  action: dns.managedZones.update
  resource_scope: projects/{project}
where:
- Effective permission includes dns.managedZones.update at the project (or ancestor folder/org) level
- The target managed zone has visibility == 'private'
- The binding is not blocked by an IAM deny policy or org policy constraint that restricts dns.managedZones.update
emit:
  source_type: Identity
  target_type: DNS
  source: <principal with effective dns.managedZones.update>
  target: <private Cloud DNS managed zone in the project>
  permissions:
  - dns.managedZones.update
  state_logic: ACTIVE when the principal's effective IAM policy at the project (or ancestor) scope includes
    dns.managedZones.update and no IAM deny policy or org policy constraint blocks it at any level. dns.managedZones.update
    is included in roles/dns.admin and roles/editor; emit for each such binding resolved to the project
    scope. CONDITIONAL(iam_permission) when an IAM condition expression is attached to the binding. BLOCKED
    when an IAM deny policy (iam.googleapis.com/deny) covers dns.managedZones.update for this principal
    at any scope. Only emit for PRIVATE zones; public zones cannot have their visibility scope modified
    (visibility is immutable once created).
  derived_from:
  - '<effective IAM binding: principal -> dns.managedZones.update (or bundled role) at project scope>'
  - '<dns.managedZones.list: zone visibility=private>'
  false_positive_note: dns.managedZones.update is project-scoped; a single binding grants access to all
    private managed zones in the project. Emit one CanModifyConfiguration edge per (principal, zone) pair
    within the project. Do not emit for projects where the principal has the permission at a parent scope
    without verifying that the project is within that scope's hierarchy. Modifying privateVisibilityConfig
    affects DNS resolution reachability scope (which VPCs can resolve the zone), not record content. This
    is a distinct CanModifyConfiguration vector from dns.changes.create (record content mutation). Emit
    both edges if the principal holds both permissions.
  narrative: '{principal.name} holds dns.managedZones.update at project {project.name} and can modify
    the network visibility configuration for private managed zone {zone.name}. This allows expanding or
    narrowing which VPC networks can resolve the zone by modifying privateVisibilityConfig.networks[].
    An attacker can use this to inject the zone into attacker-controlled shared VPC networks or cross-project
    VPCs, extending the blast radius of any DNS record poisoning to additional VPCs. Combined with dns.changes.create,
    this enables full DNS hijack of services across network boundaries.'
move · open · esc close