Azure Service Principal Authentication from Multiple Countries
Detects when an Azure service principal authenticates from multiple countries within a short time window, which may indicate stolen credentials being used from different geographic locations. Service principals typically authenticate from consistent locations tied to their deployment infrastructure. Authentication from multiple countries in a brief period suggests credential compromise, particularly when the source countries do not align with the organization's expected operating regions. This pattern has been observed in attacks using stolen CI/CD credentials, phished service principal secrets, and compromised automation accounts.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/03/10"
3integration = ["azure"]
4maturity = "production"
5updated_date = "2026/03/10"
6
7[rule]
8author = ["Elastic"]
9description = """
10Detects when an Azure service principal authenticates from multiple countries within a short time window, which may
11indicate stolen credentials being used from different geographic locations. Service principals typically authenticate
12from consistent locations tied to their deployment infrastructure. Authentication from multiple countries in a brief
13period suggests credential compromise, particularly when the source countries do not align with the organization's
14expected operating regions. This pattern has been observed in attacks using stolen CI/CD credentials, phished service
15principal secrets, and compromised automation accounts.
16"""
17false_positives = [
18 """
19 Service principals used by globally distributed CI/CD systems (e.g., GitHub Actions runners in multiple regions) may
20 legitimately authenticate from different countries. Baseline the expected geographic distribution for each service
21 principal.
22 """,
23 """
24 VPN or proxy usage by administrators managing service principals from different locations may produce multi-country
25 sign-in patterns. Correlate with the administrator's known travel or access patterns.
26 """,
27]
28from = "now-8h"
29interval = "1h"
30language = "esql"
31license = "Elastic License v2"
32name = "Azure Service Principal Authentication from Multiple Countries"
33note = """## Triage and analysis
34
35### Investigating Azure Service Principal Authentication from Multiple Countries
36
37Service principals are non-interactive identities used for automation and application access. Unlike user accounts,
38they rarely change geographic location. Authentication from multiple countries in a short window is a strong indicator
39of credential compromise.
40
41### Possible investigation steps
42
43- Identify the service principal using the `app_id` and `app_display_name` from the alert.
44- Review the list of countries and source IPs — do they match known infrastructure locations?
45- Check when the service principal credentials were last rotated — stale credentials are more likely compromised.
46- Investigate what resources were accessed after authentication using Azure Activity Logs and Graph Activity Logs.
47- Correlate with Azure AD Audit Logs for recent changes to the service principal (new credentials, federated
48 identities, owner changes).
49- Check if the service principal has Azure Arc or Kubernetes-related role assignments, which could indicate
50 targeting of cluster resources.
51
52### False positive analysis
53- If the service principal is used by a CI/CD pipeline, check if the different countries align with known runner locations. Baseline the expected geographic distribution for that SP.
54- If administrators manage the SP, correlate with known travel patterns or VPN usage that could explain multi-country access.
55
56### Response and remediation
57
58- Immediately rotate the service principal credentials (secrets and certificates).
59- Revoke active sessions and tokens.
60- Review and remove any unauthorized role assignments.
61- Audit resources accessed from the suspicious locations.
62- Enable conditional access policies to restrict service principal authentication by location if supported.
63"""
64references = [
65 "https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins",
66 "https://learn.microsoft.com/en-us/entra/identity/conditional-access/workload-identities",
67 "https://www.microsoft.com/en-us/security/blog/2025/08/27/storm-0501s-evolving-techniques-lead-to-cloud-based-ransomware/",
68 "https://www.wiz.io/blog/lateral-movement-risks-in-the-cloud-and-how-to-prevent-them-part-3-from-compromis",
69]
70risk_score = 73
71rule_id = "db97a2aa-3ba5-4fa5-b8b9-bf42284edb5f"
72severity = "high"
73tags = [
74 "Domain: Cloud",
75 "Domain: Identity",
76 "Data Source: Azure",
77 "Data Source: Microsoft Entra ID",
78 "Data Source: Microsoft Entra ID Sign-In Logs",
79 "Use Case: Identity and Access Audit",
80 "Use Case: Threat Detection",
81 "Tactic: Initial Access",
82 "Resources: Investigation Guide",
83]
84timestamp_override = "event.ingested"
85type = "esql"
86
87query = '''
88FROM logs-azure.signinlogs-* metadata _id, _index
89| WHERE event.dataset == "azure.signinlogs"
90 AND azure.signinlogs.category == "ServicePrincipalSignInLogs"
91 AND azure.signinlogs.properties.status.error_code == 0
92 AND source.geo.country_iso_code IS NOT NULL
93 AND azure.signinlogs.properties.service_principal_id IS NOT NULL
94 AND NOT azure.signinlogs.properties.app_owner_tenant_id IN (
95 "f8cdef31-a31e-4b4a-93e4-5f571e91255a",
96 "72f988bf-86f1-41af-91ab-2d7cd011db47"
97 )
98
99| EVAL
100 Esql.source_ip_string = TO_STRING(source.ip),
101 Esql.source_ip_country_pair = CONCAT(Esql.source_ip_string, " - ", source.geo.country_name)
102
103| STATS
104 Esql.source_geo_country_iso_code_count_distinct = COUNT_DISTINCT(source.geo.country_iso_code),
105 Esql.source_geo_country_name_values = VALUES(source.geo.country_name),
106 Esql.source_geo_city_name_values = VALUES(source.geo.city_name),
107 Esql.source_ip_values = VALUES(source.ip),
108 Esql.source_ip_country_pair_values = VALUES(Esql.source_ip_country_pair),
109 Esql.source_network_org_name_values = VALUES(`source.as.organization.name`),
110 Esql.resource_display_name_values = VALUES(azure.signinlogs.properties.resource_display_name),
111 Esql.app_id_values = VALUES(azure.signinlogs.properties.app_id),
112 Esql.app_owner_tenant_id_values = VALUES(azure.signinlogs.properties.app_owner_tenant_id),
113 Esql.source_ip_count_distinct = COUNT_DISTINCT(source.ip),
114 Esql.source_geo_city_name_count_distinct = COUNT_DISTINCT(source.geo.city_name),
115 Esql.source_network_org_name_count_distinct = COUNT_DISTINCT(`source.as.organization.name`),
116 Esql.timestamp_first_seen = MIN(@timestamp),
117 Esql.timestamp_last_seen = MAX(@timestamp),
118 Esql.event_count = COUNT(*)
119 BY azure.signinlogs.properties.service_principal_id, azure.signinlogs.properties.app_display_name
120
121| WHERE Esql.source_geo_country_iso_code_count_distinct >= 2
122| KEEP *
123'''
124
125
126[[rule.threat]]
127framework = "MITRE ATT&CK"
128[[rule.threat.technique]]
129id = "T1078"
130name = "Valid Accounts"
131reference = "https://attack.mitre.org/techniques/T1078/"
132[[rule.threat.technique.subtechnique]]
133id = "T1078.004"
134name = "Cloud Accounts"
135reference = "https://attack.mitre.org/techniques/T1078/004/"
136
137
138
139[rule.threat.tactic]
140id = "TA0001"
141name = "Initial Access"
142reference = "https://attack.mitre.org/tactics/TA0001/"
143
144[rule.investigation_fields]
145field_names = [
146 "azure.signinlogs.properties.service_principal_id",
147 "azure.signinlogs.properties.app_display_name",
148 "Esql.source_geo_country_iso_code_count_distinct",
149 "Esql.source_geo_country_name_values",
150 "Esql.source_geo_city_name_values",
151 "Esql.source_ip_values",
152 "Esql.source_ip_country_pair_values",
153 "Esql.source_network_org_name_values",
154 "Esql.resource_display_name_values",
155 "Esql.app_id_values",
156 "Esql.app_owner_tenant_id_values",
157 "Esql.source_ip_count_distinct",
158 "Esql.source_geo_city_name_count_distinct",
159 "Esql.source_network_org_name_count_distinct",
160 "Esql.timestamp_first_seen",
161 "Esql.timestamp_last_seen",
162 "Esql.event_count",
163]
Triage and analysis
Investigating Azure Service Principal Authentication from Multiple Countries
Service principals are non-interactive identities used for automation and application access. Unlike user accounts, they rarely change geographic location. Authentication from multiple countries in a short window is a strong indicator of credential compromise.
Possible investigation steps
- Identify the service principal using the
app_idandapp_display_namefrom the alert. - Review the list of countries and source IPs — do they match known infrastructure locations?
- Check when the service principal credentials were last rotated — stale credentials are more likely compromised.
- Investigate what resources were accessed after authentication using Azure Activity Logs and Graph Activity Logs.
- Correlate with Azure AD Audit Logs for recent changes to the service principal (new credentials, federated identities, owner changes).
- Check if the service principal has Azure Arc or Kubernetes-related role assignments, which could indicate targeting of cluster resources.
False positive analysis
- If the service principal is used by a CI/CD pipeline, check if the different countries align with known runner locations. Baseline the expected geographic distribution for that SP.
- If administrators manage the SP, correlate with known travel patterns or VPN usage that could explain multi-country access.
Response and remediation
- Immediately rotate the service principal credentials (secrets and certificates).
- Revoke active sessions and tokens.
- Review and remove any unauthorized role assignments.
- Audit resources accessed from the suspicious locations.
- Enable conditional access policies to restrict service principal authentication by location if supported.
References
Related rules
- Entra ID OAuth User Impersonation to Microsoft Graph
- Azure Service Principal Sign-In Followed by Arc Cluster Credential Access
- Entra ID OAuth Device Code Grant by Unusual User
- Entra ID Service Principal Federated Credential Authentication by Unusual Client
- Entra ID OAuth Authorization Code Grant for Unusual User, App, and Resource