This hunt detects adversaries leveraging compromised credentials to authenticate privileged identities via legacy protocols that often bypass modern security controls like Conditional Access policies. Proactively hunting for these events in Azure Sentinel is critical because legacy authentication methods lack robust security features, creating a high-risk entry point for attackers aiming to establish persistence or escalate privileges within the environment.
let timeframe = 30d;
let LegacyProtocols = dynamic([
"Exchange ActiveSync",
"IMAP4",
"MAPI over HTTP",
"POP3",
"SMTP Auth",
"Authenticated SMTP",
"Other clients"
]);
let PrivilegedUsers = IdentityInfo
| where isnotempty(AssignedRoles) or isnotempty(PrivilegedEntraPimRoles)
| summarize arg_max(Timestamp, AccountUpn) by AccountObjectId;
SigninLogs
| where TimeGenerated > ago(timeframe)
| where ResultType == "0"
| where ClientAppUsed in~ (LegacyProtocols)
| join kind=inner PrivilegedUsers on $left.UserId == $right.AccountObjectId
| extend
AccountName = tostring(split(UserPrincipalName, "@")[0]),
AccountUPNSuffix = tostring(split(UserPrincipalName, "@")[1])
| summarize
SignInAttempts = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
ClientApps = make_set(ClientAppUsed),
AuthRequirements = make_set(AuthenticationRequirement),
LastIP = tostring(arg_max(TimeGenerated, IPAddress))
by UserPrincipalName, AccountName, AccountUPNSuffix, UserId, AccountUpn, AppDisplayName
| project
UserPrincipalName, AccountName, AccountUPNSuffix, AccountUpn,
SignInAttempts, FirstSeen, LastSeen, ClientApps, AuthRequirements,
LastIP, AppDisplayName
| sort by SignInAttempts desc
id: 06184800-6d66-46ce-aabd-7e17e9cf3fb0
name: Privileged identities authenticating via legacy protocols
description: |
Identifies successful sign-ins by privileged accounts using legacy clients
such as Exchange ActiveSync, IMAP4, POP3, SMTP Auth, MAPI over HTTP, or other
legacy clients. These protocols may bypass modern Conditional Access and
MFA controls.
description-detailed: |
Joins privileged accounts from IdentityInfo (AssignedRoles /
PrivilegedEntraPimRoles) against SigninLogs where ClientAppUsed matches
a legacy protocol and the sign-in succeeded. Review each hit against
Conditional Access 'legacy authentication' blocking policies and
block-legacy-auth settings in Exchange Online; disable the legacy
protocol for any account that does not require it. Protocol names here
follow the SigninLogs ClientAppUsed values used by existing repo
legacy-auth detection patterns.
References:
- https://learn.microsoft.com/entra/identity/conditional-access/block-legacy-authentication
- https://attack.mitre.org/techniques/T1078/004/
- https://attack.mitre.org/techniques/T1110/003/
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- IdentityInfo
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
tactics:
- InitialAccess
- CredentialAccess
relevantTechniques:
- T1078.004
- T1110.003
query: |
let timeframe = 30d;
let LegacyProtocols = dynamic([
"Exchange ActiveSync",
"IMAP4",
"MAPI over HTTP",
"POP3",
"SMTP Auth",
"Authenticated SMTP",
"Other clients"
]);
let PrivilegedUsers = IdentityInfo
| where isnotempty(AssignedRoles) or isnotempty(PrivilegedEntraPimRoles)
| summarize arg_max(Timestamp, AccountUpn) by AccountObjectId;
SigninLogs
| where TimeGenerated > ago(timeframe)
| where ResultType == "0"
| where ClientAppUsed in~ (LegacyProtocols)
| join kind=inner PrivilegedUsers on $left.UserId == $right.AccountObjectId
| extend
AccountName = tostring(split(UserPrincipalName, "@")[0]),
AccountUPNSuffix = tostring(split(UserPrincipalName, "@")[1])
| summarize
SignInAttempts = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
ClientApps = make_set(ClientAppUsed),
AuthRequirements = make_set(AuthenticationRequirement),
LastIP = tostring(arg_max(TimeGenerated, IPAddress))
by UserPrincipalName, AccountName, AccountUPNSuffix, UserId, AccountUpn, AppDisplayName
| project
UserPrincipalName, AccountName, AccountUPNSuffix, AccountUpn,
SignInAttempts, FirstSeen, LastSeen, ClientApps, AuthRequirements,
LastIP, AppDisplayName
| sort by SignInAttempts desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifie
| Sentinel Table | Notes |
|---|---|
IdentityInfo | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Here are 5 specific false positive scenarios for the “Privileged identities authenticating via legacy protocols” rule, along with targeted filtering strategies:
Scenario: Automated Exchange Mailbox Sync for Compliance Archiving
svc-compliance-archive) authenticates via Exchange ActiveSync (EAS) to pull messages into the legal hold repository every hour.svc-compliance-archive account when the client type is “ActiveSync” and the authentication method is “Modern Auth with Legacy Fallback.”Scenario: Scheduled PowerShell Script for Directory Health Checks
Exchange Online PowerShell Module. The script connects to the Exchange tenant via MAPI over HTTP (or legacy SMTP Auth) to validate mailbox quotas and generate health reports. This is run by a dedicated service account (svc-dir-health-check) from the internal DMZ subnet.10.20.50.x) and the user principal name matches the pattern svc-*. Additionally, filter out events occurring strictly between 02:00 and 04:00 UTC on weekdays.Scenario: Legacy On-Premise Printer Management System