aws-msk-replication-can-read-data

An MSK Replicator reading from a source cluster with kafka-cluster:ReadData permission enables cross-cluster/cross-account data exfiltration of message contents.

derived aws emits CanReadData

match

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

{'replicator': None} ExecutesAs {'role': None} {'role': None} CanReadData {'source_cluster': None}

where

node_type(?replicator) matches aws:kafka:Replicator OR generic_type == GenericCompute node_type(?source_cluster) == GenericMessaging ?source_cluster.provider_type == 'AWS::MSK::Cluster'

emit

source typeIdentity
target typeQueue
source?role
target<topic in ?source_cluster>
permissionskafka-cluster:ReadData
conditionsiam_permission
state logicACTIVE when ?role has effective kafka-cluster:ReadData on topics in the source cluster AND the replicator has been successfully created and is operational (status != FAILED/STOPPED). When the replicator state is PAUSED or STOPPED, emit CONDITIONAL(service_state). BLOCKED if the role's CanReadData is BLOCKED by an explicit deny.

Narrative

MSK Replicator {replicator.name} running as {role.name} can consume messages from source cluster {source_cluster.name} (kafka-cluster:ReadData). Replicated messages are copied to the destination cluster, enabling cross-cluster/cross-account data exfiltration via continuous replication.

Raw rule rules/derived/aws/msk.yaml

id: aws-msk-replication-can-read-data
emits: CanReadData
description: An MSK Replicator reading from a source cluster with kafka-cluster:ReadData permission enables
  cross-cluster/cross-account data exfiltration of message contents.
match:
- - replicator: null
  - ExecutesAs
  - role: null
- - role: null
  - CanReadData
  - source_cluster: null
where:
- node_type(?replicator) matches aws:kafka:Replicator OR generic_type == GenericCompute
- node_type(?source_cluster) == GenericMessaging
- ?source_cluster.provider_type == 'AWS::MSK::Cluster'
emit:
  source_type: Identity
  target_type: Queue
  source: ?role
  target: <topic in ?source_cluster>
  permissions:
  - kafka-cluster:ReadData
  conditions:
  - iam_permission
  state_logic: ACTIVE when ?role has effective kafka-cluster:ReadData on topics in the source cluster
    AND the replicator has been successfully created and is operational (status != FAILED/STOPPED). When
    the replicator state is PAUSED or STOPPED, emit CONDITIONAL(service_state). BLOCKED if the role's
    CanReadData is BLOCKED by an explicit deny.
  derived_from:
  - ?replicator ExecutesAs ?role (implicit from replicator config)
  - ?role CanReadData ?source_cluster
  false_positive_note: 'This edge is a data-exfiltration path for the role''s inherited ReadData on the
    source cluster. The attack value is cross-account or same-account replication persistence: the replicator
    continuously replicates messages as it runs, making it an automated data-flow vector vs. a one-shot
    read. If the source cluster and destination are in separate accounts, the role must have cross-account
    CanReadData on the source (granted by the source cluster resource policy). Downgrade to CONDITIONAL(role_compatibility)
    when the cross-account resource policy is not confirmed. When the replicator role is the only data
    access, cite this edge; when the principal also has direct CanReadData on the source, the principal''s
    edge is preferred (lower hop count).'
  narrative: MSK Replicator {replicator.name} running as {role.name} can consume messages from source
    cluster {source_cluster.name} (kafka-cluster:ReadData). Replicated messages are copied to the destination
    cluster, enabling cross-cluster/cross-account data exfiltration via continuous replication.
move · open · esc close