aws-s3-exfiltrate-replication

s3:PutReplicationConfiguration sets up continuous S3 Replication to an external/attacker-controlled destination bucket, establishing a persistent data exfiltration channel.

derived aws emits CanExfiltrate

match

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

{'principal': None} HasPermission {'bucket': None}

where

node_type(?bucket) == ObjectStorage ?bucket.provider_type == 'AWS::S3::Bucket' ?principal has EFFECTIVE s3:PutReplicationConfiguration on ?bucket ARN ?principal has EFFECTIVE iam:PassRole for a replication IAM role (required for every PutReplicationConfiguration call to pass the role ARN) ?bucket.properties.VersioningStatus == 'Enabled' (replication requires versioning on source bucket)

emit

source typeIdentity
target typeObjectStorage
source?principal
target?bucket
permissionss3:PutReplicationConfiguration iam:PassRole
conditionsiam_permission scp_or_org_policy role_compatibility
state logicACTIVE when s3:PutReplicationConfiguration AND iam:PassRole (for the replication role) are both EFFECTIVE, and bucket versioning is enabled. CONDITIONAL (role_compatibility) when the replication role or its destination permissions are unknown. CONDITIONAL(iam_permission) when s3:PutReplicationConfiguration is effective but iam:PassRole for a replication role with destination write access is not confirmed. BLOCKED by an SCP deny on PutReplicationConfiguration or iam:PassRole. Note: the destination bucket policy must also grant s3:ReplicateObject to the source replication role — if it does not, replication fails at the first object (emit CONDITIONAL(role_compatibility) when destination policy is unknown).

Narrative

{principal.name} can configure S3 Replication on {bucket.name} (s3:PutReplicationConfiguration + iam:PassRole) to continuously copy all new objects to an external destination bucket - establishing a persistent exfiltration channel that survives credential rotation.

Raw rule rules/derived/aws/s3.yaml

id: aws-s3-exfiltrate-replication
emits: CanExfiltrate
description: s3:PutReplicationConfiguration sets up continuous S3 Replication to an external/attacker-controlled
  destination bucket, establishing a persistent data exfiltration channel.
match:
- - principal: null
  - HasPermission
  - bucket: null
where:
- node_type(?bucket) == ObjectStorage
- ?bucket.provider_type == 'AWS::S3::Bucket'
- ?principal has EFFECTIVE s3:PutReplicationConfiguration on ?bucket ARN
- ?principal has EFFECTIVE iam:PassRole for a replication IAM role (required for every PutReplicationConfiguration
  call to pass the role ARN)
- ?bucket.properties.VersioningStatus == 'Enabled'  (replication requires versioning on source bucket)
emit:
  source_type: Identity
  target_type: ObjectStorage
  source: ?principal
  target: ?bucket
  permissions:
  - s3:PutReplicationConfiguration
  - iam:PassRole
  conditions:
  - iam_permission
  - scp_or_org_policy
  - role_compatibility
  state_logic: "ACTIVE when s3:PutReplicationConfiguration AND iam:PassRole (for the replication role)\
    \ are both EFFECTIVE, and bucket versioning is enabled. CONDITIONAL (role_compatibility) when the\
    \ replication role or its destination permissions are unknown. CONDITIONAL(iam_permission) when s3:PutReplicationConfiguration\
    \ is effective but iam:PassRole for a replication role with destination write access is not confirmed.\
    \ BLOCKED by an SCP deny on PutReplicationConfiguration or iam:PassRole. Note: the destination bucket\
    \ policy must also grant s3:ReplicateObject to the source replication role \u2014 if it does not,\
    \ replication fails at the first object (emit CONDITIONAL(role_compatibility) when destination policy\
    \ is unknown)."
  confidence: min(contributing_confidences) * 0.85
  derived_from:
  - ?principal HasPermission ?bucket (s3:PutReplicationConfiguration effective permission)
  - iam:PassRole for replication IAM role
  false_positive_note: "Replication requires SOURCE bucket versioning to be enabled (VersioningStatus:\
    \ Enabled) \u2014 without it the API call succeeds but replication never starts. Check the bucket's\
    \ VersioningConfiguration before emitting ACTIVE. The replication IAM role must have s3:GetObject\
    \ / s3:GetObjectVersion on the source AND s3:ReplicateObject on the destination bucket; without both,\
    \ objects fail to replicate. The DESTINATION bucket policy must allow the source role's account/ principal\
    \ to write (s3:ReplicateObject) \u2014 cross-account replication requires the destination to explicitly\
    \ grant this; if the destination is in the same account, the role's identity policy may be sufficient.\
    \ SSE-KMS source objects also require the replication role to have kms:Decrypt on the source CMK and\
    \ kms:GenerateDataKey on the destination CMK \u2014 CONDITIONAL(key_permission) when CMKs are involved\
    \ and the role's key access is unconfirmed."
  narrative: "{principal.name} can configure S3 Replication on {bucket.name} (s3:PutReplicationConfiguration\
    \ + iam:PassRole) to continuously copy all new objects to an external destination bucket \u2014 establishing\
    \ a persistent exfiltration channel that survives credential rotation."
move · open · esc close