gcp-cloudsql-reset-password-read-data

A principal that can reset the root/admin DB user password on a Cloud SQL instance gains full SQL read access to all databases in that instance.

derived gcp emits CanReadData

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanResetCredential {'db_user': None}

where

node_type(?db_user) == GenericIdentity # Cloud SQL root/admin DB user (internal engine identity) ?db_user is a root or admin-level built-in DB user of a Cloud SQL instance (?instance)

emit

source typeIdentity
target typeData
source?principal
target?instance
permissionscloudsql.users.update
conditionsiam_permission network_reachability
state logicinherit_weakest(CanResetCredential): if CanResetCredential is ACTIVE and a network path exists to the instance (public IP or Auth Proxy via cloudsql.instances.connect), this is ACTIVE; if the instance is only reachable via private IP and no Auth Proxy path is modeled, downgrade to CONDITIONAL(network_reachability); if CanResetCredential is CONDITIONAL or BLOCKED, inherit that state. The attacker must connect AFTER resetting the password; the two steps must be feasible (not blocked by network isolation).

Narrative

{principal.name} can reset the root password of {instance.name} (cloudsql.users.update) and then authenticate as root to read all databases in the instance.

Raw rule rules/derived/gcp/cloudsql.yaml

id: gcp-cloudsql-reset-password-read-data
emits: CanReadData
description: A principal that can reset the root/admin DB user password on a Cloud SQL instance gains
  full SQL read access to all databases in that instance.
match:
- - principal: null
  - CanResetCredential
  - db_user: null
where:
- 'node_type(?db_user) == GenericIdentity  # Cloud SQL root/admin DB user (internal engine identity)'
- ?db_user is a root or admin-level built-in DB user of a Cloud SQL instance (?instance)
emit:
  source_type: Identity
  target_type: Data
  source: ?principal
  target: ?instance
  permissions:
  - cloudsql.users.update
  conditions:
  - iam_permission
  - network_reachability
  state_logic: 'inherit_weakest(CanResetCredential): if CanResetCredential is ACTIVE and a network path
    exists to the instance (public IP or Auth Proxy via cloudsql.instances.connect), this is ACTIVE; if
    the instance is only reachable via private IP and no Auth Proxy path is modeled, downgrade to CONDITIONAL(network_reachability);
    if CanResetCredential is CONDITIONAL or BLOCKED, inherit that state. The attacker must connect AFTER
    resetting the password; the two steps must be feasible (not blocked by network isolation).'
  false_positive_note: "This chain (reset + connect) requires TWO conditions: (1) cloudsql.users.update\
    \ effective on the instance, AND (2) a network path to connect to the DB (public IP reachable OR Cloud\
    \ SQL Auth Proxy available, which requires cloudsql.instances.connect). If the instance is private-IP-only\
    \ and the attacker has no VPC foothold, downgrade to CONDITIONAL(network_reachability). Do not emit\
    \ ACTIVE when the instance state is not RUNNABLE. Only chain to full CanReadData when the targeted\
    \ user is root/admin with all-database access; partial user resets yield partial access \u2014 lower\
    \ confidence (0.7)."
  narrative: '{principal.name} can reset the root password of {instance.name} (cloudsql.users.update)
    and then authenticate as root to read all databases in the instance.'
  derived_from:
  - ?principal CanResetCredential ?db_user
  - ?db_user is root user of ?instance
move · open · esc close