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.
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 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. |
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.'