Query Details

05 SIGNIN Legacy Auth Brute Force

Query

id: a5b6c7d8-e9f0-4a1b-2c3d-4e5f6a7b8c9d
name: "SigninLogs — Legacy Authentication Brute Force (IMAP/POP/SMTP)"
version: 1.0.0
kind: Scheduled
description: |
  Detects brute force attacks using legacy authentication protocols (IMAP, POP3, SMTP, Exchange ActiveSync). Legacy auth bypasses MFA and Conditional Access — any sustained attack via these protocols warrants immediate investigation. MITRE ATT&CK: T1110 (Brute Force), T1550 (Use Alternate Authentication Material).
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT1H
queryPeriod: PT1H
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - DefenseEvasion
relevantTechniques:
  - T1110
  - T1550
query: |

    // ---- Network Allowlist (exclude trusted IPs / CIDR / ranges) --------------
    let _allow = materialize(union isfuzzy=true (print R="" | take 0), (_GetWatchlist('NetworkAllowlist') | project R = tostring(IPOrRange)) | where isnotempty(R));
    let _allowCIDR  = toscalar(_allow | where not(R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$') | extend R = iff(R has '/', R, strcat(R, '/32')) | summarize make_list(R));
    let _allowRange = toscalar(_allow | where R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$' | summarize make_list(R));
    let _ExcludeAllowlistedIPs = (T:(IPAddress:string)) {
        T
        | extend IPAddress = tostring(IPAddress)
        | where array_length(_allowCIDR) == 0 or isnull(ipv4_is_in_any_range(IPAddress, _allowCIDR)) or not(ipv4_is_in_any_range(IPAddress, _allowCIDR))
        | mv-apply _r = _allowRange to typeof(string) on (
            extend _lo = tostring(split(_r,'-')[0]), _hi = tostring(split(_r,'-')[1])
            | extend _inRange = ipv4_compare(IPAddress, _lo) >= 0 and ipv4_compare(IPAddress, _hi) <= 0
            | summarize _anyInRange = max(toint(_inRange)))
        | where isnull(_anyInRange) or _anyInRange == 0
        | project-away _anyInRange
    };
    // ---------------------------------------------------------------------------
    let failureThreshold = 10;
    let legacyProtocols  = dynamic([
        "IMAP", "POP", "SMTP", "Authenticated SMTP",
        "Exchange ActiveSync", "Other clients"]);
    SigninLogs
    | invoke _ExcludeAllowlistedIPs()
    | where TimeGenerated > ago(1h)
    | where ClientAppUsed in (legacyProtocols)
    | where ResultType != "0"
    | summarize
        FailedAttempts = count(),
        UniqueUsers    = dcount(UserPrincipalName),
        TargetedUsers  = make_set(UserPrincipalName, 50),
        Protocols      = make_set(ClientAppUsed),
        StartTime      = min(TimeGenerated),
        EndTime        = max(TimeGenerated),
        Locations      = make_set(Location, 5)
      by IPAddress
    | where FailedAttempts >= failureThreshold
    | project
        TimeGenerated = StartTime,
        IPAddress, FailedAttempts, UniqueUsers,
        TargetedUsers, Protocols, Locations, EndTime
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  FailedAttempts: FailedAttempts
  UniqueUsers: UniqueUsers
alertDetailsOverride:
  alertDisplayNameFormat: "Legacy Auth Brute Force from {{IPAddress}} — {{UniqueUsers}} accounts"
  alertDescriptionFormat: "{{FailedAttempts}} failed legacy auth attempts (IMAP/POP/SMTP) from {{IPAddress}}."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT1H
    matchingMethod: AllEntities
    groupByEntities:
  - IP



Explanation

This query is designed to detect brute force attacks using legacy authentication protocols such as IMAP, POP3, SMTP, and Exchange ActiveSync. These protocols can bypass modern security measures like Multi-Factor Authentication (MFA) and Conditional Access, making them a target for attackers. The query runs every hour and looks for failed sign-in attempts using these protocols.

Here's a simplified breakdown of what the query does:

  1. Network Allowlist: It first defines a list of trusted IP addresses or ranges (allowlist) that should be excluded from the analysis.

  2. Failure Threshold: It sets a threshold of 10 failed attempts to identify potential brute force attacks.

  3. Legacy Protocols: It specifies the legacy protocols to monitor.

  4. Data Filtering: The query filters sign-in logs from the last hour, excluding any attempts from the allowlisted IPs. It focuses on failed attempts using the specified legacy protocols.

  5. Data Aggregation: It aggregates data by IP address, counting the number of failed attempts, the number of unique users targeted, and the protocols used. It also records the time range and locations of these attempts.

  6. Alerting: If the number of failed attempts from a single IP address meets or exceeds the threshold, it generates an alert. The alert includes details like the number of failed attempts, the IP address, and the number of unique user accounts targeted.

  7. Incident Management: The query is configured to create an incident if an alert is triggered, with options for grouping related alerts by IP address.

Overall, this query helps security teams identify and respond to potential brute force attacks using legacy authentication methods, which are often less secure.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SigninLogs

Keywords

SigninLogsIPAddressUserPrincipalNameClientAppUsedLocation

Operators

letmaterializeunionisfuzzyprinttakeprojecttostringwhereisnotemptymatchesregexextendiffhasstrcatsummarizemake_listtoscalarnotarray_lengthisnullipv4_is_in_any_rangemv-applytotypeofonsplitipv4_comparemaxtointproject-awaydynamicinagoinvokesummarizecountdcountmake_setminmaxbyproject

Actions