Query Details

22 SIGNIN Fresh IP Multi Account Auth

Query

id: f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c
name: "SigninLogs — Fresh IP Authenticating Multiple Accounts (Compromised Proxy)"
version: 1.0.0
kind: Scheduled
description: |
  Detects an IP not seen in the prior 14 days that successfully authenticates 5 or more distinct user accounts within 2 hours. Indicates a newly-provisioned attack proxy or relay used with a purchased credential list. Complements the spray rule (failures) by catching pre-validated or successful spray runs. MITRE ATT&CK: T1110.003 (Password Spraying), T1078 (Valid Accounts).
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT2H
queryPeriod: P14D
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - InitialAccess
relevantTechniques:
  - T1110
  - T1078
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 lookback      = 2h;
    let baselineDays  = 14d;
    let minUsers      = 5;
    let KnownIPs =
        SigninLogs
        | invoke _ExcludeAllowlistedIPs()
        | where TimeGenerated between (ago(baselineDays) .. ago(lookback))
        | where ResultType == "0"
        | distinct IPAddress;
    SigninLogs
    | invoke _ExcludeAllowlistedIPs()
    | where TimeGenerated > ago(lookback)
    | where ResultType == "0"
    | where isnotempty(IPAddress)
    | join kind=leftanti KnownIPs on IPAddress
    | summarize
        UniqueUsers        = dcount(UserPrincipalName),
        AuthenticatedUsers = make_set(UserPrincipalName, 30),
        TotalLogins        = count(),
        Apps               = make_set(AppDisplayName, 5),
        Locations          = make_set(Location, 3),
        ClientTypes        = make_set(ClientAppUsed, 5),
        FirstSeen          = min(TimeGenerated),
        LastSeen           = max(TimeGenerated)
      by IPAddress
    | where UniqueUsers >= minUsers
    | project
        TimeGenerated = FirstSeen,
        IPAddress, UniqueUsers, TotalLogins,
        AuthenticatedUsers, Apps,
        Locations, ClientTypes, LastSeen
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  UniqueUsers: UniqueUsers
  TotalLogins: TotalLogins
alertDetailsOverride:
  alertDisplayNameFormat: "Fresh IP Mass Auth — {{IPAddress}} authenticated {{UniqueUsers}} accounts (not seen in 14d)"
  alertDescriptionFormat: "IP {{IPAddress}} (first seen in a 2h window — not in 14d baseline) successfully authenticated {{UniqueUsers}} distinct accounts — strong indicator of a newly-deployed attack proxy."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT2H
    matchingMethod: AllEntities
    groupByEntities:
  - IP



Explanation

This query is designed to detect suspicious activity in sign-in logs, specifically focusing on identifying potential attack proxies or relays. Here's a simplified breakdown of what it does:

  1. Purpose: The query aims to find IP addresses that have not been seen in the last 14 days but have successfully authenticated five or more distinct user accounts within a two-hour window. This behavior suggests the use of a newly-provisioned attack proxy or relay, potentially using a list of purchased credentials.

  2. Exclusions: It excludes IP addresses that are on a predefined allowlist (trusted IPs or ranges) to avoid false positives from known safe sources.

  3. Data Source: It uses data from Azure Active Directory's SigninLogs.

  4. Detection Logic:

    • It first identifies IP addresses that have successfully logged in during the past 14 days.
    • Then, it looks for IP addresses that have appeared in the last two hours but were not seen in the previous 14 days.
    • It checks if these new IP addresses have authenticated five or more distinct user accounts.
  5. Output: For each suspicious IP address, it provides details such as:

    • Number of unique users authenticated
    • Total logins
    • List of authenticated users
    • Applications accessed
    • Locations and client types used
    • First and last seen timestamps
  6. Alerting: If such an IP address is detected, an alert is generated with a high severity level, indicating a potential security threat. The alert includes details about the IP address and the number of accounts it authenticated.

  7. Incident Management: The query is set to create incidents for detected cases, with configurations for grouping related alerts and managing incident states.

Overall, this query helps in identifying and alerting on potential credential-based attacks using new IP addresses, enhancing security monitoring and response capabilities.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SigninLogs

Keywords

SigninLogsUserAccountsIPAddressLocationClientAppUsedAppDisplayName

Operators

unionprinttakeprojectwhereisnotemptymatchesregexextendhasstrcatsummarizemake_listtoscalariffmaterializeisfuzzylettostringarray_lengthisnullipv4_is_in_any_rangemv-applysplitipv4_comparemaxtointproject-awayagobetweendistinctinvokejoinkindleftantidcountmake_setcountminmaxbyproject

Actions