compromise_suspects
Identifies probable account takeovers in Entra ID sign-in logs by flagging password-spray breaches, foreign sign-ins, and Microsoft risky users for triage.
Instructions
Accounts that were probably taken over, from one walk of the sign-in log.
The success-side counterpart of signin_failure_stats' spray suspects.
Two findings, both from EXTERNAL source IPs (outside the address ranges
in ENTRAADM_SITES_INI; without that file every IP counts as external):
spray_breaches(severityhigh): an IP with credential failures (wrong password, unknown, locked or disabled account -- not MFA prompts, session interrupts or Conditional Access blocks, which follow a correct password) formin_spray_usersor more distinct accounts in the window AND a successful sign-in as at least one account. Without internal ranges (ENTRAADM_SITES_INI unset or unreadable,internal_ranges_known: false) these aremediumwith anote: the tenant's own NAT egress can look like a spray source. One row per account and IP:failed_distinct_usersfrom that IP, first/last success, client apps, countries,legacy_auth. The strongest signal: a spray that found a working password.foreign_successes: successes from a country outside ENTRAADM_HOME_COUNTRIES (assigned ISO 3166-1 alpha-2 codes, e.g. "JP"; one invalid code disables this list, seehome_countries.error), grouped per account with countries, the most used IPs (10, with counts), client apps, first/last, andhome_success_in_window(the account also signed in from a home country or an internal IP in the window; null when not seen in a capped scan, since the unread part may hold it).highwhen a legacy protocol was used (password only, no MFA) or the account is also a highspray_breachesentry;mediumotherwise. Unset home countries:home_countries.configuredis false and this list stays empty -- nothing is guessed. A success with no country is never called foreign.microsoft_risky_users: Microsoft's own verdicts from Identity Protection, which needs Entra ID P2 and the optionalIdentityRiskyUser.Read.Allpermission. A cheap probe (cached per tenant) decides: without P2 the section is{"available": false, "reason": "not_licensed" | "hidden" | "no_permission" | "error"}and nothing else changes -- never read it as "no risky users". With P2: open (atRisk / confirmedCompromised)highand, separately,mediumrisky users (50 each,*_capped), each marked when also inspray_breaches/foreign_successes.
counts are distinct accounts over every finding (case-insensitive,
counted before the lists are cut): high is any account high in this
tool's own lists or in Microsoft's high list; medium is any account
medium here or in Microsoft's medium list that is not high. With P2,
microsoft_high / microsoft_medium say how many accounts Microsoft
lists. Lists are capped at 50 (*_capped).
how_to_read says what to act on. Like signin_failure_stats this
walks the interactive sign-in log for the window (every legacy-protocol
authentication is interactive) under ENTRAADM_DEADLINE; capped=true
means the window was not fully read and the findings are a lower bound.
Read-only (AuditLog.Read.All application permission, or -- for azure-cli
auth -- the Reports Reader directory role; IdentityRiskyUser.Read.All,
optional, for microsoft_risky_users).
Args: hours: How far back to look, clamped to [1, 720] (30 days). tenant: Tenant to read: a [tenant.] section name or one of its domains (default: the default tenant). min_spray_users: Distinct failed accounts that make an IP a spray source (default 5, clamped to >= 2). max_pages: Page budget (default: ENTRAADM_MAX_PAGES_DEFAULT).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| hours | No | ||
| tenant | No | ||
| max_pages | No | ||
| min_spray_users | No |