Techniques
Sample rules
Microsoft Entra ID Concurrent Sign-Ins with Suspicious Properties
- source: elastic
- technicques:
- T1528
Description
Identifies concurrent azure signin events for the same user and from multiple sources, and where one of the authentication event has some suspicious properties often associated to DeviceCode and OAuth phishing. Adversaries may steal Refresh Tokens (RTs) via phishing to bypass multi-factor authentication (MFA) and gain unauthorized access to Azure resources.
Detection logic
FROM logs-azure.signinlogs* metadata _id, _version, _index
// the rule is scheduled to run every hour and looks for events occured during last 1 hour.
| where @timestamp > NOW() - 1 hours
| where event.dataset == "azure.signinlogs" and source.ip is not null and azure.signinlogs.identity is not null and to_lower(event.outcome) == "success"
| keep @timestamp, azure.signinlogs.identity, source.ip, azure.signinlogs.properties.authentication_requirement, azure.signinlogs.properties.app_id, azure.signinlogs.properties.resource_display_name, azure.signinlogs.properties.authentication_protocol, azure.signinlogs.properties.app_display_name
// devicecode authentication no MFA
| eval device_code = case(azure.signinlogs.properties.authentication_protocol == "deviceCode" and azure.signinlogs.properties.authentication_requirement != "multiFactorAuthentication", azure.signinlogs.identity, null),
// potential Visual Studio Code OAuth code phish - sign-in events with client set to Visual Studio Code
visual_studio = case(azure.signinlogs.properties.app_id == "aebc6443-996d-45c2-90f0-388ff96faa56" and azure.signinlogs.properties.resource_display_name == "Microsoft Graph", azure.signinlogs.identity, null),
// Other sign-in events
other = case(azure.signinlogs.properties.authentication_protocol != "deviceCode" and azure.signinlogs.properties.app_id != "aebc6443-996d-45c2-90f0-388ff96faa56", azure.signinlogs.identity, null)
| stats total = COUNT(*), device_code_count = COUNT_DISTINCT(device_code), vsc = count_distinct(visual_studio), other_count = COUNT_DISTINCT(other), src_ip = COUNT_DISTINCT(source.ip), ips = values(source.ip), clients = values(azure.signinlogs.properties.app_display_name), resources = VALUES(azure.signinlogs.properties.resource_display_name), auth_requirement = VALUES(azure.signinlogs.properties.authentication_requirement) by azure.signinlogs.identity
// 2 unique source.ip for same account - which may indicate the presence 2 sign-ins one by the adversary and the other by the victim
| where src_ip >= 2 and (device_code_count > 0 or vsc >0)