gcp-cloudsql-export-exfiltrate

cloudsql.instances.export triggers the Cloud SQL service agent to write a full SQL dump (or CSV export) to a GCS bucket; when the destination bucket is attacker-accessible, this constitutes data exfiltration.

derived gcp emits CanExfiltrate

match (effective permission)

{ "action": "cloudsql.instances.export", "resource_type": "google.sql.Instance" }

where

the Cloud SQL service agent (service-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com) holds storage.objects.create on a destination GCS bucket the attacker can read or control

emit

source typeIdentity
target typeData
source<principal>
target<Cloud SQL Instance node>
permissionscloudsql.instances.export
conditionsiam_permission resource_policy
state logicACTIVE when cloudsql.instances.export is confirmed effective AND the Cloud SQL service agent (service-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com) holds storage.objects.create on a same-project GCS bucket (verified from service agent's IAM grants within this project) AND the attacker can read that bucket. CONDITIONAL(resource_policy) for cross-project destination buckets where the service agent's cross-project grant cannot be confirmed from the analyzed project's IAM. CONDITIONAL(iam_permission) when the service agent's GCS grant cannot be confirmed. BLOCKED when VPC-SC perimeter blocks sqladmin.googleapis.com.

Narrative

{principal.name} holds cloudsql.instances.export on {instance.name} and can trigger a full SQL dump to a GCS bucket where the Cloud SQL service agent has write permission; the dump is exfiltrated and readable by the attacker.

Raw rule rules/derived/gcp/cloudsql.yaml

id: gcp-cloudsql-export-exfiltrate
emits: CanExfiltrate
description: cloudsql.instances.export triggers the Cloud SQL service agent to write a full SQL dump (or
  CSV export) to a GCS bucket; when the destination bucket is attacker-accessible, this constitutes data
  exfiltration.
match_effective_permission:
  action: cloudsql.instances.export
  resource_type: google.sql.Instance
where:
- the Cloud SQL service agent (service-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com) holds
  storage.objects.create on a destination GCS bucket the attacker can read or control
emit:
  source_type: Identity
  target_type: Data
  source: <principal>
  target: <Cloud SQL Instance node>
  permissions:
  - cloudsql.instances.export
  conditions:
  - iam_permission
  - resource_policy
  state_logic: ACTIVE when cloudsql.instances.export is confirmed effective AND the Cloud SQL service
    agent (service-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com) holds storage.objects.create
    on a same-project GCS bucket (verified from service agent's IAM grants within this project) AND the
    attacker can read that bucket. CONDITIONAL(resource_policy) for cross-project destination buckets
    where the service agent's cross-project grant cannot be confirmed from the analyzed project's IAM.
    CONDITIONAL(iam_permission) when the service agent's GCS grant cannot be confirmed. BLOCKED when VPC-SC
    perimeter blocks sqladmin.googleapis.com.
  false_positive_note: "The export is performed SERVER-SIDE by the Cloud SQL service agent (service-PROJECT_NUMBER@gcp-sa-cloud-sql.iam.gserviceaccount.com),\
    \ NOT the calling principal directly. The calling principal needs cloudsql.instances.export to trigger\
    \ the export; the service agent needs storage.objects.create on the DESTINATION bucket. If the service\
    \ agent lacks that grant, export fails with 403.\nFor SAME-PROJECT exfiltration (ACTIVE case): the\
    \ destination is a bucket in the same project. The attacker with cloudsql.instances.export can trigger\
    \ the export to that bucket. If the attacker also holds IAM grants on that bucket (e.g. storage.buckets.get,\
    \ storage.objects.get), they can read the exported dump directly. ACTIVE when both conditions hold:\
    \ (1) service agent has storage.objects.create on bucket, (2) attacker can read bucket.\nFor CROSS-PROJECT\
    \ exfiltration (CONDITIONAL case): the destination bucket is in a foreign project. The attacker cannot\
    \ see the foreign project's IAM policies from the source project's analysis. The service agent's cross-project\
    \ grant must be pre-established by the foreign bucket owner \u2014 the attacker CANNOT retroactively\
    \ grant storage.objects.create to the service agent in a bucket they do not already control (granting\
    \ the service agent requires knowledge of its identity and write permission on the bucket). Thus,\
    \ cross-project export is only realistic if the attacker has PRE-PROVISIONED the service agent's write\
    \ grant on the foreign bucket BEFORE the attack. This requires out-of-band pre-configuration. Flag\
    \ this case as CONDITIONAL(resource_policy) with a note that it assumes pre-granted cross-project\
    \ access.\nFor ATTACKER-OWNED BUCKETS: if the destination is an attacker-controlled bucket in a different\
    \ project, the attacker must have PRE-GRANTED the source project's Cloud SQL service agent write access\
    \ to that bucket. This is a realistic path only if the attacker set it up in advance (e.g., owns both\
    \ projects or was granted cross-project permissions beforehand).\nThe export produces a full schema+data\
    \ SQL dump or CSV; all table data is included unless filtered by the export request's database/table\
    \ spec."
  narrative: '{principal.name} holds cloudsql.instances.export on {instance.name} and can trigger a full
    SQL dump to a GCS bucket where the Cloud SQL service agent has write permission; the dump is exfiltrated
    and readable by the attacker.'
  derived_from:
  - cloudsql.instances.export effective permission on google.sql.Instance
  - Cloud SQL service agent storage.objects.create on destination GCS bucket
move · open · esc close