← Back to SOC feed Coverage →

Bulk admin-initiated password reset across multiple accounts

kql MEDIUM Azure-Sentinel
T1098T1078.004
AuditLogs
huntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-27T23:00:00Z · Confidence: medium

Hunt Hypothesis

A single actor resetting passwords for three or more accounts within one hour may indicate an adversary attempting to take over multiple accounts through credential compromise. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential account takeovers before they escalate into broader breaches.

KQL Query

let timeframe = 1d;
let resetThreshold = 3;
let correlationWindow = 1h;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName in~ (
      "Reset password (by admin)",
      "Reset user password",
      "Reset password"
  )
| where Result =~ "success"
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend ActorIp  = iff(
      isnotempty(tostring(InitiatedBy.user.ipAddress)),
      tostring(InitiatedBy.user.ipAddress),
      tostring(InitiatedBy.app.ipAddress))
| extend Actor      = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend TargetUpn  = tostring(TargetResources[0].userPrincipalName)
| where isnotempty(Actor) and isnotempty(TargetUpn) and Actor != TargetUpn
| summarize
    ResetCount     = dcount(TargetUpn),
    TargetAccounts = make_set(TargetUpn, 20),
    FirstReset     = min(TimeGenerated),
    LastReset      = max(TimeGenerated),
    ActorIp        = take_any(ActorIp)
    by Actor, TimeBucket = bin(TimeGenerated, correlationWindow)
| where ResetCount >= resetThreshold
| extend AccountName      = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor)
| extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
| project
    TimeBucket,
    Actor,
    AccountName,
    AccountUPNSuffix,
    ResetCount,
    TargetAccounts,
    FirstReset,
    LastReset,
    ActorIp
| sort by ResetCount desc

Analytic Rule Definition

id: 0490ce45-3f01-48e2-a042-3ba0c3e559bc
name: Bulk admin-initiated password reset across multiple accounts
description: |
  Identifies a single actor resetting passwords for three or more distinct accounts
  within one hour. Bulk admin-initiated resets in rapid succession suggest active
  account takeover before detection.
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
tactics:
  - CredentialAccess
  - Persistence
relevantTechniques:
  - T1098
  - T1078.004
query: |
  let timeframe = 1d;
  let resetThreshold = 3;
  let correlationWindow = 1h;
  AuditLogs
  | where TimeGenerated >= ago(timeframe)
  | where OperationName in~ (
        "Reset password (by admin)",
        "Reset user password",
        "Reset password"
    )
  | where Result =~ "success"
  | extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
  | extend ActorApp = tostring(InitiatedBy.app.displayName)
  | extend ActorIp  = iff(
        isnotempty(tostring(InitiatedBy.user.ipAddress)),
        tostring(InitiatedBy.user.ipAddress),
        tostring(InitiatedBy.app.ipAddress))
  | extend Actor      = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
  | extend TargetUpn  = tostring(TargetResources[0].userPrincipalName)
  | where isnotempty(Actor) and isnotempty(TargetUpn) and Actor != TargetUpn
  | summarize
      ResetCount     = dcount(TargetUpn),
      TargetAccounts = make_set(TargetUpn, 20),
      FirstReset     = min(TimeGenerated),
      LastReset      = max(TimeGenerated),
      ActorIp        = take_any(ActorIp)
      by Actor, TimeBucket = bin(TimeGenerated, correlationWindow)
  | where ResetCount >= resetThreshold
  | extend AccountName      = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor)
  | extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
  | project
      TimeBucket,
      Actor,
      AccountName,
      AccountUPNSuffix,
      ResetCount,
      TargetAccounts,
      FirstReset,
      LastReset,
      ActorIp
  | sort by ResetCount desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Actor
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ActorIp
version: 1.0.0
metadata:
    source:
        kind: Community
    author:
        name: descambiado
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Protection", "Identity" ]

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/AuditLogs/BulkPasswordResetByActor.yaml