aws-cfn-changeset-execute-modifies-code

cloudformation:CreateChangeSet with attacker template + cloudformation:ExecuteChangeSet applies the attacker's template to the stack, running as the service role.

derived aws emits CanModifyCode

match

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

{'principal': None} HasPermission {'stack': None}

where

node_type(?stack) == BuildWorker # AWS::CloudFormation::Stack ?principal has EFFECTIVE cloudformation:CreateChangeSet on ?stack ?principal has EFFECTIVE cloudformation:ExecuteChangeSet on ?stack ?stack has RoleARN set (service role exists)

emit

source typeIdentity
target typeCompute
source?principal
target?stack
permissionscloudformation:CreateChangeSet cloudformation:ExecuteChangeSet
conditionsiam_permission service_state
state logicACTIVE when both cloudformation:CreateChangeSet AND cloudformation:ExecuteChangeSet are in the principal's EFFECTIVE policy on ?stack AND the stack is mutable (CREATE_COMPLETE, UPDATE_COMPLETE, UPDATE_ROLLBACK_COMPLETE). If only CreateChangeSet is held (not ExecuteChangeSet), downgrade to CONDITIONAL(approval_required) — the change set exists but a separate approver must execute it. BLOCKED if SCP/boundary denies either cloudformation:CreateChangeSet or cloudformation:ExecuteChangeSet at the effective scope.

Narrative

{principal.name} can create a change set on stack {stack.name} with an attacker- supplied template (cloudformation:CreateChangeSet) and then execute it (cloudformation:ExecuteChangeSet), applying arbitrary resource API calls as the stack's service role.

Raw rule rules/derived/aws/cloudformation.yaml

id: aws-cfn-changeset-execute-modifies-code
emits: CanModifyCode
description: cloudformation:CreateChangeSet with attacker template + cloudformation:ExecuteChangeSet applies
  the attacker's template to the stack, running as the service role.
match:
- - principal: null
  - HasPermission
  - stack: null
where:
- 'node_type(?stack) == BuildWorker  # AWS::CloudFormation::Stack'
- ?principal has EFFECTIVE cloudformation:CreateChangeSet on ?stack
- ?principal has EFFECTIVE cloudformation:ExecuteChangeSet on ?stack
- ?stack has RoleARN set (service role exists)
emit:
  source_type: Identity
  target_type: Compute
  source: ?principal
  target: ?stack
  permissions:
  - cloudformation:CreateChangeSet
  - cloudformation:ExecuteChangeSet
  conditions:
  - iam_permission
  - service_state
  state_logic: "ACTIVE when both cloudformation:CreateChangeSet AND cloudformation:ExecuteChangeSet are\
    \ in the principal's EFFECTIVE policy on ?stack AND the stack is mutable (CREATE_COMPLETE, UPDATE_COMPLETE,\
    \ UPDATE_ROLLBACK_COMPLETE). If only CreateChangeSet is held (not ExecuteChangeSet), downgrade to\
    \ CONDITIONAL(approval_required) \u2014 the change set exists but a separate approver must execute\
    \ it. BLOCKED if SCP/boundary denies either cloudformation:CreateChangeSet or cloudformation:ExecuteChangeSet\
    \ at the effective scope."
  confidence: 0.93
  derived_from:
  - cloudformation:CreateChangeSet EFFECTIVE on ?stack
  - cloudformation:ExecuteChangeSet EFFECTIVE on ?stack
  - ?stack RoleARN is set
  false_positive_note: "Holding ONLY ExecuteChangeSet (approver role) without CreateChangeSet is NOT attacker-controlled\
    \ code injection \u2014 the change set was authored by someone else. Holding ONLY CreateChangeSet\
    \ is CONDITIONAL(approval_required) \u2014 the change set must be approved/executed by another principal.\
    \ Do NOT emit ACTIVE unless both actions are held on the same stack. cicd-chains collapses this CanModifyCode\
    \ into CanExecuteAs; do not re-derive CanExecuteAs here."
  narrative: '{principal.name} can create a change set on stack {stack.name} with an attacker- supplied
    template (cloudformation:CreateChangeSet) and then execute it (cloudformation:ExecuteChangeSet), applying
    arbitrary resource API calls as the stack''s service role.'
move · open · esc close