HUNT 26 Risky Anonymizer Signin Timeline
Query
// Hunt : Hunt - Risky & Anonymizer Sign-in Timeline (30d)
// Tactics : InitialAccess, DefenseEvasion
// MITRE : T1078.004, T1090.003
// Purpose : Broad companion to RULE-19. Timeline of successful sign-ins that AAD Identity
// Protection flagged as risky or anonymizer-sourced (Tor/anonymous VPN), across both
// users and workload identities. Use to baseline anonymizer usage, hunt for
// low-and-slow access under the alert-rule window, and pivot on repeat IPs/ASNs.
//==========================================================================================
let Window = 30d;
let AnonRiskTypes = dynamic(["anonymizedIPAddress", "anonymousIPAddress"]);
let UserRisky = SigninLogs
| where TimeGenerated > ago(Window)
| where ResultType == 0
| where isnotempty(IPAddress)
| extend RiskTypes = tostring(column_ifexists("RiskEventTypes_V2", dynamic([])))
| where RiskTypes has_any (AnonRiskTypes)
or RiskLevelAggregated in~ ("medium", "high")
or RiskState =~ "atRisk"
| project TimeGenerated, IdentityKind = "User", Identity = UserPrincipalName,
IPAddress, Location = tostring(Location), AppDisplayName,
RiskLevel = tostring(RiskLevelAggregated), RiskTypes,
ConditionalAccessStatus;
let SPRisky = AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Window)
| where ResultType == 0
| where isnotempty(IPAddress)
| extend RiskTypes = tostring(column_ifexists("RiskEventTypes_V2", dynamic([])))
| where RiskTypes has_any (AnonRiskTypes)
| project TimeGenerated, IdentityKind = "ServicePrincipal", Identity = ServicePrincipalName,
IPAddress, Location = tostring(LocationDetails.countryOrRegion), AppDisplayName = AppId,
RiskLevel = "n/a", RiskTypes, ConditionalAccessStatus;
union UserRisky, SPRisky
| order by TimeGenerated descExplanation
This query is designed to identify and analyze potentially risky sign-ins over the past 30 days. It focuses on sign-ins flagged by Azure Active Directory (AAD) Identity Protection as risky or originating from anonymized sources like Tor or anonymous VPNs. Here's a simplified breakdown:
-
Time Frame: The query looks at data from the last 30 days.
-
Risk Types: It specifically targets sign-ins associated with anonymized IP addresses or those flagged as risky by AAD.
-
User Sign-ins:
- It filters successful user sign-ins (where the result was successful) with non-empty IP addresses.
- It checks if the sign-in was flagged with certain risk types or had a medium/high risk level.
- It extracts relevant details such as the time of sign-in, user identity, IP address, location, application name, risk level, and conditional access status.
-
Service Principal Sign-ins:
- It similarly filters successful sign-ins for service principals with non-empty IP addresses.
- It checks for the same anonymized risk types.
- It extracts details like the time of sign-in, service principal identity, IP address, location, application ID, and conditional access status.
-
Combining Results: The query combines the results from both user and service principal sign-ins.
-
Sorting: The combined results are sorted by the time of sign-in in descending order, showing the most recent sign-ins first.
The purpose of this query is to establish a baseline for anonymizer usage, identify potential low-and-slow access attempts that might evade alert rules, and investigate repeated IP addresses or Autonomous System Numbers (ASNs) associated with risky sign-ins.
Details

David Alonso
Released: July 27, 2026
Tables
Keywords
Operators