aws-kinesis-read-stream-data-fanout

kinesis:RegisterStreamConsumer + kinesis:SubscribeToShard enables reading from a stream via the enhanced fan-out path (push-based), equivalent to GetRecords-based polling.

derived aws emits CanReadData

match

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

{'principal': None} HasPermission {'stream': None}

where

node_type(?stream) == GenericMessaging ?stream.provider_type == 'AWS::Kinesis::Stream' ?principal has EFFECTIVE kinesis:RegisterStreamConsumer on ?stream ARN ?principal has EFFECTIVE kinesis:SubscribeToShard on consumer ARNs for ?stream

emit

source typeIdentity
target typeMessaging
source?principal
target?stream
permissionskinesis:RegisterStreamConsumer kinesis:SubscribeToShard
conditionsiam_permission resource_policy key_permission
state logicACTIVE when BOTH kinesis:RegisterStreamConsumer and kinesis:SubscribeToShard are confirmed EFFECTIVE on the stream ARN (and consumer ARNs respectively). RegisterStreamConsumer alone does not grant read access (it only creates a consumer ARN). SubscribeToShard alone is also insufficient without a registered consumer. The principal must hold both to achieve read capability. If the stream uses KMS encryption (EncryptionType: KMS), the principal also needs kms:Decrypt — same key_permission gate as rule 3. CONDITIONAL(key_permission) when encryption is present but decrypt is unconfirmed.

Narrative

{principal.name} has kinesis:RegisterStreamConsumer and kinesis:SubscribeToShard on {stream.name} and can read records via the enhanced fan-out path. By registering a consumer and subscribing to the stream via that consumer, {principal.name} can read record batches, enabling data exfiltration or credential harvest if records carry sensitive content.

Raw rule rules/derived/aws/kinesis.yaml

id: aws-kinesis-read-stream-data-fanout
emits: CanReadData
description: kinesis:RegisterStreamConsumer + kinesis:SubscribeToShard enables reading from a stream via
  the enhanced fan-out path (push-based), equivalent to GetRecords-based polling.
match:
- - principal: null
  - HasPermission
  - stream: null
where:
- node_type(?stream) == GenericMessaging
- ?stream.provider_type == 'AWS::Kinesis::Stream'
- ?principal has EFFECTIVE kinesis:RegisterStreamConsumer on ?stream ARN
- ?principal has EFFECTIVE kinesis:SubscribeToShard on consumer ARNs for ?stream
emit:
  source_type: Identity
  target_type: Messaging
  source: ?principal
  target: ?stream
  permissions:
  - kinesis:RegisterStreamConsumer
  - kinesis:SubscribeToShard
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  state_logic: "ACTIVE when BOTH kinesis:RegisterStreamConsumer and kinesis:SubscribeToShard are confirmed\
    \ EFFECTIVE on the stream ARN (and consumer ARNs respectively). RegisterStreamConsumer alone does\
    \ not grant read access (it only creates a consumer ARN). SubscribeToShard alone is also insufficient\
    \ without a registered consumer. The principal must hold both to achieve read capability. If the stream\
    \ uses KMS encryption (EncryptionType: KMS), the principal also needs kms:Decrypt \u2014 same key_permission\
    \ gate as rule 3. CONDITIONAL(key_permission) when encryption is present but decrypt is unconfirmed."
  confidence: min(contributing_confidences) * 0.9
  derived_from:
  - ?principal HasPermission ?stream (kinesis:RegisterStreamConsumer + kinesis:SubscribeToShard effective
    permission)
  - "credential-chains:decrypt-gates-secret-read (KMS-encrypted stream gate \u2014 cited not re-derived)"
  false_positive_note: "BOTH permissions are REQUIRED together; neither alone grants read access. RegisterStreamConsumer\
    \ creates the consumer ARN artifact; SubscribeToShard uses that consumer to open the push subscription.\
    \ An attacker must have both. This is the enhanced fan-out (push) variant of rule 3's GetRecords polling.\
    \ Like rule 3, KMS encryption gates the capability until kms:Decrypt is confirmed. A consumer created\
    \ by someone else (shared consumer ARN) may not be leverageable depending on access \u2014 assume\
    \ the attacker creates their own consumer via RegisterStreamConsumer. If only one of the two permissions\
    \ is confirmed, do not emit ACTIVE \u2014 downgrade to CONDITIONAL or block."
  narrative: '{principal.name} has kinesis:RegisterStreamConsumer and kinesis:SubscribeToShard on {stream.name}
    and can read records via the enhanced fan-out path. By registering a consumer and subscribing to the
    stream via that consumer, {principal.name} can read record batches, enabling data exfiltration or
    credential harvest if records carry sensitive content.'
move · open · esc close