azure-appsvc-execute-as-via-attach-mi
Attach a privileged user-assigned MI to a site the attacker can already run code on, then execute as that MI. This is an existing-site chain (CanExecuteAs), NOT a create-new-workload chain.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanAttachIdentity {'site': None}
{'principal': None} CanModifyCode {'site': None}
{'site': None} ExecutesAs {'userMI': None}
where
node_type(?site) ==
ApplicationPlatform
?userMI is the user-assigned MI attached via the CanAttachIdentity edge and now bound to ?site (ExecutesAs)
?userMI.privilege_level >= ?principal.privilege_level # escalation only
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanStart {'site': None}
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | <principal> |
| target | <user-assigned managed identity> |
| permissions | Microsoft.ManagedIdentity/userAssignedIdentities/assign/action Microsoft.Web/sites/write Microsoft.Web/sites/publish/action |
| conditions | service_state role_compatibility |
| state logic | if ?site running: ACTIVE elif matched(optional[0]): ACTIVE # attacker can start/restart it else: CONDITIONAL(service_state) |
Narrative
{principal.name} can attach user-assigned MI {userMI.name} to a site it can deploy code to, then run that code and mint {userMI.name}'s token from the App Service MSI endpoint, executing as {userMI.name}.
Raw rule rules/derived/azure/appservice.yaml
id: azure-appsvc-execute-as-via-attach-mi
emits: CanExecuteAs
description: Attach a privileged user-assigned MI to a site the attacker can already run code on, then
execute as that MI. This is an existing-site chain (CanExecuteAs), NOT a create-new-workload chain.
match:
- - principal: null
- CanAttachIdentity
- site: null
- - principal: null
- CanModifyCode
- site: null
- - site: null
- ExecutesAs
- userMI: null
where:
- node_type(?site) == ApplicationPlatform
- ?userMI is the user-assigned MI attached via the CanAttachIdentity edge and now bound to ?site (ExecutesAs)
- '?userMI.privilege_level >= ?principal.privilege_level # escalation only'
optional:
- - principal: null
- CanStart
- site: null
emit:
source_type: Identity
target_type: Identity
source: <principal>
target: <user-assigned managed identity>
state_logic: 'if ?site running: ACTIVE
elif matched(optional[0]): ACTIVE # attacker can start/restart it
else: CONDITIONAL(service_state)
'
permissions:
- Microsoft.ManagedIdentity/userAssignedIdentities/assign/action
- Microsoft.Web/sites/write
- Microsoft.Web/sites/publish/action
conditions:
- service_state
- role_compatibility
derived_from:
- <CanAttachIdentity edge_id>
- <CanModifyCode edge_id>
- <ExecutesAs edge_id>
false_positive_note: Requires the attach chain (assign/action + sites/write) AND a code-exec primitive
on that site AND the site to be running (or attacker CanStart it) so the deployed code runs and reaches
the MSI endpoint. Escalation only if the attached MI out-privileges the caller. On a stopped app without
CanStart -> CONDITIONAL(service_state).
narrative: '{principal.name} can attach user-assigned MI {userMI.name} to a site it can deploy code
to, then run that code and mint {userMI.name}''s token from the App Service MSI endpoint, executing
as {userMI.name}.'