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.
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 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
References (6)
- 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://www.justice.gov/opa/pr/nigerian-national-sentenced-prison-compromising-email-accounts-business-email-compromise
- 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
Unlock Pro Content
Get the full detection package for T1586 including response playbook, investigation guide, and atomic red team tests.