aws-sc-provision-product-executes-as
servicecatalog:ProvisionProduct or UpdateProvisionedProduct on a product bound to a LaunchRole constraint executes the product's CFN template as the launch role, even when the provisioning principal holds no CloudFormation or IAM permissions of their own. Any parameter-driven custom resource or UserData the user controls runs as the launch role.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
BuildWorker # AWS::ServiceCatalog::CloudFormationProduct
?product has a LaunchRole constraint with RoleArn = ?launch_role (from servicecatalog:DescribeConstraint.ConstraintParameters.RoleArn)
?principal has EFFECTIVE servicecatalog:ProvisionProduct OR servicecatalog:UpdateProvisionedProduct on ?product (via portfolio association)
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?launch_role |
| permissions | servicecatalog:ProvisionProduct servicecatalog:UpdateProvisionedProduct |
| conditions | iam_permission service_state |
| state logic | CONDITIONAL(parameter_controlled_resource) by default: the principal holds only ProvisionProduct / UpdateProvisionedProduct without template control. Escalation requires the product template to contain a parameter-driven resource (custom resource Lambda ServiceToken, UserData, or other attacker-influenceable path) that the provisioning user can reach. A product template with no attacker-influenceable resource path and rigid parameter validation is not trivially exploitable via parameters alone. BLOCKED when an SCP or permission boundary denies servicecatalog:ProvisionProduct or servicecatalog:UpdateProvisionedProduct at the effective scope; or when no LaunchRole constraint is present (then only the caller's own identity is used — no escalation). |
Narrative
{principal.name} can provision product {product.name} (servicecatalog:ProvisionProduct / UpdateProvisionedProduct). {product.name} has a Launch Role Constraint binding it to {launch_role.name}; Service Catalog assumes {launch_role.name} to execute the product's CloudFormation template. Any attacker-influenced parameter path (custom resource, UserData, Fn::If branch) in the template runs as {launch_role.name}, granting {launch_role.name}'s permissions to {principal.name}.
Raw rule rules/derived/aws/servicecatalog.yaml
id: aws-sc-provision-product-executes-as
emits: CanExecuteAs
description: servicecatalog:ProvisionProduct or UpdateProvisionedProduct on a product bound to a LaunchRole
constraint executes the product's CFN template as the launch role, even when the provisioning principal
holds no CloudFormation or IAM permissions of their own. Any parameter-driven custom resource or UserData
the user controls runs as the launch role.
match:
- - principal: null
- CanTrigger
- product: null
- - product: null
- ExecutesAs
- launch_role: null
where:
- 'node_type(?product) == BuildWorker # AWS::ServiceCatalog::CloudFormationProduct'
- ?product has a LaunchRole constraint with RoleArn = ?launch_role (from servicecatalog:DescribeConstraint.ConstraintParameters.RoleArn)
- ?principal has EFFECTIVE servicecatalog:ProvisionProduct OR servicecatalog:UpdateProvisionedProduct
on ?product (via portfolio association)
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?launch_role
permissions:
- servicecatalog:ProvisionProduct
- servicecatalog:UpdateProvisionedProduct
conditions:
- iam_permission
- service_state
state_logic: "CONDITIONAL(parameter_controlled_resource) by default: the principal holds only ProvisionProduct\
\ / UpdateProvisionedProduct without template control. Escalation requires the product template to\
\ contain a parameter-driven resource (custom resource Lambda ServiceToken, UserData, or other attacker-influenceable\
\ path) that the provisioning user can reach. A product template with no attacker-influenceable resource\
\ path and rigid parameter validation is not trivially exploitable via parameters alone. BLOCKED when\
\ an SCP or permission boundary denies servicecatalog:ProvisionProduct or servicecatalog:UpdateProvisionedProduct\
\ at the effective scope; or when no LaunchRole constraint is present (then only the caller's own\
\ identity is used \u2014 no escalation)."
confidence: 0.8
derived_from:
- ?principal CanTrigger ?product
- ?product ExecutesAs ?launch_role (LaunchRole constraint)
- cicd-chains.yaml cicd-trigger-runner-executes-as (trigger + ExecutesAs pattern)
false_positive_note: "The most critical false-positive risk: products WITHOUT a LaunchRole constraint\
\ execute as the CALLER's own identity \u2014 no escalation, do not emit this edge. Always confirm\
\ a LaunchRole constraint is present (ConstraintType == LAUNCH, ConstraintParameters.RoleArn non-empty).\
\ A LocalRoleName constraint uses a role in the END-USER's account; verify that role's privilege before\
\ treating this as a high-confidence escalation. The CONDITIONAL state reflects that bare ProvisionProduct\
\ access is only CanExecuteAs when the template has an attacker-controllable execution path \u2014\
\ a template with fully controlled parameters (static values, validation patterns that block injection)\
\ and no custom resources is not trivially exploitable; keep CONDITIONAL, not ACTIVE, until a parameter-driven\
\ execution path is confirmed. ServiceActionAssociation-based execution (ExecuteProvisionedProductServiceAction)\
\ is a separate path against the SSM Automation role \u2014 not modeled here."
narrative: '{principal.name} can provision product {product.name} (servicecatalog:ProvisionProduct /
UpdateProvisionedProduct). {product.name} has a Launch Role Constraint binding it to {launch_role.name};
Service Catalog assumes {launch_role.name} to execute the product''s CloudFormation template. Any
attacker-influenced parameter path (custom resource, UserData, Fn::If branch) in the template runs
as {launch_role.name}, granting {launch_role.name}''s permissions to {principal.name}.'