gcp-datastream-stream-create-exfil

datastream.streams.create + datastream.connectionProfiles.create lets a principal create a new CDC stream from an existing source database to an attacker-controlled destination, exfiltrating source data.

derived gcp emits CanExfiltrate

match (effective permission)

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

where

principal also has effective datastream.connectionProfiles.create (to create a destination profile) OR can reference an existing attacker-controlled connection profile Datastream can reach the source database (via private connection, forward SSH tunnel, or static IP allowlist on the source)

emit

source typeIdentity
target typeData
source<principal>
target<source database data accessible via existing connection profiles>
permissionsdatastream.streams.create datastream.connectionProfiles.create
conditionsiam_permission network_reachability api_enabled
state logicACTIVE if both permissions are confirmed AND network_reachability to the source DB via Datastream is established (an existing connection profile for the source DB can be referenced, or the source DB is reachable from Datastream's public IPs). CONDITIONAL(network_reachability) if source reachability is unconfirmed. CONDITIONAL(iam_permission) if connectionProfiles.create is missing (must use an existing source connection profile the principal can reference). BLOCKED if a deny policy or VPC Service Controls perimeter blocks either permission.

Narrative

{principal.name} can create a new Datastream stream (datastream.streams.create) with a new destination connection profile pointing to an attacker-controlled BigQuery dataset or GCS path, replicating data from an accessible source database to that destination. A full backfill exfiltrates the entire source database contents.

Raw rule rules/derived/gcp/datastream.yaml

id: gcp-datastream-stream-create-exfil
emits: CanExfiltrate
description: datastream.streams.create + datastream.connectionProfiles.create lets a principal create
  a new CDC stream from an existing source database to an attacker-controlled destination, exfiltrating
  source data.
match_effective_permission:
  action: datastream.streams.create
  resource_type: google.datastream.Stream
where:
- principal also has effective datastream.connectionProfiles.create (to create a destination profile)
  OR can reference an existing attacker-controlled connection profile
- Datastream can reach the source database (via private connection, forward SSH tunnel, or static IP allowlist
  on the source)
emit:
  source_type: Identity
  target_type: Data
  source: <principal>
  target: <source database data accessible via existing connection profiles>
  permissions:
  - datastream.streams.create
  - datastream.connectionProfiles.create
  conditions:
  - iam_permission
  - network_reachability
  - api_enabled
  state_logic: ACTIVE if both permissions are confirmed AND network_reachability to the source DB via
    Datastream is established (an existing connection profile for the source DB can be referenced, or
    the source DB is reachable from Datastream's public IPs). CONDITIONAL(network_reachability) if source
    reachability is unconfirmed. CONDITIONAL(iam_permission) if connectionProfiles.create is missing (must
    use an existing source connection profile the principal can reference). BLOCKED if a deny policy or
    VPC Service Controls perimeter blocks either permission.
  confidence: 0.85
  derived_from:
  - <datastream.streams.create effective permission>
  - <datastream.connectionProfiles.create effective permission (if present)>
  false_positive_note: Creating a stream requires a SOURCE connection profile (the database the stream
    reads from). If the principal cannot create a new source profile AND cannot reference an existing
    one that points to a reachable database, no new exfil stream can be established. Do NOT emit ACTIVE
    if there are no reachable source connection profiles. Also requires datastream.streams.start to actually
    begin replication; if the principal lacks streams.start, the stream is created but paused (CONDITIONAL).
    This is a slightly weaker path than streams.update (which hijacks an existing stream's output); prefer
    streams.update rules for running streams.
  narrative: '{principal.name} can create a new Datastream stream (datastream.streams.create) with a new
    destination connection profile pointing to an attacker-controlled BigQuery dataset or GCS path, replicating
    data from an accessible source database to that destination. A full backfill exfiltrates the entire
    source database contents.'
move · open · esc close