aws-cfn-template-s3-write-modifies-code

Write access to the S3 object that a stack's TemplateURL references: the next stack update will load and execute the attacker's template as the service role.

derived aws emits CanModifyCode

match

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

{'principal': None} CanWrite {'template_object': None} {'stack': None} ContainsResourceReference {'template_object': None}

where

node_type(?template_object) == ObjectStorage # S3 template bucket/key node_type(?stack) == BuildWorker # AWS::CloudFormation::Stack ?stack.TemplateURL references ?template_object.s3_uri ?stack has RoleARN set

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} CanModify {'stack': None}

emit

source typeIdentity
target typeCompute
source?principal
target?stack
permissionss3:PutObject
conditionsrun_path iam_permission
state logicCONDITIONAL(run_path) by default — overwriting the S3 template object does NOT immediately execute; the stack must be updated (cloudformation:UpdateStack / ExecuteChangeSet) against that URL to pick up the change. ACTIVE when matched(optional[0]): the principal can ALSO call UpdateStack or ExecuteChangeSet on ?stack (the run path is closed). Inherit weakest with the CanWrite edge on ?template_object (an S3 bucket policy or SCP blocking s3:PutObject makes this BLOCKED).

Narrative

{principal.name} can overwrite the S3 template object {template_object.name} referenced by stack {stack.name} (s3:PutObject). The next time {stack.name} is updated against that TemplateURL, the attacker's template runs as the stack's service role.

Raw rule rules/derived/aws/cloudformation.yaml

id: aws-cfn-template-s3-write-modifies-code
emits: CanModifyCode
description: 'Write access to the S3 object that a stack''s TemplateURL references: the next stack update
  will load and execute the attacker''s template as the service role.'
match:
- - principal: null
  - CanWrite
  - template_object: null
- - stack: null
  - ContainsResourceReference
  - template_object: null
where:
- 'node_type(?template_object) == ObjectStorage  # S3 template bucket/key'
- 'node_type(?stack) == BuildWorker              # AWS::CloudFormation::Stack'
- ?stack.TemplateURL references ?template_object.s3_uri
- ?stack has RoleARN set
optional:
- - principal: null
  - CanModify
  - stack: null
emit:
  source_type: Identity
  target_type: Compute
  source: ?principal
  target: ?stack
  permissions:
  - s3:PutObject
  conditions:
  - run_path
  - iam_permission
  state_logic: "CONDITIONAL(run_path) by default \u2014 overwriting the S3 template object does NOT immediately\
    \ execute; the stack must be updated (cloudformation:UpdateStack / ExecuteChangeSet) against that\
    \ URL to pick up the change. ACTIVE when matched(optional[0]): the principal can ALSO call UpdateStack\
    \ or ExecuteChangeSet on ?stack (the run path is closed). Inherit weakest with the CanWrite edge on\
    \ ?template_object (an S3 bucket policy or SCP blocking s3:PutObject makes this BLOCKED)."
  confidence: 0.85
  derived_from:
  - ?principal CanWrite ?template_object
  - ?stack ContainsResourceReference ?template_object
  false_positive_note: "The S3 write alone is NOT an immediate code-injection; the stack must be re-deployed\
    \ against the same URL. A stack that pins templates using a version-specific S3 URL (with version\
    \ ID lock) is NOT affected by an overwrite of the latest object \u2014 downgrade to POTENTIAL(run_path)\
    \ or omit. Verify the TemplateURL matches the S3 object (bucket + key + any version ID). cicd-chains\
    \ then collapses this CanModifyCode + ExecutesAs into CanExecuteAs; do not re-derive CanExecuteAs\
    \ here."
  narrative: '{principal.name} can overwrite the S3 template object {template_object.name} referenced
    by stack {stack.name} (s3:PutObject). The next time {stack.name} is updated against that TemplateURL,
    the attacker''s template runs as the stack''s service role.'
move · open · esc close