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