Tenant authentication methods policy changed


Description

Identifies changes to the tenant authentication methods policy, such as enabling or disabling a method (Temporary Access Pass, SMS, FIDO2) or changing Microsoft Authenticator settings, with the old and new value of each setting that changed.

Query · kql

let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
AuditLogs
| where TimeGenerated between (starttime .. endtime)
| where OperationName =~ "Authentication Methods Policy Update"
| where Result =~ "success"
// One save can change several settings, and the TargetResources entry that carries the values is not
// always the first one, so every entry and every modified property is expanded.
| mv-apply TargetResource = TargetResources on (
      mv-expand Property = TargetResource.modifiedProperties
      | extend PropertyName = tostring(Property.displayName)
      | extend OldValue = trim('"', tostring(Property.oldValue))
      | extend NewValue = trim('"', tostring(Property.newValue))
  )
| where OldValue != NewValue
// Settings are named "<Method>.<Setting>"
| extend Method  = tostring(split(PropertyName, ".")[0])
| extend Setting = strcat_array(array_slice(split(PropertyName, "."), 1, -1), ".")
| extend StateChange = case(
      Setting =~ "State" and NewValue =~ "Enabled", "Method enabled",
      Setting =~ "State" and NewValue =~ "Disabled", "Method disabled",
      "Other setting changed")
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor    = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp  = iff(
      isnotempty(tostring(InitiatedBy.user.ipAddress)),
      tostring(InitiatedBy.user.ipAddress),
      tostring(InitiatedBy.app.ipAddress))
| extend AccountName      = tostring(split(ActorUpn, "@")[0])
| extend AccountUPNSuffix = tostring(split(ActorUpn, "@")[1])
| project
    TimeGenerated,
    OperationName,
    Method,
    Setting,
    StateChange,
    OldValue,
    NewValue,
    Actor,
    ActorUpn,
    AccountName,
    AccountUPNSuffix,
    ActorIp,
    CorrelationId
| sort by TimeGenerated desc
Raw source Tenant authentication methods policy changed · KQL
Esc
Published by Azure/Azure-Sentinel ↗, licensed under MIT ↗. Reproduced here unmodified.
id: 92acf0e0-77e2-4149-905f-779cca97a591
name: Tenant authentication methods policy changed
description: |
  Identifies changes to the tenant authentication methods policy, such as enabling or disabling a method (Temporary Access Pass, SMS, FIDO2) or changing Microsoft Authenticator settings, with the old and new value of each setting that changed.
description-detailed: |
  The authentication methods policy decides which sign-in and MFA methods every user in the tenant is allowed
  to register and use. Weakening it is a quiet way to prepare account takeover and persistence: enabling a
  method that can be registered without strong proof (Temporary Access Pass, SMS, voice, email), disabling a
  phishing-resistant method such as FIDO2, or loosening the Microsoft Authenticator settings. None of this
  touches a single user, so user-level detections such as MFA registration or Temporary Access Pass creation
  never see it.
  The audit log records every save as an "Authentication Methods Policy Update" event written by the
  "Authentication Methods" service, with the old and new value of each changed setting in the modified
  properties. The setting is named "<Method>.<Setting>", for example "SoftwareOath.State" or
  "MicrosoftAuthenticator.FeatureSettings", and the entry that holds those values is not the first
  TargetResources entry (in events observed in a lab tenant it has neither a display name nor a type), so every
  entry is expanded instead of reading a fixed index. A single save can change several settings and returns one
  row per changed setting. A save also writes two "Update policy" events for the Fido and Default policies from
  the "Azure Credential Configuration Endpoint Service" application; they carry no values and are deliberately
  not matched.
  This query applies no allow-list: any successful change is returned, so a planned change must be ruled out
  against the change calendar. Treat a change made outside a change window, by an actor who does not normally
  administer authentication methods, or followed within the hour by a change in the two companion hunting
  queries of this pack (tenant authorization policy and device registration policy), as high priority. Combine
  it with the Temporary Access Pass and MFA modification hunting queries to follow what is done once a method
  has been enabled.
  Observed in one lab tenant only: the setting names beyond "SoftwareOath.State" and
  "MicrosoftAuthenticator.FeatureSettings" are shown as they appear in the log and are not mapped to a fixed
  list, so a method that is not covered by the lab still appears as long as it is logged with this operation.
  References:
  - https://learn.microsoft.com/entra/identity/authentication/concept-authentication-methods-manage
  - https://learn.microsoft.com/entra/identity/monitoring-health/reference-audit-activities
  - https://attack.mitre.org/techniques/T1556/006/
  - https://attack.mitre.org/techniques/T1098/
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
tactics:
  - Persistence
  - DefenseEvasion
  - CredentialAccess
relevantTechniques:
  - T1556.006
  - T1098
query: |
  let starttime = todatetime('{{StartTimeISO}}');
  let endtime = todatetime('{{EndTimeISO}}');
  AuditLogs
  | where TimeGenerated between (starttime .. endtime)
  | where OperationName =~ "Authentication Methods Policy Update"
  | where Result =~ "success"
  // One save can change several settings, and the TargetResources entry that carries the values is not
  // always the first one, so every entry and every modified property is expanded.
  | mv-apply TargetResource = TargetResources on (
        mv-expand Property = TargetResource.modifiedProperties
        | extend PropertyName = tostring(Property.displayName)
        | extend OldValue = trim('"', tostring(Property.oldValue))
        | extend NewValue = trim('"', tostring(Property.newValue))
    )
  | where OldValue != NewValue
  // Settings are named "<Method>.<Setting>"
  | extend Method  = tostring(split(PropertyName, ".")[0])
  | extend Setting = strcat_array(array_slice(split(PropertyName, "."), 1, -1), ".")
  | extend StateChange = case(
        Setting =~ "State" and NewValue =~ "Enabled", "Method enabled",
        Setting =~ "State" and NewValue =~ "Disabled", "Method disabled",
        "Other setting changed")
  | extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
  | extend ActorApp = tostring(InitiatedBy.app.displayName)
  | extend Actor    = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
  | extend ActorIp  = iff(
        isnotempty(tostring(InitiatedBy.user.ipAddress)),
        tostring(InitiatedBy.user.ipAddress),
        tostring(InitiatedBy.app.ipAddress))
  | extend AccountName      = tostring(split(ActorUpn, "@")[0])
  | extend AccountUPNSuffix = tostring(split(ActorUpn, "@")[1])
  | project
      TimeGenerated,
      OperationName,
      Method,
      Setting,
      StateChange,
      OldValue,
      NewValue,
      Actor,
      ActorUpn,
      AccountName,
      AccountUPNSuffix,
      ActorIp,
      CorrelationId
  | sort by TimeGenerated desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUpn
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ActorIp
customDetails:
  Method: Method
  Setting: Setting
  StateChange: StateChange
version: 1.0.0
metadata:
    source:
        kind: Community
    author:
        name: descambiado
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Protection", "Identity" ]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.