aws-s3-modify-policy

s3:PutBucketPolicy lets a principal rewrite the bucket policy in full, self-granting any S3 permission including making the bucket public.

derived aws emits CanModifyPolicy

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:PutBucketPolicy on ?bucket ARN

emit

source typeIdentity
target typeObjectStorage
source?principal
target?bucket
permissionss3:PutBucketPolicy
conditionsiam_permission scp_or_org_policy
state logicACTIVE when s3:PutBucketPolicy is confirmed EFFECTIVE on the bucket ARN (identity policy + any existing bucket policy allow; no SCP deny; no permission boundary deny). CONDITIONAL(iam_permission) when a resource policy or SCP may restrict but isn't resolved. BLOCKED when an SCP or permission boundary explicitly denies s3:PutBucketPolicy. Note: BlockPublicPolicy=true prevents writing a policy with Principal:* (the write call FAILS for public statements), but the principal can still write non-public policy modifications; if the goal is to write a public-grant statement AND BlockPublicPolicy=true, the attacker must first clear that BPA flag (s3:PutBucketPublicAccessBlock) — emit CONDITIONAL(iam_permission) for the public-grant goal when BlockPublicPolicy=true.

Narrative

{principal.name} has s3:PutBucketPolicy on {bucket.name} and can rewrite the bucket policy - self-granting any S3 permission, including full public access (if BPA permits it) or cross-account access to a chosen principal.

Raw rule rules/derived/aws/s3.yaml

id: aws-s3-modify-policy
emits: CanModifyPolicy
description: s3:PutBucketPolicy lets a principal rewrite the bucket policy in full, self-granting any
  S3 permission including making the bucket public.
match:
- - principal: null
  - HasPermission
  - bucket: null
where:
- node_type(?bucket) == ObjectStorage
- ?bucket.provider_type == 'AWS::S3::Bucket'
- ?principal has EFFECTIVE s3:PutBucketPolicy on ?bucket ARN
emit:
  source_type: Identity
  target_type: ObjectStorage
  source: ?principal
  target: ?bucket
  permissions:
  - s3:PutBucketPolicy
  conditions:
  - iam_permission
  - scp_or_org_policy
  state_logic: "ACTIVE when s3:PutBucketPolicy is confirmed EFFECTIVE on the bucket ARN (identity policy\
    \ + any existing bucket policy allow; no SCP deny; no permission boundary deny). CONDITIONAL(iam_permission)\
    \ when a resource policy or SCP may restrict but isn't resolved. BLOCKED when an SCP or permission\
    \ boundary explicitly denies s3:PutBucketPolicy. Note: BlockPublicPolicy=true prevents writing a policy\
    \ with Principal:* (the write call FAILS for public statements), but the principal can still write\
    \ non-public policy modifications; if the goal is to write a public-grant statement AND BlockPublicPolicy=true,\
    \ the attacker must first clear that BPA flag (s3:PutBucketPublicAccessBlock) \u2014 emit CONDITIONAL(iam_permission)\
    \ for the public-grant goal when BlockPublicPolicy=true."
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - ?principal HasPermission ?bucket (s3:PutBucketPolicy effective permission)
  false_positive_note: "s3:PutBucketPolicy requires that the bucket policy does not have a Deny on the\
    \ action for this principal \u2014 an existing bucket policy Deny on PutBucketPolicy itself blocks\
    \ the modification (bucket-policy self-denial). BlockPublicPolicy=true causes the API to reject policy\
    \ writes that contain Principal:* statements; this does not block non-public policy writes. An SCP\
    \ mandating that bucket policies must include aws:PrincipalOrgID blocks the Principal:* self-grant\
    \ path. Model the CanModifyPolicy edge as ACTIVE for any structural policy modification (the attacker\
    \ CAN change the policy); the downstream ExposedToInternet / CrossAccountTrust depends on whether\
    \ BPA and SCPs also allow the resulting policy to take effect."
  narrative: "{principal.name} has s3:PutBucketPolicy on {bucket.name} and can rewrite the bucket policy\
    \ \u2014 self-granting any S3 permission, including full public access (if BPA permits it) or cross-account\
    \ access to a chosen principal."
move · open · esc close