gcp-firestore-export-exfiltrate
datastore.databases.export + storage.objects.create on a destination GCS bucket constitutes effective Firestore data exfiltration: the entire database is copied to an attacker-controlled or cross-project GCS bucket.
match (effective permission)
{
"action": "datastore.databases.export",
"resource_type": "google.firestore.Database"
}
where
?principal ALSO has EFFECTIVE storage.objects.create on a destination GCS bucket in a different project or attacker-controlled bucket
emit
| source type | Identity |
|---|---|
| target type | Data |
| source | <principal> |
| target | <Firestore Database / NoSQLDatabase node> |
| permissions | datastore.databases.export storage.objects.create |
| conditions | iam_permission resource_policy |
| state logic | ACTIVE when datastore.databases.export is confirmed effective on the Firestore Database AND storage.objects.create on a writable destination bucket (ideally attacker-controlled or cross-project) is confirmed; CONDITIONAL(iam_permission) when destination bucket write rights are uncertain or the only confirmed destination is a project-internal bucket; POTENTIAL when no confirmed destination write path is modeled. CMEK state inherits from the underlying CanReadData: CONDITIONAL(key_permission) if the database is CMEK and service- agent grant is unconfirmed. |
Narrative
{principal.name} holds datastore.databases.export on {database.name} and can export the entire Firestore database to a GCS bucket it controls (storage.objects.create), exfiltrating all documents outside the project boundary. The export format is Firestore-native SST files; full data recovery is possible via Firestore import.
Raw rule rules/derived/gcp/firestore.yaml
id: gcp-firestore-export-exfiltrate
emits: CanExfiltrate
description: 'datastore.databases.export + storage.objects.create on a destination GCS bucket constitutes
effective Firestore data exfiltration: the entire database is copied to an attacker-controlled or cross-project
GCS bucket.'
match_effective_permission:
action: datastore.databases.export
resource_type: google.firestore.Database
where:
- ?principal ALSO has EFFECTIVE storage.objects.create on a destination GCS bucket in a different project
or attacker-controlled bucket
emit:
source_type: Identity
target_type: Data
source: <principal>
target: <Firestore Database / NoSQLDatabase node>
permissions:
- datastore.databases.export
- storage.objects.create
conditions:
- iam_permission
- resource_policy
state_logic: 'ACTIVE when datastore.databases.export is confirmed effective on the Firestore Database
AND storage.objects.create on a writable destination bucket (ideally attacker-controlled or cross-project)
is confirmed; CONDITIONAL(iam_permission) when destination bucket write rights are uncertain or the
only confirmed destination is a project-internal bucket; POTENTIAL when no confirmed destination write
path is modeled. CMEK state inherits from the underlying CanReadData: CONDITIONAL(key_permission)
if the database is CMEK and service- agent grant is unconfirmed.'
confidence: min(contributing_confidences) * 0.91
derived_from:
- datastore.databases.export effective permission on Firestore Database
- storage.objects.create on destination GCS bucket
false_positive_note: "The exported data is in Firestore's proprietary export format (a collection of\
\ LevelDB SST files with a metadata file). Raw SST files require re-import into another Firestore\
\ instance or offline parsing tools to read; they are not human-readable JSON. The exfiltration is\
\ still effective (data leaves the project boundary and can be fully recovered by an attacker), but\
\ the parsing step should be noted. Exporting to a bucket in the SAME project does NOT constitute\
\ cross-boundary exfiltration \u2014 emit POTENTIAL(iam_permission) in that case unless the bucket\
\ itself is attacker-controlled. VPC-SC perimeters co-perimeterizing Firestore and the destination\
\ GCS bucket may block the cross-project export -> BLOCKED(resource_policy). datastore.databases.export\
\ is included in roles/datastore.importExportAdmin and roles/datastore.owner. Verify that export targets\
\ the correct Database resource node, not just the project."
narrative: '{principal.name} holds datastore.databases.export on {database.name} and can export the
entire Firestore database to a GCS bucket it controls (storage.objects.create), exfiltrating all documents
outside the project boundary. The export format is Firestore-native SST files; full data recovery
is possible via Firestore import.'