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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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). |
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