gcp-datastream-stream-update-exfil

datastream.streams.update lets a principal redirect the stream's destination (BigQuery dataset or GCS path) to an attacker-controlled sink, exfiltrating all CDC data from the source database.

derived gcp emits CanExfiltrate

match (effective permission)

{ "action": "datastream.streams.update", "resource_type": "google.datastream.Stream" }

emit

source typeIdentity
target typeStorage
source<principal>
target<attacker-controlled destination: ObjectStorage (GCS bucket) or DataWarehouse (BigQuery dataset)>
permissionsdatastream.streams.update
conditionsiam_permission api_enabled
state logicACTIVE if datastream.streams.update is confirmed in the effective grant and the principal can also create a destination connection profile pointing to an attacker-controlled sink (requires datastream.connectionProfiles.create or access to an existing attacker-controlled profile). If the principal can only update the stream (not create new profiles), the attack is CONDITIONAL on an existing attacker-controlled connection profile being available. BLOCKED if a deny policy applies to datastream.streams.update or if a VPC Service Controls perimeter blocks the API call. Note: stopping and restarting the stream (streams.start/stop) triggers a backfill — a FULL copy of the source database to the new destination.

Narrative

{principal.name} can update the Datastream stream {stream.name} (datastream.streams.update) to replace its destination configuration with an attacker-controlled BigQuery dataset or GCS bucket; ongoing CDC data and any triggered backfill (full historical copy of {stream.name}'s source tables) will be written to the attacker-controlled destination.

Raw rule rules/derived/gcp/datastream.yaml

id: gcp-datastream-stream-update-exfil
emits: CanExfiltrate
description: datastream.streams.update lets a principal redirect the stream's destination (BigQuery dataset
  or GCS path) to an attacker-controlled sink, exfiltrating all CDC data from the source database.
match_effective_permission:
  action: datastream.streams.update
  resource_type: google.datastream.Stream
emit:
  source_type: Identity
  target_type: Storage
  source: <principal>
  target: '<attacker-controlled destination: ObjectStorage (GCS bucket) or DataWarehouse (BigQuery dataset)>'
  permissions:
  - datastream.streams.update
  conditions:
  - iam_permission
  - api_enabled
  state_logic: "ACTIVE if datastream.streams.update is confirmed in the effective grant and the principal\
    \ can also create a destination connection profile pointing to an attacker-controlled sink (requires\
    \ datastream.connectionProfiles.create or access to an existing attacker-controlled profile). If the\
    \ principal can only update the stream (not create new profiles), the attack is CONDITIONAL on an\
    \ existing attacker-controlled connection profile being available. BLOCKED if a deny policy applies\
    \ to datastream.streams.update or if a VPC Service Controls perimeter blocks the API call. Note: stopping\
    \ and restarting the stream (streams.start/stop) triggers a backfill \u2014 a FULL copy of the source\
    \ database to the new destination."
  confidence: 0.9
  derived_from:
  - <datastream.streams.update effective permission on principal>
  false_positive_note: "The exfiltration requires: (1) the stream is RUNNING or can be started (datastream.streams.start);\
    \ (2) the new destination connection profile is reachable from Datastream's service network; (3) the\
    \ principal has write access to create or reference the attacker-controlled destination connection\
    \ profile. Do NOT emit ACTIVE for a paused / stopped stream unless the principal also has datastream.streams.start.\
    \ A VPC Service Controls perimeter restricting datastream.googleapis.com can block this path (emit\
    \ BLOCKED with the perimeter as denied_by). Redirecting the stream does not provide the SOURCE database\
    \ credentials \u2014 only the data flowing through the stream. The target_type is Storage because\
    \ the actual exfiltration sink is an attacker-controlled GCS bucket or BigQuery dataset (both Storage\
    \ class), not the stream's source database."
  narrative: '{principal.name} can update the Datastream stream {stream.name} (datastream.streams.update)
    to replace its destination configuration with an attacker-controlled BigQuery dataset or GCS bucket;
    ongoing CDC data and any triggered backfill (full historical copy of {stream.name}''s source tables)
    will be written to the attacker-controlled destination.'
move · open · esc close