aws-cfn-service-role-triggers-custom-lambda

A stack's service role (Identity) triggers the Custom:: resource Lambda during stack lifecycle events via lambda:InvokeFunction; principals who CanExecuteAs the service role inherit this CanTrigger path.

derived aws emits CanTrigger

match

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

{'stack': None} ExecutesAs {'service_role': None} {'stack': None} ContainsResourceReference {'lambda': None}

where

node_type(?stack) == BuildWorker # AWS::CloudFormation::Stack node_type(?service_role) == Role # stack service role node_type(?lambda) == ServerlessFunction # Custom:: resource Lambda ?stack template contains a Custom:: logical resource ServiceToken referencing ?lambda.ARN ?service_role has EFFECTIVE lambda:InvokeFunction on ?lambda

emit

source typeIdentity
target typeCompute
source?service_role
target?lambda
permissionslambda:InvokeFunction
conditionsiam_permission service_state
state logicACTIVE when the stack service role has EFFECTIVE lambda:InvokeFunction on the Lambda (or Lambda resource policy grants cloudformation.amazonaws.com invoke) AND the stack is operational (CREATE_COMPLETE, UPDATE_COMPLETE). The service role will call lambda:InvokeFunction during every future stack lifecycle event (UPDATE/DELETE). CONDITIONAL(service_state) if the stack is in a failed/transitional state.

Narrative

Stack {stack.name}'s service role {service_role.name} triggers Lambda {lambda.name} during stack lifecycle events (Custom:: resource, ServiceToken) using lambda:InvokeFunction. Any attacker who can CanExecuteAs {service_role.name} inherits this CanTrigger; code inside {lambda.name} executes as {lambda.name}'s separate execution role.

Raw rule rules/derived/aws/cloudformation.yaml

id: aws-cfn-service-role-triggers-custom-lambda
emits: CanTrigger
description: 'A stack''s service role (Identity) triggers the Custom:: resource Lambda during stack lifecycle
  events via lambda:InvokeFunction; principals who CanExecuteAs the service role inherit this CanTrigger
  path.'
match:
- - stack: null
  - ExecutesAs
  - service_role: null
- - stack: null
  - ContainsResourceReference
  - lambda: null
where:
- 'node_type(?stack)        == BuildWorker        # AWS::CloudFormation::Stack'
- 'node_type(?service_role) == Role               # stack service role'
- 'node_type(?lambda)       == ServerlessFunction # Custom:: resource Lambda'
- '?stack template contains a Custom:: logical resource ServiceToken referencing ?lambda.ARN'
- ?service_role has EFFECTIVE lambda:InvokeFunction on ?lambda
emit:
  source_type: Identity
  target_type: Compute
  source: ?service_role
  target: ?lambda
  permissions:
  - lambda:InvokeFunction
  conditions:
  - iam_permission
  - service_state
  state_logic: ACTIVE when the stack service role has EFFECTIVE lambda:InvokeFunction on the Lambda (or
    Lambda resource policy grants cloudformation.amazonaws.com invoke) AND the stack is operational (CREATE_COMPLETE,
    UPDATE_COMPLETE). The service role will call lambda:InvokeFunction during every future stack lifecycle
    event (UPDATE/DELETE). CONDITIONAL(service_state) if the stack is in a failed/transitional state.
  confidence: 0.9
  derived_from:
  - ?stack ExecutesAs ?service_role
  - ?stack ContainsResourceReference ?lambda
  - ?service_role EFFECTIVE lambda:InvokeFunction on ?lambda
  false_positive_note: "Source is the service role Identity, not the Compute stack (CanTrigger requires\
    \ Identity/Messaging/Storage/Network as source; Compute is not in that set). The Lambda execution\
    \ role (separate from the service role) is the terminal escalation target \u2014 Lambda derived rules\
    \ (aws-lambda-execute-as-via-code etc.) propagate from the Lambda's ExecutesAs edge. A principal who\
    \ can CanModifyCode on the stack => cicd-chains yields CanExecuteAs(?p -> service_role) => this CanTrigger(?service_role\
    \ -> ?lambda) chains to CanExecuteAs(?p -> lambdaRole) via cicd-chains rule 2. Do NOT conflate the\
    \ Lambda execution role with the stack service role \u2014 they are separate identities."
  narrative: 'Stack {stack.name}''s service role {service_role.name} triggers Lambda {lambda.name} during
    stack lifecycle events (Custom:: resource, ServiceToken) using lambda:InvokeFunction. Any attacker
    who can CanExecuteAs {service_role.name} inherits this CanTrigger; code inside {lambda.name} executes
    as {lambda.name}''s separate execution role.'
move · open · esc close