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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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. |
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.