|
| 1 | +[metadata] |
| 2 | +creation_date = "2025/07/14" |
| 3 | +integration = ["azure"] |
| 4 | +maturity = "production" |
| 5 | +updated_date = "2025/07/14" |
| 6 | + |
| 7 | +[rule] |
| 8 | +author = ["Elastic"] |
| 9 | +description = """ |
| 10 | +Detects a change to the OpenID Connect (OIDC) discovery URL in the Entra ID Authentication Methods Policy. This behavior |
| 11 | +may indicate an attempt to federate Entra ID with an attacker-controlled identity provider, enabling bypass of |
| 12 | +multi-factor authentication (MFA) and unauthorized access through bring-your-own IdP (BYOIDP) methods. |
| 13 | +""" |
| 14 | +from = "now-9m" |
| 15 | +language = "esql" |
| 16 | +license = "Elastic License v2" |
| 17 | +name = "OIDC Discovery URL Changed in Entra ID" |
| 18 | +note = """## Triage and analysis |
| 19 | +
|
| 20 | +### Investigating OIDC Discovery URL Changed in Entra ID |
| 21 | +
|
| 22 | +This rule detects when the OIDC `discoveryUrl` is changed within the Entra ID Authentication Methods policy. Adversaries may leverage this to federate Entra ID with a rogue Identity Provider (IdP) under their control, allowing them to authenticate users with attacker-owned credentials and bypass MFA. This misconfiguration allows an attacker to impersonate valid users by issuing tokens via a third-party OIDC IdP while still passing validation in Entra ID. This technique has been publicly demonstrated and has critical implications for trust in federated identity. |
| 23 | +
|
| 24 | +### Possible investigation steps |
| 25 | +- Review `azure.auditlogs.properties.initiated_by.user.userPrincipalName` and `ipAddress` to identify who made the change and from where. |
| 26 | +- Examine the `old_oidc_discovery` and `new_oidc_discovery` to confirm if the new `discoveryUrl` points to an unexpected or untrusted IdP. |
| 27 | +- Check that the discovery URLs have `.well-known/openid-configuration` endpoints, which are standard for OIDC providers. |
| 28 | +- Use `azure.auditlogs.properties.correlation_id` to pivot to related changes and activity from the same session. |
| 29 | +- Review any subsequent sign-in activity that may have originated from the new IdP. |
| 30 | +- Pivot to additional logs associated with the user or application that made the change to identify any further suspicious activity. |
| 31 | +
|
| 32 | +### False positive analysis |
| 33 | +- Entra ID administrators may intentionally reconfigure OIDC trust relationships to support new business requirements. |
| 34 | +- Validate any changes with the identity or security operations team before taking action. |
| 35 | +
|
| 36 | +### Response and remediation |
| 37 | +- If the change is unauthorized, immediately revert the discovery URL to the trusted IdP via the Entra ID portal. |
| 38 | +- Revoke tokens or sessions issued after the configuration change. |
| 39 | +- Investigate how the unauthorized change occurred (e.g., compromised account or over-privileged app). |
| 40 | +- Apply conditional access policies and change control procedures to protect IdP configuration changes. |
| 41 | +""" |
| 42 | +references = ["https://dirkjanm.io/persisting-with-federated-credentials-entra-apps-managed-identities/"] |
| 43 | +risk_score = 73 |
| 44 | +rule_id = "498e4094-60e7-11f0-8847-f661ea17fbcd" |
| 45 | +severity = "high" |
| 46 | +tags = [ |
| 47 | + "Domain: Cloud", |
| 48 | + "Domain: Identity", |
| 49 | + "Data Source: Azure", |
| 50 | + "Data Source: Microsoft Entra ID", |
| 51 | + "Data Source: Microsoft Entra ID Audit Logs", |
| 52 | + "Use Case: Identity and Access Audit", |
| 53 | + "Tactic: Persistence", |
| 54 | + "Resources: Investigation Guide", |
| 55 | +] |
| 56 | +timestamp_override = "event.ingested" |
| 57 | +type = "query" |
| 58 | + |
| 59 | +query = ''' |
| 60 | +FROM logs-azure.auditlogs-* |
| 61 | +| WHERE event.action == "Authentication Methods Policy Update" |
| 62 | +| EVAL Esql.azure.auditlogs.properties.target_resources.modified_properties.new_value.replace = REPLACE(`azure.auditlogs.properties.target_resources.0.modified_properties.0.new_value`, "\\\\", "") |
| 63 | +| EVAL Esql.azure.auditlogs.properties.target_resources.modified_properties.old_value.replace = REPLACE(`azure.auditlogs.properties.target_resources.0.modified_properties.0.old_value`, "\\\\", "") |
| 64 | +| DISSECT Esql.azure.auditlogs.properties.target_resources.modified_properties.new_value.replace "%{}discoveryUrl\":\"%{Esql.azure.auditlogs.properties.auth.oidc.discovery.url.new}\"}%{}" |
| 65 | +| DISSECT Esql.azure.auditlogs.properties.target_resources.modified_properties.old_value.replace "%{}discoveryUrl\":\"%{Esql.azure.auditlogs.properties.auth.oidc.discovery.url.old}\"}%{}" |
| 66 | +| WHERE Esql.azure.auditlogs.properties.auth.oidc.discovery.url.new IS NOT NULL and Esql.azure.auditlogs.properties.auth.oidc.discovery.url.old IS NOT NULL |
| 67 | +| WHERE Esql.azure.auditlogs.properties.auth.oidc.discovery.url.new != Esql.azure.auditlogs.properties.auth.oidc.discovery.url.old |
| 68 | +| KEEP |
| 69 | + @timestamp, |
| 70 | + event.action, |
| 71 | + event.outcome, |
| 72 | + azure.tenant_id, |
| 73 | + azure.correlation_id, |
| 74 | + azure.auditlogs.properties.activity_datetime, |
| 75 | + azure.auditlogs.properties.operation_type, |
| 76 | + azure.auditlogs.properties.initiated_by.user.userPrincipalName, |
| 77 | + azure.auditlogs.properties.initiated_by.user.displayName, |
| 78 | + azure.auditlogs.properties.initiated_by.user.ipAddress, |
| 79 | + source.geo.city_name, |
| 80 | + source.geo.region_name, |
| 81 | + source.geo.country_name, |
| 82 | + Esql.azure.auditlogs.properties.auth.oidc.discovery.url.new, |
| 83 | + Esql.azure.auditlogs.properties.auth.oidc.discovery.url.old |
| 84 | +''' |
| 85 | + |
| 86 | + |
| 87 | +[[rule.threat]] |
| 88 | +framework = "MITRE ATT&CK" |
| 89 | +[[rule.threat.technique]] |
| 90 | +id = "T1556" |
| 91 | +name = "Modify Authentication Process" |
| 92 | +reference = "https://attack.mitre.org/techniques/T1556/" |
| 93 | +[[rule.threat.technique.subtechnique]] |
| 94 | +id = "T1556.009" |
| 95 | +name = "Conditional Access Policies" |
| 96 | +reference = "https://attack.mitre.org/techniques/T1556/009/" |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +[rule.threat.tactic] |
| 101 | +id = "TA0003" |
| 102 | +name = "Persistence" |
| 103 | +reference = "https://attack.mitre.org/tactics/TA0003/" |
| 104 | + |
0 commit comments