T1589 Microsoft Sentinel · KQL

Detect Gather Victim Identity Information in Microsoft Sentinel

This detection identifies adversary attempts to enumerate victim identity information—credentials, email addresses, and employee names—through active probing of authentication services and monitoring of downstream indicators of OSINT-driven targeting. Since T1589 is a PRE-ATT&CK technique occurring largely outside victim infrastructure, detection focuses on second-order observable signals: anomalous username enumeration via Azure AD sign-in failures with differential error codes (e.g., UserNameDoesNotExist vs. InvalidPassword), Self-Service Password Reset (SSPR) flow abuse, high-volume authentication probing from single sources against multiple distinct accounts, and MFA method enumeration patterns. Groups such as LAPSUS$, Scattered Spider, and HEXANE have exploited these mechanisms to build target identity lists before launching phishing, credential stuffing, or social engineering campaigns.

MITRE ATT&CK

Tactic
Reconnaissance
Technique
T1589 Gather Victim Identity Information
Canonical reference
https://attack.mitre.org/techniques/T1589/

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let lookback = 1h;
let enumThreshold = 15;
// Primary: Azure AD username enumeration via differential auth error codes
let AzureADEnum = AADSignInLogs
| where TimeGenerated > ago(lookback)
| where ResultType in ("50034", "50053", "50055", "50057", "50072", "50076")
// 50034=UserNameDoesNotExist, 50053=AccountLocked, 50055=PasswordExpired, 50057=AccountDisabled
| extend 
    IsUsernameNotFound = iff(ResultType == "50034", 1, 0),
    IsLockedOrDisabled = iff(ResultType in ("50053", "50057"), 1, 0)
| summarize 
    TotalFailures = count(),
    UniqueUsernames = dcount(UserPrincipalName),
    UsernameNotFoundCount = sum(IsUsernameNotFound),
    LockedDisabledCount = sum(IsLockedOrDisabled),
    UserList = make_set(UserPrincipalName, 25),
    ErrorCodes = make_set(ResultType),
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated)
    by IPAddress, AppDisplayName, ResultDescription
| where UniqueUsernames >= enumThreshold
| extend 
    DurationMinutes = datetime_diff('minute', LastSeen, FirstSeen),
    EnumRate = round(todouble(UniqueUsernames) / iff(datetime_diff('minute', LastSeen, FirstSeen) == 0, 1, todouble(datetime_diff('minute', LastSeen, FirstSeen))), 2),
    SuspicionScore = case(
        UniqueUsernames >= 100, "Critical",
        UniqueUsernames >= 50 or UsernameNotFoundCount >= 30, "High",
        UniqueUsernames >= 15, "Medium",
        "Low"
    )
| project 
    DetectionTime = LastSeen,
    IPAddress,
    AppDisplayName,
    UniqueUsernames,
    TotalFailures,
    UsernameNotFoundCount,
    EnumRate,
    SuspicionScore,
    UserList,
    ErrorCodes,
    DurationMinutes
| order by UniqueUsernames desc;
// Secondary: SSPR abuse for identity enumeration
let SSPREnum = AuditLogs
| where TimeGenerated > ago(lookback)
| where OperationName in ("Reset password (self-service)", "Self-service password reset flow activity", "Verify email address phone number")
| where ResultReason contains "blocked" or ResultReason contains "failed" or ActivityDisplayName contains "verify"
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| where isnotempty(IPAddress)
| summarize
    SSPRAttempts = count(),
    UniqueTargets = dcount(tostring(TargetResources[0].userPrincipalName)),
    TargetList = make_set(tostring(TargetResources[0].userPrincipalName), 25),
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated)
    by IPAddress
| where UniqueTargets >= 10
| extend DetectionType = "SSPR_Enumeration", SuspicionScore = iff(UniqueTargets >= 25, "High", "Medium")
| project DetectionTime = LastSeen, IPAddress, UniqueTargets, SSPRAttempts, SuspicionScore, TargetList;
// Union both detection signals
AzureADEnum
| extend DetectionType = "Auth_Username_Enumeration"
| union (SSPREnum | extend UniqueUsernames = UniqueTargets, TotalFailures = SSPRAttempts, UserList = TargetList, UsernameNotFoundCount = 0, EnumRate = 0.0, ErrorCodes = dynamic(["SSPR"]), DurationMinutes = 0)
| order by SuspicionScore asc, UniqueUsernames desc
high severity medium confidence

Detects adversary username enumeration via two vectors: (1) Azure AD sign-in attempts producing differential error codes (50034=UserNameDoesNotExist vs. password errors) from a single source IP across 15+ distinct usernames within an hour, and (2) Self-Service Password Reset flow abuse targeting multiple unique accounts. Combines both signals to surface identity reconnaissance activity consistent with LAPSUS$, Scattered Spider, and HEXANE TTPs.

Data Sources

Azure Active DirectoryMicrosoft Entra ID

Required Tables

AADSignInLogsAuditLogs

False Positives & Tuning

  • Penetration testing engagements performing authorized username enumeration against Azure AD tenants
  • Misconfigured applications cycling through user lists for automated login (e.g., legacy SSO, misconfigured service accounts)
  • Employee self-service helpdesk tools that probe SSPR status for multiple users during bulk account operations
  • Password expiration notification systems contacting multiple accounts in rapid succession
  • IT onboarding scripts performing bulk account validation during directory synchronization
Download portable Sigma rule (.yml)

Other platforms for T1589


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Azure AD Username Enumeration via GetCredentialType API

    Expected signal: Azure AD Sign-in Logs (AADSignInLogs) will show ResultType=50034 (UserNameDoesNotExist) for non-existent accounts. Successful lookups may show ResultType=0 or MFA-related codes. Source IP will be the test machine's public IP. Check Azure AD portal under Monitoring > Sign-in Logs filtering by the test domain.

  2. Test 2On-Premises Active Directory Username Enumeration via Kerberos

    Expected signal: Windows Security Event ID 4625 with SubStatus 0xc0000064 (user does not exist) on domain controller for each non-existent username tested. Event ID 4625 with SubStatus 0xc000006a (wrong password) for valid usernames. Event ID 4771 with Status 0x6 on DCs running Kerberos logging. Check DC Security event logs filtering: EventID=4625 AND (SubStatus=0xc0000064 OR SubStatus=0xc0000072).

  3. Test 3SSPR Username Existence Probing via Azure AD Password Reset Flow

    Expected signal: Azure AD Audit Logs will contain SSPR-related entries under 'Self-service password reset flow activity' and 'Verify email address phone number'. Check Azure portal: Azure Active Directory > Monitoring > Audit Logs, filter Activity='Reset password (self-service)' or 'Self-service password management'. In Sentinel: AuditLogs | where OperationName contains 'password' | where TimeGenerated > ago(1h)

Unlock Pro Content

Get the full detection package for T1589 including response playbook, investigation guide, and atomic red team tests.

Response PlaybookInvestigation GuideHunting QueriesAtomic Red Team TestsTuning Guidance

Related Detections