Query Details

13 SIGNIN Service Account Interactive Login

Query

id: c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f
name: "SigninLogs — Service Account Interactive Browser Sign-In"
version: 1.0.0
kind: Scheduled
description: |
  Detects service accounts and non-human identities performing interactive browser-based sign-ins. Service accounts should only authenticate via client credentials or managed identity flows. Interactive sessions indicate credential theft or an attacker manually leveraging stolen service account creds. MITRE ATT&CK: T1078 (Valid Accounts), T1078.004 (Cloud Accounts).
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT4H
queryPeriod: PT4H
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
relevantTechniques:
  - 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 serviceAccountKeywords = dynamic([
        "svc", "svc-", "svc.", "-svc", "_svc",
        "app-", "app_", "api-", "api_",
        "bot-", "bot_", "sys-", "sys_",
        "func-", "func_", "daemon", "automation",
        "robot", "service_", "service-", "noreply",
        "pipeline", "cicd", "ci-", "cd-"
    ]);
    SigninLogs
    | invoke _ExcludeAllowlistedIPs()
    | where TimeGenerated > ago(4h)
    | where ResultType == "0"
    | where IsInteractive == true
    | where ClientAppUsed == "Browser"
    | where UserPrincipalName has_any (serviceAccountKeywords)
    | project
        TimeGenerated,
        UserPrincipalName,
        IPAddress,
        Location,
        AppDisplayName,
        RiskLevel = RiskLevelDuringSignIn,
        CAStatus = ConditionalAccessStatus,
        UserAgent
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  AppDisplayName: AppDisplayName
  CAStatus: CAStatus
alertDetailsOverride:
  alertDisplayNameFormat: "Service Account Interactive Login — {{UserPrincipalName}} from {{IPAddress}}"
  alertDescriptionFormat: "Service account {{UserPrincipalName}} used a browser session from {{IPAddress}} — service accounts should never authenticate interactively. Possible credential theft."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT4H
    matchingMethod: AllEntities
    groupByEntities:
  - Account
  - IP



Explanation

This query is designed to detect suspicious activity involving service accounts and non-human identities that are performing interactive sign-ins using a web browser. Here's a simplified breakdown of what the query does:

  1. Purpose: The query aims to identify instances where service accounts, which should typically authenticate using non-interactive methods like client credentials or managed identity flows, are instead signing in interactively via a browser. This behavior could indicate credential theft or an attacker using stolen credentials.

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

  3. Frequency: The query runs every 4 hours and looks at sign-in logs from the past 4 hours.

  4. Network Allowlist: The query excludes sign-ins from trusted IP addresses or ranges specified in a "Network Allowlist" watchlist.

  5. Service Account Identification: It looks for user principal names (UPNs) that contain keywords typically associated with service accounts, such as "svc", "app", "bot", etc.

  6. Conditions: The query filters for successful sign-ins (ResultType "0") that are interactive (IsInteractive is true) and performed using a browser (ClientAppUsed is "Browser").

  7. Output: It projects relevant details such as the time of the sign-in, user principal name, IP address, location, application name, risk level during sign-in, conditional access status, and user agent.

  8. Alerting: If any such sign-ins are detected, an alert is generated with a high severity level. The alert includes details like the service account name and IP address used for the sign-in, suggesting possible credential theft.

  9. Incident Management: The query is configured to create incidents for detected events, with grouping enabled based on account and IP to manage related alerts efficiently.

Overall, this query is a security measure to monitor and alert on potentially unauthorized or risky use of service accounts in an organization's Azure environment.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SigninLogs

Keywords

SigninLogsAzureActiveDirectoryAccountIPAddressUserPrincipalNameIPAddressLocationAppDisplayNameRiskLevelConditionalAccessStatusUserAgentNetworkAllowlistServiceAccountKeywordsTimeGeneratedResultTypeIsInteractiveClientAppUsed

Operators

letmaterializeunionisfuzzyprinttakeprojecttostringwhereisnotemptytoscalarmatchesregexextendiffstrcatsummarizemake_listnotipv4_is_in_any_rangemv-applytotypeofonsplitipv4_comparemaxtointproject-awaydynamicSigninLogsinvokeagohas_anyproject

Actions