aws-dynamodb-put-resource-policy

dynamodb:PutResourcePolicy allows rewriting the table/stream resource policy, enabling self-grant of data-plane access or cross-account grant - a direct privilege escalation primitive.

derived aws emits CanModifyPolicy

match

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

{'principal': None} HasPermission {'resource': None}

where

node_type(?resource) in [NoSQLDatabase, Messaging] ?resource.provider_type in ['AWS::DynamoDB::Table', 'AWS::DynamoDB::GlobalTable', 'AWS::DynamoDB::Stream'] ?principal has EFFECTIVE dynamodb:PutResourcePolicy on ?resource ARN

emit

source typeIdentity
target typenode_type(?resource)
source?principal
target?resource
permissionsdynamodb:PutResourcePolicy
conditionsiam_permission scp_or_org_policy
state logicACTIVE when dynamodb:PutResourcePolicy is confirmed EFFECTIVE on the table (or stream) ARN and no SCP deny exists. CONDITIONAL(scp_or_org_policy) when an SCP may restrict PutResourcePolicy but hasn't been resolved. BLOCKED by an explicit SCP or permission-boundary deny on dynamodb:PutResourcePolicy. Note: PutResourcePolicy requires a resource policy block (cannot be an empty policy — the API requires a syntactically valid policy document). The new policy takes effect immediately for subsequent API calls. An SCP with aws:ResourceTag conditions may prevent policy changes on specific tables — honor tag-based SCPs.

Narrative

{principal.name} can rewrite the resource policy of {resource.name} (dynamodb:PutResourcePolicy), self-granting full data-plane access (GetItem, Scan, PutItem) or granting access to an external account - a direct privilege escalation without requiring an IAM role assumption.

Raw rule rules/derived/aws/dynamodb.yaml

id: aws-dynamodb-put-resource-policy
emits: CanModifyPolicy
description: "dynamodb:PutResourcePolicy allows rewriting the table/stream resource policy, enabling self-grant\
  \ of data-plane access or cross-account grant \u2014 a direct privilege escalation primitive."
match:
- - principal: null
  - HasPermission
  - resource: null
where:
- node_type(?resource) in [NoSQLDatabase, Messaging]
- ?resource.provider_type in ['AWS::DynamoDB::Table', 'AWS::DynamoDB::GlobalTable', 'AWS::DynamoDB::Stream']
- ?principal has EFFECTIVE dynamodb:PutResourcePolicy on ?resource ARN
emit:
  source_type: Identity
  target_type: node_type(?resource)
  source: ?principal
  target: ?resource
  permissions:
  - dynamodb:PutResourcePolicy
  conditions:
  - iam_permission
  - scp_or_org_policy
  state_logic: "ACTIVE when dynamodb:PutResourcePolicy is confirmed EFFECTIVE on the table (or stream)\
    \ ARN and no SCP deny exists. CONDITIONAL(scp_or_org_policy) when an SCP may restrict PutResourcePolicy\
    \ but hasn't been resolved. BLOCKED by an explicit SCP or permission-boundary deny on dynamodb:PutResourcePolicy.\
    \ Note: PutResourcePolicy requires a resource policy block (cannot be an empty policy \u2014 the API\
    \ requires a syntactically valid policy document). The new policy takes effect immediately for subsequent\
    \ API calls. An SCP with aws:ResourceTag conditions may prevent policy changes on specific tables\
    \ \u2014 honor tag-based SCPs."
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - ?principal HasPermission ?resource (dynamodb:PutResourcePolicy effective permission)
  false_positive_note: "PutResourcePolicy is a control-plane action; it does NOT require data-plane permissions\
    \ (GetItem/Scan) to grant escalation. Even a principal with no existing data-plane access who has\
    \ PutResourcePolicy can escalate to full data access in one call. The policy must be syntactically\
    \ valid JSON and comply with IAM resource policy limits (20 KB max). Cross-account grants via PutResourcePolicy\
    \ require the principal in the foreign account to ALSO have a matching identity-policy grant in their\
    \ own account (resource policy alone does not override the need for an identity-based allow in cross-account\
    \ scenarios \u2014 unlike same-account resource policies that can grant access without identity policy).\
    \ Model cross-account PutResourcePolicy escalation as ACTIVE for the SAME-account self-grant. CrossAccountTrust\
    \ edges are emitted by the explicit normalization rule aws-dynamodb-cross-account-table-policy when\
    \ the resource policy is collected. Stream vs. table targeting: when the resource is a stream (Messaging\
    \ node_type), emit target_type: Messaging; when the resource is a table (NoSQLDatabase node_type),\
    \ emit target_type: NoSQLDatabase."
  narrative: "{principal.name} can rewrite the resource policy of {resource.name} (dynamodb:PutResourcePolicy),\
    \ self-granting full data-plane access (GetItem, Scan, PutItem) or granting access to an external\
    \ account \u2014 a direct privilege escalation without requiring an IAM role assumption."
move · open · esc close