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.
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. |
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