gcp-bigquery-exfiltrate

An EXPORT DATA statement or bq extract job copies BigQuery table contents to a GCS bucket - effective data exfiltration across the project boundary.

derived gcp emits CanExfiltrate

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanReadData {'dataset': None}

where

?principal has EFFECTIVE bigquery.tables.export on the dataset/tables (required for EXPORT DATA / bq extract) ?principal has EFFECTIVE storage.objects.create on a destination GCS bucket (attacker-controlled or accessible)

emit

source typeIdentity
target typeData
source?principal
target?dataset
permissionsbigquery.tables.getData bigquery.jobs.create bigquery.tables.export storage.objects.create
conditionsiam_permission resource_policy
state logicACTIVE when CanReadData(?principal, ?dataset) is ACTIVE AND bigquery.tables.export is confirmed ACTIVE AND storage.objects.create on a writable destination bucket is confirmed; CONDITIONAL(iam_permission) when bigquery.tables.export is uncertain or bucket write rights are uncertain; BLOCKED when tables.export is explicitly denied.

Narrative

{principal.name} can submit an EXPORT DATA job to copy tables from {dataset.name} to a GCS bucket it controls (bigquery.tables.getData + bigquery.jobs.create + bigquery.tables.export + storage.objects.create), exfiltrating data outside the project boundary.

Raw rule rules/derived/gcp/bigquery.yaml

id: gcp-bigquery-exfiltrate
emits: CanExfiltrate
description: "An EXPORT DATA statement or bq extract job copies BigQuery table contents to a GCS bucket\
  \ \u2014 effective data exfiltration across the project boundary."
match:
- - principal: null
  - CanReadData
  - dataset: null
where:
- ?principal has EFFECTIVE bigquery.tables.export on the dataset/tables (required for EXPORT DATA / bq
  extract)
- ?principal has EFFECTIVE storage.objects.create on a destination GCS bucket (attacker-controlled or
  accessible)
emit:
  source_type: Identity
  target_type: Data
  source: ?principal
  target: ?dataset
  permissions:
  - bigquery.tables.getData
  - bigquery.jobs.create
  - bigquery.tables.export
  - storage.objects.create
  conditions:
  - iam_permission
  - resource_policy
  state_logic: ACTIVE when CanReadData(?principal, ?dataset) is ACTIVE AND bigquery.tables.export is confirmed
    ACTIVE AND storage.objects.create on a writable destination bucket is confirmed; CONDITIONAL(iam_permission)
    when bigquery.tables.export is uncertain or bucket write rights are uncertain; BLOCKED when tables.export
    is explicitly denied.
  false_positive_note: "EXPORT DATA / bq extract requires THREE permissions on the principal's identity:\
    \ (1) bigquery.tables.getData (read the table data), (2) bigquery.jobs.create (submit the export job),\
    \ (3) bigquery.tables.export (execute EXPORT DATA / extract \u2014 NOT included in roles/bigquery.dataViewer,\
    \ only in roles/bigquery.dataEditor and higher). Additionally, storage.objects.create is required\
    \ on the DESTINATION bucket. A principal with roles/bigquery.dataViewer + bigquery.jobUser satisfies\
    \ CanReadData but CANNOT export without tables.export. Without bucket write rights the export fails\
    \ (CanExfiltrate is CONDITIONAL(iam_permission) on that right). The destination can be any GCS bucket\
    \ the principal can write to \u2014 including buckets in other projects or owned by the attacker.\
    \ VPC-SC perimeters can restrict cross-perimeter GCS writes (bigquery + GCS must be co-perimeterized\
    \ or the export blocked). Authorized views do NOT grant EXPORT capability \u2014 a view reader cannot\
    \ export the underlying base table unless they also hold getData + tables.export on it. Only emit\
    \ when the source node is a DataWarehouse or its containing dataset."
  narrative: '{principal.name} can submit an EXPORT DATA job to copy tables from {dataset.name} to a GCS
    bucket it controls (bigquery.tables.getData + bigquery.jobs.create + bigquery.tables.export + storage.objects.create),
    exfiltrating data outside the project boundary.'
  derived_from:
  - ?principal CanReadData ?dataset
  - bigquery.tables.export effective permission
  - storage.objects.create on destination bucket
move · open · esc close