Elastic medium stable kql

Entra ID User Sign-in Brute Force Attempted

Identifies potential brute-force attacks targeting user accounts by analyzing failed sign-in patterns in Microsoft Entra ID Sign-In Logs. This detection focuses on a high volume of failed interactive or non-interactive authentication attempts within a short time window, often indicative of password spraying, credential stuffing, or password guessing. Adversaries may use these techniques to gain unauthorized access to applications integrated with Entra ID or to compromise valid user accounts.

View Source

Detection Logic

from logs-azure.signinlogs-*

// Define a time window for grouping and maintain the original event timestamp
| eval Esql.time_window_date_trunc = date_trunc(15 minutes, @timestamp)

// Filter relevant failed authentication events with specific error codes
| where data_stream.dataset == "azure.signinlogs"
    and event.category == "authentication"
    and azure.signinlogs.category in ("NonInteractiveUserSignInLogs", "SignInLogs")
    and event.outcome == "failure"
    and azure.signinlogs.properties.authentication_requirement == "singleFactorAuthentication"
    and azure.signinlogs.properties.status.error_code in (
        50034,  // UserAccountNotFound
        50126,  // InvalidUsernameOrPassword
        50055,  // PasswordExpired
        50056,  // InvalidPassword
        50057,  // UserDisabled
        50064,  // CredentialValidationFailure
        50076,  // MFARequiredButNotPassed
        50079,  // MFARegistrationRequired
        50105,  // EntitlementGrantsNotFound
        70000,  // InvalidGrant
        70008,  // ExpiredOrRevokedRefreshToken
        70043,  // BadTokenDueToSignInFrequency
        80002,  // OnPremisePasswordValidatorRequestTimedOut
        80005,  // OnPremisePasswordValidatorUnpredictableWebException
        50144,  // InvalidPasswordExpiredOnPremPassword
        50135,  // PasswordChangeCompromisedPassword
        50142,  // PasswordChangeRequiredConditionalAccess
        120000, // PasswordChangeIncorrectCurrentPassword
        120002, // PasswordChangeInvalidNewPasswordWeak
        120020  // PasswordChangeFailure
    )
    and azure.signinlogs.properties.user_principal_name is not null and azure.signinlogs.properties.user_principal_name != ""
    and user_agent.original != "Mozilla/5.0 (compatible; MSAL 1.0) PKeyAuth/1.0"
    and source.`as`.organization.name != "MICROSOFT-CORP-MSN-as-BLOCK"
| stats
    Esql.azure_signinlogs_properties_authentication_requirement_values = values(azure.signinlogs.properties.authentication_requirement),
    Esql.azure_signinlogs_properties_app_id_values = values(azure.signinlogs.properties.app_id),
    Esql.azure_signinlogs_properties_app_display_name_values = values(azure.signinlogs.properties.app_display_name),
    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_conditional_access_status_values = values(azure.signinlogs.properties.conditional_access_status),
    Esql.azure_signinlogs_properties_device_detail_browser_values = values(azure.signinlogs.properties.device_detail.browser),
    Esql.azure_signinlogs_properties_device_detail_device_id_values = values(azure.signinlogs.properties.device_detail.device_id),
    Esql.azure_signinlogs_properties_device_detail_operating_system_values = values(azure.signinlogs.properties.device_detail.operating_system),
    Esql.azure_signinlogs_properties_incoming_token_type_values = values(azure.signinlogs.properties.incoming_token_type),
    Esql.azure_signinlogs_properties_risk_state_values = values(azure.signinlogs.properties.risk_state),
    Esql.azure_signinlogs_properties_session_id_values = values(azure.signinlogs.properties.session_id),
    Esql.azure_signinlogs_properties_user_id_values = values(azure.signinlogs.properties.user_id),
    Esql_priv.azure_signinlogs_properties_user_principal_name_values = values(azure.signinlogs.properties.user_principal_name),
    Esql.azure_signinlogs_result_description_values = values(azure.signinlogs.result_description),
    Esql.azure_signinlogs_result_signature_values = values(azure.signinlogs.result_signature),
    Esql.azure_signinlogs_result_type_values = values(azure.signinlogs.result_type),

    Esql.azure_signinlogs_properties_user_id_count_distinct = count_distinct(azure.signinlogs.properties.user_id),
    Esql.azure_signinlogs_properties_user_id_list = values(azure.signinlogs.properties.user_id),
    Esql.azure_signinlogs_result_description_values_all = values(azure.signinlogs.result_description),
    Esql.azure_signinlogs_result_description_count_distinct = count_distinct(azure.signinlogs.result_description),
    Esql.azure_signinlogs_properties_status_error_code_values = values(azure.signinlogs.properties.status.error_code),
    Esql.azure_signinlogs_properties_status_error_code_count_distinct = count_distinct(azure.signinlogs.properties.status.error_code),
    Esql.azure_signinlogs_properties_incoming_token_type_values_all = values(azure.signinlogs.properties.incoming_token_type),
    Esql.azure_signinlogs_properties_app_display_name_values_all = values(azure.signinlogs.properties.app_display_name),
    Esql.source_ip_values = values(source.ip),
    Esql.source_ip_count_distinct = count_distinct(source.ip),
    Esql.source_as_organization_name_values = values(source.`as`.organization.name),
    Esql.source_geo_country_name_values = values(source.geo.country_name),
    Esql.source_geo_country_name_count_distinct = count_distinct(source.geo.country_name),
    Esql.source_as_organization_name_count_distinct = count_distinct(source.`as`.organization.name),
    Esql.timestamp_first_seen = min(@timestamp),
    Esql.timestamp_last_seen = max(@timestamp),
    Esql.event_count = count()
