aws-emr-serverless-execute-as
Submit an EMR Serverless job run with attacker-controlled application code and a passed privileged executionRole via emr-serverless:StartJobRun + iam:PassRole.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?app) ==
AnalyticsService
?app provider_type == AWS::EMRServerless::Application
CanModifyCode here is aws-emr-serverless-start-job-code (StartJobRun on Serverless)
CanPassIdentity here is aws-emr-serverless-pass-exec-role (emr-serverless.amazonaws.com)
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?role |
| permissions | emr-serverless:StartJobRun iam:PassRole |
| conditions | iam_permission role_compatibility |
| state logic | ACTIVE when principal has EFFECTIVE emr-serverless:StartJobRun on the application AND iam:PassRole for ?role (PassedToService = emr-serverless.amazonaws.com). CONDITIONAL(role_compatibility) when PassedToService is unknown. BLOCKED if SCP or permission boundary denies either action. |
Narrative
{principal.name} can submit an EMR Serverless job run with attacker-chosen application code (emr-serverless:StartJobRun) and pass privileged execution role {role.name} (iam:PassRole); the Spark/Hive job executes arbitrary code as {role.name} via the EMR credential relay endpoint.
Raw rule rules/derived/aws/emr.yaml
id: aws-emr-serverless-execute-as
emits: CanExecuteAs
description: Submit an EMR Serverless job run with attacker-controlled application code and a passed privileged
executionRole via emr-serverless:StartJobRun + iam:PassRole.
match:
- - principal: null
- CanModifyCode
- app: null
- - principal: null
- CanPassIdentity
- role: null
where:
- node_type(?app) == AnalyticsService
- ?app provider_type == AWS::EMRServerless::Application
- CanModifyCode here is aws-emr-serverless-start-job-code (StartJobRun on Serverless)
- CanPassIdentity here is aws-emr-serverless-pass-exec-role (emr-serverless.amazonaws.com)
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?role
permissions:
- emr-serverless:StartJobRun
- iam:PassRole
conditions:
- iam_permission
- role_compatibility
state_logic: ACTIVE when principal has EFFECTIVE emr-serverless:StartJobRun on the application AND iam:PassRole
for ?role (PassedToService = emr-serverless.amazonaws.com). CONDITIONAL(role_compatibility) when PassedToService
is unknown. BLOCKED if SCP or permission boundary denies either action.
confidence: min(contributing_confidences) * 0.95
derived_from:
- <CanModifyCode(aws-emr-serverless-start-job-code) edge_id>
- <CanPassIdentity(aws-emr-serverless-pass-exec-role) edge_id>
false_positive_note: "PassRole is ALWAYS required for EMR Serverless \u2014 unlike classic EMR AddJobFlowSteps.\
\ Do not emit ACTIVE when iam:PassRole is missing or denied. Honor emr-serverless.amazonaws.com in\
\ the PassedToService condition. Escalation roll-up is can-control escalate-via-execute-as \u2014\
\ CITE, do not re-derive."
narrative: '{principal.name} can submit an EMR Serverless job run with attacker-chosen application code
(emr-serverless:StartJobRun) and pass privileged execution role {role.name} (iam:PassRole); the Spark/Hive
job executes arbitrary code as {role.name} via the EMR credential relay endpoint.'