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.

derived gcp emits CanExfiltrate

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 typeIdentity
target typeData
source<principal>
target<Firestore Database / NoSQLDatabase node>
permissionsdatastore.databases.export storage.objects.create
conditionsiam_permission resource_policy
state logicACTIVE 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.'
move · open · esc close