azure-msgraph-fedcred-add
A service principal holding Application.ReadWrite.All or Directory.ReadWrite.All can modify the trust configuration of any app registration by adding a federated identity credential (OIDC trust), allowing an attacker-controlled external issuer to obtain tokens as that app without a static secret.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?sp) in [
MachineIdentity, ApplicationIdentity]
?approle is one of: Application.ReadWrite.All (id: 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9) OR Directory.ReadWrite.All (id: 19dbc75e-c2e2-444c-a770-ec69d8559fc7) on microsoft.graph SP
for all ApplicationIdentity nodes (?target_app) in the tenant where ?sp != ?target_app
emit
| source type | Identity |
|---|---|
| target type | MachineIdentity |
| source | ?sp |
| target | ?target_app (service principal / app trust configuration) |
| permissions | Application.ReadWrite.All Directory.ReadWrite.All |
| conditions | iam_permission service_state |
| state logic | ACTIVE when the appRoleAssignment is granted and SP credential is valid. CONDITIONAL(service_state) if SP credential expired. Downstream federation path (TrustsExternalIdentity -> ExternalIdentityMapsTo -> CanFederateAs) is governed by federation-chains.yaml federation-maps-to-identity and depends on the attacker controlling an OIDC issuer whose tokens satisfy the federated credential's issuer/subject/audience conditions; that downstream state is CONDITIONAL(trust_condition) until confirmed broad/unpinned. |
Narrative
{sp.name} holds Application.ReadWrite.All and can modify the configuration of {target_app.name} by adding a federated identity credential via POST /applications/{id}/federatedIdentityCredentials, configuring it to trust an attacker-controlled OIDC issuer; an external identity matching that trust can then obtain tokens as {target_app.name} without any static secret.
Raw rule rules/derived/azure/msgraph.yaml
id: azure-msgraph-fedcred-add
emits: CanModifyTrust
description: A service principal holding Application.ReadWrite.All or Directory.ReadWrite.All can modify
the trust configuration of any app registration by adding a federated identity credential (OIDC trust),
allowing an attacker-controlled external issuer to obtain tokens as that app without a static secret.
match:
- - sp: null
- HasRole
- approle: null
where:
- node_type(?sp) in [MachineIdentity, ApplicationIdentity]
- '?approle is one of: Application.ReadWrite.All (id: 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9) OR Directory.ReadWrite.All
(id: 19dbc75e-c2e2-444c-a770-ec69d8559fc7) on microsoft.graph SP'
- for all ApplicationIdentity nodes (?target_app) in the tenant where ?sp != ?target_app
emit:
source_type: Identity
target_type: MachineIdentity
source: ?sp
target: ?target_app (service principal / app trust configuration)
permissions:
- Application.ReadWrite.All
- Directory.ReadWrite.All
conditions:
- iam_permission
- service_state
state_logic: ACTIVE when the appRoleAssignment is granted and SP credential is valid. CONDITIONAL(service_state)
if SP credential expired. Downstream federation path (TrustsExternalIdentity -> ExternalIdentityMapsTo
-> CanFederateAs) is governed by federation-chains.yaml federation-maps-to-identity and depends on
the attacker controlling an OIDC issuer whose tokens satisfy the federated credential's issuer/subject/audience
conditions; that downstream state is CONDITIONAL(trust_condition) until confirmed broad/unpinned.
confidence: '0.9'
derived_from:
- <HasRole edge_id (appRoleAssignment Application.ReadWrite.All or Directory.ReadWrite.All on ?sp)>
- "azure/identity-escalation.yaml#azure-federated-credential-write (object-ownership/credential-update\
\ path \u2014 parallel; this covers the app-permission path)"
- federation-chains.yaml#federation-maps-to-identity (consumes the resulting TrustsExternalIdentity
+ ExternalIdentityMapsTo pair)
false_positive_note: "Adding a federated credential requires: (a) SP credential is valid to call Graph;\
\ (b) target app allows federatedIdentityCredentials (most do by default). The attack is a two-step:\
\ add the fedcred (this edge = CanModifyPolicy on the app configuration), then use an attacker-controlled\
\ external identity that satisfies the issuer/sub/aud criteria to obtain a token (federation-chains\
\ handles the downstream). Do NOT model the downstream CanFederateAs here \u2014 let federation-chains\
\ derive it. Do NOT emit for the SP's OWN app (self-modification does not escalate if the SP already\
\ has Application.ReadWrite.All; it may still be a persistence mechanism \u2014 flag separately if\
\ needed). Confidence reduced from rule 2 because the downstream federation step adds friction. This\
\ rule emits CanModifyPolicy for every ApplicationIdentity in the tenant, creating significant fan-out;\
\ queries should filter by target privilege score when seeking high-impact paths."
narrative: '{sp.name} holds Application.ReadWrite.All and can modify the configuration of {target_app.name}
by adding a federated identity credential via POST /applications/{id}/federatedIdentityCredentials,
configuring it to trust an attacker-controlled OIDC issuer; an external identity matching that trust
can then obtain tokens as {target_app.name} without any static secret.'