aws-dynamodb-exfil-export

dynamodb:ExportTableToPointInTime (requires PITR enabled) writes a complete table snapshot to a caller-specified S3 bucket - full table exfiltration in one API call.

derived aws emits CanExfiltrate

match

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

{'principal': None} HasPermission {'table': None}

where

node_type(?table) == NoSQLDatabase ?table.provider_type in ['AWS::DynamoDB::Table', 'AWS::DynamoDB::GlobalTable'] ?principal has EFFECTIVE dynamodb:ExportTableToPointInTime on ?table ARN ?table.properties.PointInTimeRecoveryStatus == 'ENABLED' # ContinuousBackupsDescription.PointInTimeRecoveryDescription

emit

source typeIdentity
target typeNoSQLDatabase
source?principal
target?table
permissionsdynamodb:ExportTableToPointInTime
conditionsiam_permission scp_or_org_policy service_state
state logicACTIVE when dynamodb:ExportTableToPointInTime is confirmed EFFECTIVE on the table ARN AND the table has PITR enabled (PointInTimeRecoveryStatus == ENABLED). The target S3 bucket need not be in the same account — the DynamoDB service uses its own service role to write; the calling principal specifies only the S3 URI and does not need s3:PutObject. CONDITIONAL(service_state) when PITR status is DISABLED or unknown — dynamodb:ExportTableToPointInTime fails without PITR. CONDITIONAL(scp_or_org_policy) when an SCP or permission boundary may restrict the action but isn't resolved (do not use iam_permission label for unresolved SCPs; that label refers to the primary action effectiveness). BLOCKED by an explicit SCP deny on dynamodb:ExportTableToPointInTime. Note: the export is asynchronous (InProgress -> Completed); data lands in S3 when the export completes (minutes to hours for large tables). SSE-KMS on the table does NOT prevent export — the DynamoDB service decrypts using the table CMK during export and the S3 bucket receives plaintext (or re-encrypted with the bucket's SSE).

Narrative

{principal.name} can export a complete snapshot of DynamoDB table {table.name} to S3 (dynamodb:ExportTableToPointInTime), exfiltrating all item data at any point within the PITR retention window. The DynamoDB service writes to the specified S3 bucket without requiring s3:PutObject from the calling principal.

Raw rule rules/derived/aws/dynamodb.yaml

id: aws-dynamodb-exfil-export
emits: CanExfiltrate
description: "dynamodb:ExportTableToPointInTime (requires PITR enabled) writes a complete table snapshot\
  \ to a caller-specified S3 bucket \u2014 full table exfiltration in one API call."
match:
- - principal: null
  - HasPermission
  - table: null
where:
- node_type(?table) == NoSQLDatabase
- ?table.provider_type in ['AWS::DynamoDB::Table', 'AWS::DynamoDB::GlobalTable']
- ?principal has EFFECTIVE dynamodb:ExportTableToPointInTime on ?table ARN
- '?table.properties.PointInTimeRecoveryStatus == ''ENABLED''  # ContinuousBackupsDescription.PointInTimeRecoveryDescription'
emit:
  source_type: Identity
  target_type: NoSQLDatabase
  source: ?principal
  target: ?table
  permissions:
  - dynamodb:ExportTableToPointInTime
  conditions:
  - iam_permission
  - scp_or_org_policy
  - service_state
  state_logic: "ACTIVE when dynamodb:ExportTableToPointInTime is confirmed EFFECTIVE on the table ARN\
    \ AND the table has PITR enabled (PointInTimeRecoveryStatus == ENABLED). The target S3 bucket need\
    \ not be in the same account \u2014 the DynamoDB service uses its own service role to write; the calling\
    \ principal specifies only the S3 URI and does not need s3:PutObject. CONDITIONAL(service_state) when\
    \ PITR status is DISABLED or unknown \u2014 dynamodb:ExportTableToPointInTime fails without PITR.\
    \ CONDITIONAL(scp_or_org_policy) when an SCP or permission boundary may restrict the action but isn't\
    \ resolved (do not use iam_permission label for unresolved SCPs; that label refers to the primary\
    \ action effectiveness). BLOCKED by an explicit SCP deny on dynamodb:ExportTableToPointInTime. Note:\
    \ the export is asynchronous (InProgress -> Completed); data lands in S3 when the export completes\
    \ (minutes to hours for large tables). SSE-KMS on the table does NOT prevent export \u2014 the DynamoDB\
    \ service decrypts using the table CMK during export and the S3 bucket receives plaintext (or re-encrypted\
    \ with the bucket's SSE)."
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - ?principal HasPermission ?table (dynamodb:ExportTableToPointInTime effective permission)
  - ?table PointInTimeRecoveryStatus == ENABLED (dynamodb:DescribeContinuousBackups evidence)
  false_positive_note: "PITR MUST be enabled \u2014 ExportTableToPointInTime returns ExportConflictException\
    \ if PITR is disabled. Confirm PointInTimeRecoveryStatus == ENABLED before emitting ACTIVE; otherwise\
    \ CONDITIONAL(service_state). The target S3 bucket must allow the DynamoDB service (dynamodb.amazonaws.com)\
    \ to call s3:PutObject \u2014 typically done via the bucket policy allowing the DynamoDB service principal.\
    \ If the target bucket blocks the DynamoDB service, the export fails (CONDITIONAL(resource_policy)\
    \ for the target S3 bucket, but this is typically transparent to the calling principal who controls\
    \ the destination). Table-level SSE with a CMK does NOT gate the export \u2014 the export decrypts\
    \ transparently. The export includes ALL items at the chosen point in time; there is no per-attribute\
    \ redaction. ExportTableToPointInTime requires no data-plane GetItem/Scan permission \u2014 it is\
    \ a control-plane export action. Emit even if the principal lacks GetItem/Query/Scan."
  narrative: '{principal.name} can export a complete snapshot of DynamoDB table {table.name} to S3 (dynamodb:ExportTableToPointInTime),
    exfiltrating all item data at any point within the PITR retention window. The DynamoDB service writes
    to the specified S3 bucket without requiring s3:PutObject from the calling principal.'
move · open · esc close