Entra ID Federated Identity Credential Modified
Detects when a federated identity credential is added to an Entra ID application, or when the issuer or subject of an existing credential is changed. Adversaries with application-management rights can federate the application to an attacker-controlled identity provider, or re-scope an existing trust on a shared issuer such as GitHub Actions to an identity they control, and authenticate as its service principal without creating a client secret.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/07/14"
3integration = ["azure"]
4maturity = "production"
5min_stack_comments = "Requires ES|QL JSON_EXTRACT to read federated credential Id, Issuer, and Subject values from the audit JSON."
6min_stack_version = "9.4.0"
7updated_date = "2026/10/01"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects when a federated identity credential is added to an Entra ID application, or when the issuer or subject of an
13existing credential is changed. Adversaries with application-management rights can federate the application to an
14attacker-controlled identity provider, or re-scope an existing trust on a shared issuer such as GitHub Actions to an
15identity they control, and authenticate as its service principal without creating a client secret.
16"""
17from = "now-9m"
18language = "esql"
19license = "Elastic License v2"
20name = "Entra ID Federated Identity Credential Modified"
21note = """## Triage and analysis
22
23### Investigating Entra ID Federated Identity Credential Modified
24
25This rule detects changes to the federated identity credentials on an Entra ID application. `Esql.fic_change` says which change was observed:
26
27- `fic_created`: the credential collection was an empty array and now holds a credential.
28- `fic_added`: a credential `Id` is present in the new value and absent from the old value, and the credential count increased.
29- `fic_issuer_changed`: a credential `Id` is in both values, but its issuer URL changed.
30- `fic_subject_changed`: a credential `Id` is in both values with the same issuer, but its subject identifier changed. On shared public issuers such as GitHub Actions or GitLab the issuer is identical for every tenant, so the subject is the only value scoping the trust to a specific repository, branch, or environment.
31
32The query expands the new credential array into one row per credential and matches each one to the old array by `Id`, so a change to the audience of an existing credential does not alert, and neither does removing a credential.
33
34### Possible investigation steps
35
36- Review `azure.auditlogs.properties.initiated_by.user.userPrincipalName` and `ipAddress` to identify who made the change and from where.
37- Use `Esql.fic_change` to decide whether this is a new trust, an existing trust pointed at a different issuer, or an existing trust re-scoped to a different subject.
38- For an issuer change, compare `Esql.fic_old_issuer` and `Esql.fic_new_issuer` for the credential in `Esql.fic_id`.
39- For a subject change, compare `Esql.fic_old_subject` and `Esql.fic_new_subject`. Confirm the new repository, organization, branch, or environment belongs to the organization and that the change was expected by the pipeline owner. A subject that is now null may indicate a switch to a claims matching expression; review the new credential object in `azure.auditlogs.properties.target_resources.0.modified_properties.0.new_value`.
40- For an added credential, review `Esql.fic_id`, `Esql.fic_new_issuer`, and `Esql.fic_new_subject`.
41- Check whether the issuer domain is controlled by the organization.
42- Review the application's assigned roles and permissions to understand the scope of access.
43- Use `azure.correlation_id` to pivot to related changes in the same session.
44- Check for subsequent federated sign-ins as the application's service principal.
45
46### False positive analysis
47
48- Onboarding a workload to workload identity federation adds the first credential.
49- DevOps teams add further credentials for additional repositories, branches, or environments.
50- Migrations from one identity provider to another change the issuer on an existing credential id.
51- Renaming a branch, transferring a repository to a new organization, or moving a pipeline from a branch-scoped to an environment-scoped subject changes the subject on an existing credential id.
52- Validate the change with the application owner before taking action.
53
54### Response and remediation
55
56- If the change is unauthorized, immediately remove or revert the federated identity credential.
57- Rotate any secrets or certificates associated with the application.
58- Review Azure sign-in logs and audit logs for any unauthorized activity using the application's identity.
59- Disable the application or service principal if compromise is confirmed.
60- Investigate how the unauthorized change occurred (e.g., compromised admin account, over-privileged service principal).
61- Implement conditional access policies and PIM (Privileged Identity Management) to protect application management operations.
62"""
63references = [
64 "https://dirkjanm.io/persisting-with-federated-credentials-entra-apps-managed-identities/",
65 "https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation",
66]
67risk_score = 73
68rule_id = "498e4094-60e7-11f0-8847-f661ea17fbcd"
69setup = """### Microsoft Entra ID Audit Logs
70This rule requires the Azure integration with Microsoft Entra ID Audit Logs data stream ingesting in your Elastic Stack deployment. For more information, refer to the [Microsoft Entra ID Audit Logs integration documentation](https://www.elastic.co/docs/reference/integrations/azure/adlogs).
71"""
72severity = "high"
73tags = [
74 "Domain: Cloud",
75 "Domain: Identity",
76 "Data Source: Azure",
77 "Data Source: Microsoft Entra ID Audit Logs",
78 "Use Case: Identity and Access Audit",
79 "Tactic: Persistence",
80 "Tactic: Privilege Escalation",
81 "Tactic: Defense Evasion",
82 "Resources: Investigation Guide",
83 "Profile: Beta",
84 "Rule Type: ES|QL",
85 "Platform: Entra ID",
86]
87timestamp_override = "event.ingested"
88type = "esql"
89
90query = '''
91from logs-azure.auditlogs-* metadata _id, _version, _index
92| where event.action == "Update application"
93| where `azure.auditlogs.properties.target_resources.0.modified_properties.0.display_name` == "FederatedIdentityCredentials"
94| eval Esql.fic_old_value = `azure.auditlogs.properties.target_resources.0.modified_properties.0.old_value`
95| eval Esql.fic_new_value = `azure.auditlogs.properties.target_resources.0.modified_properties.0.new_value`
96// each credential is one JSON object, so the number of "{" is the credential count
97| eval Esql.fic_old_count = mv_count(split(Esql.fic_old_value, "{")) - 1
98| eval Esql.fic_new_count = mv_count(split(Esql.fic_new_value, "{")) - 1
99// removals never alert
100| where Esql.fic_new_count > 0 and Esql.fic_new_count >= Esql.fic_old_count
101// strip the outer brackets and expand the new array into one row per credential object
102| eval Esql.fic_object = split(replace(substring(Esql.fic_new_value, 2, length(Esql.fic_new_value) - 2), "\\},\\{", "}\n{"), "\n")
103| mv_expand Esql.fic_object
104| eval Esql.fic_id = json_extract(Esql.fic_object, "Id"),
105 Esql.fic_new_issuer = json_extract(Esql.fic_object, "Issuer"),
106 Esql.fic_new_subject = json_extract(Esql.fic_object, "Subject")
107// locate the same credential Id in the old array and rebuild that object to read its previous issuer and subject
108| eval Esql.fic_old_id_pos = locate(Esql.fic_old_value, concat("\"Id\":\"", Esql.fic_id, "\""))
109| eval Esql.fic_old_object = case(
110 Esql.fic_old_id_pos > 0,
111 concat("{", mv_last(split(substring(Esql.fic_old_value, 1, Esql.fic_old_id_pos - 1), "{")), mv_first(split(substring(Esql.fic_old_value, Esql.fic_old_id_pos), "}")), "}"),
112 null
113 )
114| eval Esql.fic_old_issuer = json_extract(Esql.fic_old_object, "Issuer"),
115 Esql.fic_old_subject = json_extract(Esql.fic_old_object, "Subject")
116// subject is null on flexible credentials that use a claims matching expression, so coalesce before comparing
117| eval Esql.fic_change = case(
118 Esql.fic_old_count == 0, "fic_created",
119 Esql.fic_old_id_pos == 0 and Esql.fic_new_count > Esql.fic_old_count, "fic_added",
120 Esql.fic_old_issuer != Esql.fic_new_issuer, "fic_issuer_changed",
121 coalesce(Esql.fic_old_subject, "") != coalesce(Esql.fic_new_subject, ""), "fic_subject_changed",
122 "none"
123 )
124| where Esql.fic_change != "none"
125// the raw old/new arrays stay on the alert under azure.*; drop the working copies and intermediates
126| drop Esql.fic_old_value, Esql.fic_new_value, Esql.fic_object, Esql.fic_old_object, Esql.fic_old_id_pos
127| keep @timestamp, Esql.*, azure.*, event.*, cloud.*, related.*, tags, source.*, agent.*, client.*, _id, _version, _index, data_stream.namespace
128'''
129
130
131[[rule.threat]]
132framework = "MITRE ATT&CK"
133[[rule.threat.technique]]
134id = "T1098"
135name = "Account Manipulation"
136reference = "https://attack.mitre.org/techniques/T1098/"
137[[rule.threat.technique.subtechnique]]
138id = "T1098.001"
139name = "Additional Cloud Credentials"
140reference = "https://attack.mitre.org/techniques/T1098/001/"
141
142
143
144[rule.threat.tactic]
145id = "TA0003"
146name = "Persistence"
147reference = "https://attack.mitre.org/tactics/TA0003/"
148[[rule.threat]]
149framework = "MITRE ATT&CK"
150[[rule.threat.technique]]
151id = "T1098"
152name = "Account Manipulation"
153reference = "https://attack.mitre.org/techniques/T1098/"
154[[rule.threat.technique.subtechnique]]
155id = "T1098.001"
156name = "Additional Cloud Credentials"
157reference = "https://attack.mitre.org/techniques/T1098/001/"
158[[rule.threat.technique]]
159id = "T1484"
160name = "Domain or Tenant Policy Modification"
161reference = "https://attack.mitre.org/techniques/T1484/"
162[[rule.threat.technique.subtechnique]]
163id = "T1484.002"
164name = "Trust Modification"
165reference = "https://attack.mitre.org/techniques/T1484/002/"
166
167
168
169[rule.threat.tactic]
170id = "TA0004"
171name = "Privilege Escalation"
172reference = "https://attack.mitre.org/tactics/TA0004/"
173[[rule.threat]]
174framework = "MITRE ATT&CK"
175[[rule.threat.technique]]
176id = "T1484"
177name = "Domain or Tenant Policy Modification"
178reference = "https://attack.mitre.org/techniques/T1484/"
179[[rule.threat.technique.subtechnique]]
180id = "T1484.002"
181name = "Trust Modification"
182reference = "https://attack.mitre.org/techniques/T1484/002/"
183
184
185
186[rule.threat.tactic]
187id = "TA0005"
188name = "Defense Evasion"
189reference = "https://attack.mitre.org/tactics/TA0005/"
Triage and analysis
Investigating Entra ID Federated Identity Credential Modified
This rule detects changes to the federated identity credentials on an Entra ID application. Esql.fic_change says which change was observed:
fic_created: the credential collection was an empty array and now holds a credential.fic_added: a credentialIdis present in the new value and absent from the old value, and the credential count increased.fic_issuer_changed: a credentialIdis in both values, but its issuer URL changed.fic_subject_changed: a credentialIdis in both values with the same issuer, but its subject identifier changed. On shared public issuers such as GitHub Actions or GitLab the issuer is identical for every tenant, so the subject is the only value scoping the trust to a specific repository, branch, or environment.
The query expands the new credential array into one row per credential and matches each one to the old array by Id, so a change to the audience of an existing credential does not alert, and neither does removing a credential.
Possible investigation steps
- Review
azure.auditlogs.properties.initiated_by.user.userPrincipalNameandipAddressto identify who made the change and from where. - Use
Esql.fic_changeto decide whether this is a new trust, an existing trust pointed at a different issuer, or an existing trust re-scoped to a different subject. - For an issuer change, compare
Esql.fic_old_issuerandEsql.fic_new_issuerfor the credential inEsql.fic_id. - For a subject change, compare
Esql.fic_old_subjectandEsql.fic_new_subject. Confirm the new repository, organization, branch, or environment belongs to the organization and that the change was expected by the pipeline owner. A subject that is now null may indicate a switch to a claims matching expression; review the new credential object inazure.auditlogs.properties.target_resources.0.modified_properties.0.new_value. - For an added credential, review
Esql.fic_id,Esql.fic_new_issuer, andEsql.fic_new_subject. - Check whether the issuer domain is controlled by the organization.
- Review the application's assigned roles and permissions to understand the scope of access.
- Use
azure.correlation_idto pivot to related changes in the same session. - Check for subsequent federated sign-ins as the application's service principal.
False positive analysis
- Onboarding a workload to workload identity federation adds the first credential.
- DevOps teams add further credentials for additional repositories, branches, or environments.
- Migrations from one identity provider to another change the issuer on an existing credential id.
- Renaming a branch, transferring a repository to a new organization, or moving a pipeline from a branch-scoped to an environment-scoped subject changes the subject on an existing credential id.
- Validate the change with the application owner before taking action.
Response and remediation
- If the change is unauthorized, immediately remove or revert the federated identity credential.
- Rotate any secrets or certificates associated with the application.
- Review Azure sign-in logs and audit logs for any unauthorized activity using the application's identity.
- Disable the application or service principal if compromise is confirmed.
- Investigate how the unauthorized change occurred (e.g., compromised admin account, over-privileged service principal).
- Implement conditional access policies and PIM (Privileged Identity Management) to protect application management operations.
References
Related rules
- Entra ID End-User Consent to Application with High-Risk Delegated Scopes
- Entra ID Deviceless Windows Hello Sign-in Followed by Device Registration
- Entra ID Domain Federation Configuration Change
- Entra ID Multiple Device Registrations by a Single User
- Entra ID Conditional Access Policy (CAP) Modified