Techniques
Sample rules
Entra ID OAuth Device Code Flow with Concurrent Sign-ins
- source: elastic
- technicques:
- T1528
- T1566
Description
Identifies Entra ID device code authentication flows where multiple user agents are observed within the same session. This pattern is indicative of device code phishing, where an attacker’s polling client (e.g., Python script) and the victim’s browser both appear in the same authentication session. In legitimate device code flows, the user authenticates via browser while the requesting application polls for tokens - when these have distinctly different user agents (e.g., Python Requests vs Chrome), it may indicate the code was phished and redeemed by an attacker.
Detection logic
from logs-azure.signinlogs-* metadata _id, _version, _index
| where event.category == "authentication" and event.dataset == "azure.signinlogs" and
azure.signinlogs.properties.original_transfer_method == "deviceCodeFlow"
// Track events with deviceCode authentication protocol (browser auth) vs polling client
| eval is_device_code_auth = case(azure.signinlogs.properties.authentication_protocol == "deviceCode", 1, 0)
| stats Esql.count_logon = count(*),
Esql.device_code_auth_count = sum(is_device_code_auth),
Esql.timestamp_values = values(@timestamp),
Esql.source_ip_count_distinct = count_distinct(source.ip),
Esql.user_agent_count_distinct = count_distinct(user_agent.original),
Esql.user_agent_values = values(user_agent.original),
Esql.authentication_protocol_values = values(azure.signinlogs.properties.authentication_protocol),
Esql.azure_signinlogs_properties_client_app_values = values(azure.signinlogs.properties.app_display_name),
Esql.azure_signinlogs_properties_app_id_values = values(azure.signinlogs.properties.app_id),
Esql.azure_signinlogs_properties_resource_display_name_values = values(azure.signinlogs.properties.resource_display_name),
Esql.azure_signinlogs_properties_auth_requirement_values = values(azure.signinlogs.properties.authentication_requirement),
Esql.azure_signinlogs_properties_tenant_id = values(azure.tenant_id),
Esql.azure_signinlogs_properties_status_error_code_values = values(azure.signinlogs.properties.status.error_code),
Esql.message_values = values(message),
Esql.azure_signinlogs_properties_resource_id_values = values(azure.signinlogs.properties.resource_id),
Esql.source_ip_values = values(source.ip)
by azure.signinlogs.properties.session_id, azure.signinlogs.identity
// Require: 2+ events, at least one deviceCode auth protocol event, and either 2+ IPs or 2+ user agents
| where Esql.count_logon >= 2 and Esql.device_code_auth_count >= 1 and (Esql.source_ip_count_distinct >= 2 or Esql.user_agent_count_distinct >= 2)
| keep
Esql.*,
azure.signinlogs.properties.session_id,
azure.signinlogs.identity