Compromise Accounts
This detection identifies indicators of compromised accounts being leveraged against the organization, including credential stuffing attacks that transition from repeated failures to success, impossible travel anomalies where a single identity authenticates from geographically distant locations within an implausible timeframe, sign-ins from known hosting or anonymization infrastructure, and MFA bypass patterns consistent with session token theft or adversary-in-the-middle phishing toolkits such as Evilginx2 or Modlishka. Because T1586 is a PRE-ATT&CK technique occurring outside the victim environment, detections focus on the observable authentication artifacts generated when adversaries weaponize stolen credentials or session material against organizational identity providers including Azure AD, on-premises Active Directory, and SaaS application login flows.
What is T1586 Compromise Accounts?
Compromise Accounts (T1586) maps to the Resource Development tactic — the adversary is trying to establish resources they can use to support operations in MITRE ATT&CK.
This page provides production-ready detection logic for Compromise Accounts, covering the data sources and telemetry it touches: Azure Active Directory, Microsoft Entra ID. The queries below are rated high severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.
MITRE ATT&CK
- Tactic
- Resource Development
- Technique
- T1586 Compromise Accounts
- Canonical reference
- https://attack.mitre.org/techniques/T1586/
let timeWindow = 24h;
let failThreshold = 5;
let travelWindowMinutes = 60;
// --- Signal 1: Credential stuffing — many failures then success from multiple IPs ---
let CredentialStuffing = AADSignInLogs
| where TimeGenerated > ago(timeWindow)
| where ResultType != "50053" and ResultType != "50076" // exclude locked-out and MFA-required noise
| summarize
FailureCount = countif(ResultType != "0"),
SuccessCount = countif(ResultType == "0"),
UniqueIPs = dcount(IPAddress),
IPList = make_set(IPAddress, 10),
Apps = make_set(AppDisplayName, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by UserPrincipalName
| where FailureCount >= failThreshold and SuccessCount >= 1
| extend DetectionType = "CredentialStuffing",
RiskScore = iff(UniqueIPs > 5, 90, iff(UniqueIPs > 2, 75, 60));
// --- Signal 2: Impossible travel — successful logins from 2+ countries within 60 minutes ---
let ImpossibleTravel = AADSignInLogs
| where TimeGenerated > ago(timeWindow)
| where ResultType == "0"
| where isnotempty(Location)
| extend Country = tostring(LocationDetails.countryOrRegion)
| where isnotempty(Country)
| summarize
Countries = make_set(Country, 20),
IPList = make_set(IPAddress, 10),
Apps = make_set(AppDisplayName, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
SuccessCount = count()
by UserPrincipalName, bin(TimeGenerated, 1h)
| where array_length(Countries) > 1
| extend DetectionType = "ImpossibleTravel", RiskScore = 85,
FailureCount = 0, UniqueIPs = array_length(IPList);
// --- Signal 3: Successful login from Tor exit node or known bulletproof hosting ASN ---
let SuspiciousASN = AADSignInLogs
| where TimeGenerated > ago(timeWindow)
| where ResultType == "0"
| extend ASN = tostring(todynamic(NetworkLocationDetails)[0].networkNames)
| where ASN has_any ("M247", "Frantech", "Sharktech", "Psychz", "Quasi Networks",
"FranTech", "Alexhost", "ITL-Bulgaria", "Serverius", "Combahton")
or IPAddress matches regex @"^185\.(220|129|220)\."
| summarize
IPList = make_set(IPAddress, 10),
Apps = make_set(AppDisplayName, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
SuccessCount = count(),
Countries = make_set(tostring(LocationDetails.countryOrRegion), 5)
by UserPrincipalName
| extend DetectionType = "SuspiciousHostingASN", RiskScore = 80,
FailureCount = 0, UniqueIPs = array_length(IPList);
// --- Union all signals ---
CredentialStuffing
| union ImpossibleTravel
| union SuspiciousASN
| project
TimeGenerated = LastSeen,
UserPrincipalName,
DetectionType,
RiskScore,
FailureCount,
SuccessCount,
UniqueIPs,
IPList,
Apps,
Countries,
FirstSeen,
LastSeen
| sort by RiskScore desc, TimeGenerated desc Detects three distinct compromise account patterns against Azure AD: (1) credential stuffing where 5+ authentication failures from multiple IPs precede a successful login, (2) impossible travel where a single identity successfully authenticates from two or more countries within a 60-minute window, and (3) successful logins sourced from known bulletproof hosting or anonymization ASNs associated with threat actor infrastructure. Results are scored by risk level to prioritize analyst triage.
Data Sources
Required Tables
False Positives
- Legitimate corporate VPN services routing authentication through shared exit nodes may match ASN-based detection; allowlist known corporate egress IP ranges
- Traveling employees authenticating from multiple countries within a short window (e.g. connecting through an airline hub) will trigger impossible travel; cross-reference with HR travel records and conditional access named locations
- Shared service accounts used by automation platforms (CI/CD, monitoring) may generate high failure counts if misconfigured credentials are in rotation before being corrected; baseline service account authentication patterns
- Password reset self-service workflows may generate multiple ResultType failures before a successful reset, mimicking credential stuffing; filter on UserType and correlate with SSPR audit events in AuditLogs
Sigma rule & cross-platform mapping
The detection logic for Compromise Accounts (T1586) above is provided in a vendor-neutral
form so you can deploy it on any SIEM. The same logic is shipped here as native
KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the
following logsource:
logsource:
product: azure Browse the community-maintained Sigma rules for this technique:
Platform-specific guides for T1586
References (5)
- https://attack.mitre.org/techniques/T1586/
- https://www.microsoft.com/en-us/security/blog/2022/03/22/dev-0537-criminal-actor-targeting-organizations-for-data-exfiltration-and-destruction/
- https://learn.microsoft.com/en-us/azure/active-directory/identity-protection/overview-identity-protection
- https://learn.microsoft.com/en-us/microsoft-365/security/office-365-security/anti-phishing-protection
- https://www.cisa.gov/sites/default/files/publications/Multifactor-Authentication-Fact-Sheet-FINAL.pdf
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.
- Test 1Simulate credential stuffing authentication pattern using PowerShell against Azure AD
Expected signal: AADSignInLogs will show 6 ResultType != 0 events followed by 1 ResultType == 0 event for the test UPN from the same source IP within a short time window. RiskState may update in AADRiskyUsers within 15-30 minutes.
- Test 2Simulate legacy protocol authentication bypass against Exchange Online (SMTP AUTH)
Expected signal: AADSignInLogs entry with ClientAppUsed='Authenticated SMTP', AuthenticationRequirement='singleFactorAuthentication', ResultType=0 for the test account. This event will NOT appear in modern auth logs, validating the legacy auth gap.
- Test 3Simulate account compromise indicators via failed then successful Windows network logon from multiple sources
Expected signal: Windows Security EventID 4625 (LogonType 3, SubStatus 0xC000006A = wrong password) six times followed by EventID 4624 (LogonType 3) once for the test account in the domain controller Security event log. Source workstation will be the executing host.
Unlock Pro Content
Get the full detection package for T1586 including response playbook, investigation guide, and atomic red team tests.