Suspicious Microsoft OAuth Flow via Auth Broker to DRS

Last updated a month ago on 2025-07-16
Created 4 months ago on 2025-04-30

About

Identifies separate OAuth authorization flows in Microsoft Entra ID where the same user principal and session ID are observed across multiple IP addresses within a 5-minute window. These flows involve the Microsoft Authentication Broker (MAB) as the client application and the Device Registration Service (DRS) as the target resource. This pattern is highly indicative of OAuth phishing activity, where an adversary crafts a legitimate Microsoft login URL to trick a user into completing authentication and sharing the resulting authorization code, which is then exchanged for an access and refresh token by the attacker.
Tags
Domain: CloudDomain: IdentityData Source: AzureData Source: Entra IDData Source: Entra ID Sign-in LogsUse Case: Identity and Access AuditUse Case: Threat DetectionTactic: Initial AccessLanguage: esql
Severity
high
Risk Score
73
MITRE ATT&CK™

Initial Access (TA0001)(opens in a new tab or window)

False Positive Examples
Legitimate device registrations using Microsoft Authentication Broker may occur during corporate enrollment scenarios or bulk provisioning, but it is uncommon for multiple source IPs to register the same identity across Microsoft Graph, Device Registration Service (DRS), and Azure Active Directory (AAD) in a short time span.
License
Elastic License v2(opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

azure(opens in a new tab or window)

Query
from logs-azure.signinlogs* metadata _id, _version, _index
| where
    event.dataset == "azure.signinlogs" and
    event.outcome == "success" and
    azure.signinlogs.properties.user_type == "Member" and
    azure.signinlogs.identity is not null and
    azure.signinlogs.properties.user_principal_name is not null and
    source.address is not null and
    azure.signinlogs.properties.app_id == "29d9ed98-a469-4536-ade2-f981bc1d605e" and  // MAB
    azure.signinlogs.properties.resource_id == "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9"  // DRS

| eval
    Esql.time_window_date_trunc = date_trunc(30 minutes, @timestamp),
    Esql.azure_signinlogs_properties_session_id = azure.signinlogs.properties.session_id,
    Esql.is_browser_case = case(
        to_lower(azure.signinlogs.properties.device_detail.browser) rlike "(chrome|firefox|edge|safari).*", 1, 0
    )

| stats
    Esql_priv.azure_signinlogs_properties_user_display_name_values = values(azure.signinlogs.properties.user_display_name),
    Esql_priv.azure_signinlogs_properties_user_principal_name_values = values(azure.signinlogs.properties.user_principal_name),
    Esql.azure_signinlogs_properties_session_id_values = values(azure.signinlogs.properties.session_id),
    Esql.azure_signinlogs_properties_unique_token_identifier_values = values(azure.signinlogs.properties.unique_token_identifier),

    Esql.source_geo_city_name_values = values(source.geo.city_name),
    Esql.source_geo_country_name_values = values(source.geo.country_name),
    Esql.source_geo_region_name_values = values(source.geo.region_name),
    Esql.source_address_values = values(source.address),
    Esql.source_address_count_distinct = count_distinct(source.address),
    Esql.source_as_organization_name_values = values(source.`as`.organization.name),

    Esql.azure_signinlogs_properties_authentication_protocol_values = values(azure.signinlogs.properties.authentication_protocol),
    Esql.azure_signinlogs_properties_authentication_requirement_values = values(azure.signinlogs.properties.authentication_requirement),
    Esql.azure_signinlogs_properties_is_interactive_values = values(azure.signinlogs.properties.is_interactive),

    Esql.azure_signinlogs_properties_incoming_token_type_values = values(azure.signinlogs.properties.incoming_token_type),
    Esql.azure_signinlogs_properties_token_protection_status_details_sign_in_session_status_values = values(azure.signinlogs.properties.token_protection_status_details.sign_in_session_status),
    Esql.azure_signinlogs_properties_session_id_count_distinct = count_distinct(azure.signinlogs.properties.session_id),
    Esql.azure_signinlogs_properties_app_display_name_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_id_values = values(azure.signinlogs.properties.resource_id),
    Esql.azure_signinlogs_properties_resource_display_name_values = values(azure.signinlogs.properties.resource_display_name),

    Esql.azure_signinlogs_properties_app_owner_tenant_id_values = values(azure.signinlogs.properties.app_owner_tenant_id),
    Esql.azure_signinlogs_properties_resource_owner_tenant_id_values = values(azure.signinlogs.properties.resource_owner_tenant_id),

    Esql.azure_signinlogs_properties_conditional_access_status_values = values(azure.signinlogs.properties.conditional_access_status),
    Esql.azure_signinlogs_properties_risk_state_values = values(azure.signinlogs.properties.risk_state),
    Esql.azure_signinlogs_properties_risk_level_aggregated_values = values(azure.signinlogs.properties.risk_level_aggregated),

    Esql.azure_signinlogs_properties_device_detail_browser_values = values(azure.signinlogs.properties.device_detail.browser),
    Esql.azure_signinlogs_properties_device_detail_operating_system_values = values(azure.signinlogs.properties.device_detail.operating_system),
    Esql.user_agent_original_values = values(user_agent.original),
    Esql.is_browser_case_max = max(Esql.is_browser_case),

    Esql.event_count = count(*)
  by
    Esql.time_window_date_trunc,
    azure.signinlogs.properties.user_principal_name,
    azure.signinlogs.properties.session_id

| keep
    Esql.time_window_date_trunc,
    Esql_priv.azure_signinlogs_properties_user_display_name_values,
    Esql_priv.azure_signinlogs_properties_user_principal_name_values,
    Esql.azure_signinlogs_properties_session_id_values,
    Esql.azure_signinlogs_properties_unique_token_identifier_values,
    Esql.source_geo_city_name_values,
    Esql.source_geo_country_name_values,
    Esql.source_geo_region_name_values,
    Esql.source_address_values,
    Esql.source_address_count_distinct,
    Esql.source_as_organization_name_values,
    Esql.azure_signinlogs_properties_authentication_protocol_values,
    Esql.azure_signinlogs_properties_authentication_requirement_values,
    Esql.azure_signinlogs_properties_is_interactive_values,
    Esql.azure_signinlogs_properties_incoming_token_type_values,
    Esql.azure_signinlogs_properties_token_protection_status_details_sign_in_session_status_values,
    Esql.azure_signinlogs_properties_session_id_count_distinct,
    Esql.azure_signinlogs_properties_app_display_name_values,
    Esql.azure_signinlogs_properties_app_id_values,
    Esql.azure_signinlogs_properties_resource_id_values,
    Esql.azure_signinlogs_properties_resource_display_name_values,
    Esql.azure_signinlogs_properties_app_owner_tenant_id_values,
    Esql.azure_signinlogs_properties_resource_owner_tenant_id_values,
    Esql.azure_signinlogs_properties_conditional_access_status_values,
    Esql.azure_signinlogs_properties_risk_state_values,
    Esql.azure_signinlogs_properties_risk_level_aggregated_values,
    Esql.azure_signinlogs_properties_device_detail_browser_values,
    Esql.azure_signinlogs_properties_device_detail_operating_system_values,
    Esql.user_agent_original_values,
    Esql.is_browser_case_max,
    Esql.event_count

| where
    Esql.source_address_count_distinct >= 2 and
    Esql.azure_signinlogs_properties_session_id_count_distinct == 1 and
    Esql.is_browser_case_max >= 1 and
    Esql.event_count >= 2

Install detection rules in Elastic Security

Detect Suspicious Microsoft OAuth Flow via Auth Broker to DRS in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(opens in a new tab or window).