by Esql.time_window_date_trunc
| eval
    Esql.duration_seconds = date_diff("seconds", Esql.timestamp_first_seen, Esql.timestamp_last_seen),
    Esql.brute_force_type = case(
        Esql.azure_signinlogs_properties_user_id_count_distinct >= 10 and Esql.event_count >= 30 and Esql.azure_signinlogs_result_description_count_distinct <= 3
            and Esql.source_ip_count_distinct >= 5
            and Esql.duration_seconds <= 600
            and Esql.azure_signinlogs_properties_user_id_count_distinct > Esql.source_ip_count_distinct,
        "credential_stuffing",

        Esql.azure_signinlogs_properties_user_id_count_distinct >= 15 and Esql.azure_signinlogs_result_description_count_distinct == 1 and Esql.event_count >= 15 and Esql.duration_seconds <= 1800,
        "password_spraying",

        (Esql.azure_signinlogs_properties_user_id_count_distinct == 1 and Esql.azure_signinlogs_result_description_count_distinct == 1 and Esql.event_count >= 30 and Esql.duration_seconds <= 300)
            or (Esql.azure_signinlogs_properties_user_id_count_distinct <= 3 and Esql.source_ip_count_distinct > 30 and Esql.event_count >= 100),
        "password_guessing",

        "other"
    )
| keep
    Esql.time_window_date_trunc,
    Esql.brute_force_type,
    Esql.duration_seconds,
    Esql.event_count,
    Esql.timestamp_first_seen,
    Esql.timestamp_last_seen,
    Esql.azure_signinlogs_properties_user_id_count_distinct,
    Esql.azure_signinlogs_properties_user_id_list,
    Esql.azure_signinlogs_result_description_values_all,
    Esql.azure_signinlogs_result_description_count_distinct,
    Esql.azure_signinlogs_properties_status_error_code_count_distinct,
    Esql.azure_signinlogs_properties_status_error_code_values,
    Esql.azure_signinlogs_properties_incoming_token_type_values_all,
    Esql.azure_signinlogs_properties_app_display_name_values_all,
    Esql.source_ip_values,
    Esql.source_ip_count_distinct,
    Esql.source_as_organization_name_values,
    Esql.source_geo_country_name_values,
    Esql.source_geo_country_name_count_distinct,
    Esql.source_as_organization_name_count_distinct,
    Esql.azure_signinlogs_properties_authentication_requirement_values,
    Esql.azure_signinlogs_properties_app_id_values,
    Esql.azure_signinlogs_properties_app_display_name_values,
    Esql.azure_signinlogs_properties_resource_id_values,
    Esql.azure_signinlogs_properties_resource_display_name_values,
    Esql.azure_signinlogs_properties_conditional_access_status_values,
    Esql.azure_signinlogs_properties_device_detail_browser_values,
    Esql.azure_signinlogs_properties_device_detail_device_id_values,
    Esql.azure_signinlogs_properties_device_detail_operating_system_values,
    Esql.azure_signinlogs_properties_incoming_token_type_values,
    Esql.azure_signinlogs_properties_risk_state_values,
    Esql.azure_signinlogs_properties_session_id_values,
    Esql.azure_signinlogs_properties_user_id_values,
    Esql_priv.azure_signinlogs_properties_user_principal_name_values,
    Esql.azure_signinlogs_result_description_values,
    Esql.azure_signinlogs_result_signature_values,
    Esql.azure_signinlogs_result_type_values
| where Esql.brute_force_type != "other"

False Positives

  • Automated processes that attempt to authenticate using expired credentials or have misconfigured authentication settings may lead to false positives.

Field Validations

Loading…

Comments (0)

Loading comments...