Azure Protection Multiple Alerts for User
Description
Detects when a user account triggers multiple Microsoft Entra ID Protection risk alerts within a short time window, indicating a potentially ongoing attack or compromised account. Entra ID Protection uses machine learning and heuristics to detect risky sign-in activity including anonymous IP usage, atypical travel patterns, malware-linked IP addresses, unfamiliar sign-in properties, password spray attempts, leaked credentials, and other suspicious behaviors.
Query · python
from panther_azuresignin_helpers import (
azure_signin_alert_context,
azure_signin_success,
is_sign_in_event,
)
# Risk states that indicate protection alerts
RISK_STATES = {
"atrisk",
"confirmedcompromised",
}
# Risk levels that should be tracked
RISK_LEVELS = {
"high",
"medium",
}
def rule(event):
if not is_sign_in_event(event) or not azure_signin_success(event):
return False
# Only track events with actual risk
risk_state = event.deep_get("properties", "riskState", default="")
if not risk_state or risk_state.lower() not in RISK_STATES:
return False
# Check risk levels
risk_level_during = event.deep_get("properties", "riskLevelDuringSignIn", default="").lower()
risk_level_agg = event.deep_get("properties", "riskLevelAggregated", default="").lower()
has_risk = risk_level_during in RISK_LEVELS or risk_level_agg in RISK_LEVELS
if not has_risk:
return False
# Get user principal name for tracking
user_principal_name = event.deep_get("properties", "userPrincipalName", default="")
return bool(user_principal_name)
def dedup(event):
return event.deep_get("properties", "userPrincipalName", default="<UNKNOWN_USER>")
def title(event):
user_principal_name = event.deep_get(
"properties", "userPrincipalName", default="<UNKNOWN_USER>"
)
return (
f"Multiple Entra ID Protection Alerts: User [{user_principal_name}] "
f"has multiple risk events "
)
def alert_context(event):
context = azure_signin_alert_context(event)
# Add risk-specific context
context["risk_state"] = event.deep_get("properties", "riskState", default="<NO_RISK_STATE>")
context["risk_level_during_signin"] = event.deep_get(
"properties", "riskLevelDuringSignIn", default="<NO_RISK_LEVEL>"
)
context["risk_level_aggregated"] = event.deep_get(
"properties", "riskLevelAggregated", default="<NO_RISK_LEVEL>"
)
context["risk_detail"] = event.deep_get("properties", "riskDetail", default="<NO_RISK_DETAIL>")
context["risk_event_types"] = event.deep_get(
"properties", "riskEventTypes", default="<NO_RISK_TYPES>"
)
# Add authentication details
context["authentication_protocol"] = event.deep_get(
"properties", "authenticationProtocol", default="<NO_PROTOCOL>"
)
context["client_app_used"] = event.deep_get(
"properties", "clientAppUsed", default="<NO_CLIENT_APP>"
)
context["device_detail_browser"] = event.deep_get(
"properties", "deviceDetail", "browser", default="<NO_BROWSER>"
)
context["device_detail_os"] = event.deep_get(
"properties", "deviceDetail", "operatingSystem", default="<NO_OS>"
)
context["is_interactive"] = event.deep_get("properties", "isInteractive", default=None)
context["user_agent"] = event.deep_get("properties", "userAgent", default="<NO_USER_AGENT>")
# Add location details
context["location_city"] = event.deep_get("properties", "location", "city", default="<NO_CITY>")
context["location_country"] = event.deep_get(
"properties", "location", "countryOrRegion", default="<NO_COUNTRY>"
)
return context
Analyst notes
- Query Azure.Audit logs for all sign-in events by properties:userPrincipalName in the 1 hour surrounding this alert to identify the full sequence of risk events, noting properties:riskEventTypes, callerIpAddress, properties:location, and properties:userAgent for each authentication attempt
- Review properties:riskDetail and properties:riskState for each event to understand the specific risk detections that triggered the alerts, and check if multiple different risk types occurred (e.g., anonymous IP + atypical travel + unfamiliar properties) which strongly indicates account compromise rather than false positives
- Immediately disable the user account or revoke all active sessions if suspicious activity is confirmed, force password reset with MFA re-enrollment, review Azure.Audit logs for any API calls or resource access by the user in the 24 hours after the first risk event to identify data exfiltration or privilege escalation, and contact the user through out-of-band communication to verify if they are aware of the authentication attempts