From 937a7a35e60fc97e03ba0691fdf441c5b0139de6 Mon Sep 17 00:00:00 2001 From: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:06:47 -0400 Subject: [PATCH] [New Rule] Azure Arc Kubernetes Cluster Connect Abuse (#5824) * [New Rule] Azure Arc Kubernetes Cluster Connect Abuse Fixes #5823 * rename, adjusted query * adding KEEP * * adjusting maturity * added to non-ecs schema * updating rule * addressing unit test failures * adjustments to logic, mitre mappings, unit test failures, etc. * Update rules/integrations/azure/initial_access_azure_arc_cluster_credential_access_unusual_source.toml Co-authored-by: Mika Ayenson, PhD --------- Co-authored-by: Mika Ayenson, PhD --- detection_rules/etc/non-ecs-schema.json | 3 +- ...al_signin_then_arc_credential_listing.toml | 151 ++++++++++++++++ ...ster_credential_access_unusual_source.toml | 146 ++++++++++++++++ ...e_principal_signin_multiple_countries.toml | 164 ++++++++++++++++++ ...ure_arc_proxy_secret_configmap_access.toml | 141 +++++++++++++++ 5 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 rules/integrations/azure/credential_access_azure_service_principal_signin_then_arc_credential_listing.toml create mode 100644 rules/integrations/azure/initial_access_azure_arc_cluster_credential_access_unusual_source.toml create mode 100644 rules/integrations/azure/initial_access_azure_service_principal_signin_multiple_countries.toml create mode 100644 rules/integrations/kubernetes/credential_access_azure_arc_proxy_secret_configmap_access.toml diff --git a/detection_rules/etc/non-ecs-schema.json b/detection_rules/etc/non-ecs-schema.json index e89775049..5722fd023 100644 --- a/detection_rules/etc/non-ecs-schema.json +++ b/detection_rules/etc/non-ecs-schema.json @@ -223,7 +223,8 @@ "azure.activitylogs.properties.appDisplayName": "keyword", "azure.activitylogs.properties.requestbody.properties.roleDefinitionId": "keyword", "azure.activitylogs.properties.responseBody": "keyword", - "azure.activitylogs.properties.status_code": "keyword" + "azure.activitylogs.properties.status_code": "keyword", + "azure.activitylogs.identity.claims.appid": "keyword" }, "logs-azure.graphactivitylogs-*": { "azure.graphactivitylogs.properties.c_idtyp": "keyword", diff --git a/rules/integrations/azure/credential_access_azure_service_principal_signin_then_arc_credential_listing.toml b/rules/integrations/azure/credential_access_azure_service_principal_signin_then_arc_credential_listing.toml new file mode 100644 index 000000000..563375a5e --- /dev/null +++ b/rules/integrations/azure/credential_access_azure_service_principal_signin_then_arc_credential_listing.toml @@ -0,0 +1,151 @@ +[metadata] +creation_date = "2026/03/10" +integration = ["azure"] +maturity = "production" +updated_date = "2026/03/10" + +[rule] +author = ["Elastic"] +description = """ +Detects when a service principal authenticates to Microsoft Entra ID and then lists credentials for an Azure +Arc-connected Kubernetes cluster within a short time window. The `listClusterUserCredential` action retrieves tokens +that enable kubectl access through the Arc Cluster Connect proxy. This sequence (service principal sign-in followed by +Arc credential retrieval), represents the exact attack chain used by adversaries with stolen service principal secrets +to establish a proxy tunnel into Kubernetes clusters. Service principals that authenticate externally (as opposed to +managed identities) and immediately access Arc cluster credentials warrant investigation, particularly when the sign-in +originates from an unexpected location or ASN. +""" +false_positives = [ + """ + CI/CD pipelines that authenticate as a service principal and then access Arc clusters as part of deployment + workflows will trigger this rule. Identify and exclude known automation service principal app IDs. + """, + """ + Administrators using service principal credentials to manage Arc-connected clusters during maintenance windows may + trigger this rule. Correlate with change management records. + """, +] +from = "now-30m" +index = ["logs-azure.signinlogs-*", "logs-azure.activitylogs-*"] +interval = "15m" +language = "eql" +license = "Elastic License v2" +name = "Azure Service Principal Sign-In Followed by Arc Cluster Credential Access" +note = """## Triage and analysis + +### Investigating Azure Service Principal Sign-In Followed by Arc Cluster Credential Access + +This rule detects the complete attack entry point for Arc-proxied Kubernetes attacks: a service principal authenticates +to Azure AD, then immediately retrieves Arc cluster credentials. This is the prerequisite sequence before any +Kubernetes-level activity can occur through the Arc proxy. + +### Possible investigation steps + +- Identify the service principal using the `app_id` from the sign-in event and resolve it in Azure AD — is this a + known application? +- Check the sign-in source IP and geolocation — does it match expected infrastructure locations for this SP? +- Review when the SP credentials were last rotated — stale credentials are more likely compromised. +- Check the ASN of the sign-in source — is it from a known cloud provider, corporate network, or unexpected consumer ISP? +- Examine Azure Activity Logs after the credential listing for any Arc-proxied operations (secret/configmap CRUD). +- Correlate with Kubernetes audit logs for operations by the Arc proxy service account + (`system:serviceaccount:azure-arc:azure-arc-kube-aad-proxy-sa`) in the same time window. +- Review Azure AD Audit Logs for recent changes to this SP (new credentials, federated identities, owner changes). + +### Response and remediation + +- Immediately rotate the service principal credentials (secrets and certificates). +- Revoke active sessions and tokens for the SP. +- Review and remove any unauthorized Azure role assignments on Arc-connected clusters. +- Check Kubernetes audit logs for any operations performed through the Arc proxy after credential access. +- Rotate any Kubernetes secrets that may have been accessed through the proxy tunnel. +- Enable conditional access policies to restrict service principal authentication by location if supported. +""" +references = [ + "https://learn.microsoft.com/en-us/azure/azure-arc/kubernetes/cluster-connect", + "https://learn.microsoft.com/en-us/cli/azure/connectedk8s#az-connectedk8s-proxy", + "https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins", + "https://www.ibm.com/think/x-force/identifying-abusing-azure-arc-for-hybrid-escalation-persistence", + "https://www.microsoft.com/en-us/security/blog/2025/08/27/storm-0501s-evolving-techniques-lead-to-cloud-based-ransomware/", +] +risk_score = 47 +rule_id = "dacfbecd-7927-46a7-a8ba-feb65a2e990d" +severity = "medium" +tags = [ + "Domain: Cloud", + "Domain: Identity", + "Data Source: Azure", + "Data Source: Azure Arc", + "Data Source: Microsoft Entra ID", + "Data Source: Microsoft Entra ID Sign-In Logs", + "Use Case: Threat Detection", + "Tactic: Credential Access", + "Tactic: Initial Access", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "eql" + +query = ''' +sequence with maxspan=30m +[authentication where event.dataset == "azure.signinlogs" + and azure.signinlogs.category == "ServicePrincipalSignInLogs" + and azure.signinlogs.properties.status.error_code == 0 +] by azure.signinlogs.properties.app_id +[any where event.dataset == "azure.activitylogs" + and azure.activitylogs.operation_name : "MICROSOFT.KUBERNETES/CONNECTEDCLUSTERS/LISTCLUSTERUSERCREDENTIAL/ACTION" + and event.outcome : ("Success", "success") +] by azure.activitylogs.identity.claims.appid +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1552" +name = "Unsecured Credentials" +reference = "https://attack.mitre.org/techniques/T1552/" +[[rule.threat.technique.subtechnique]] +id = "T1552.007" +name = "Container API" +reference = "https://attack.mitre.org/techniques/T1552/007/" + + + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1078" +name = "Valid Accounts" +reference = "https://attack.mitre.org/techniques/T1078/" +[[rule.threat.technique.subtechnique]] +id = "T1078.004" +name = "Cloud Accounts" +reference = "https://attack.mitre.org/techniques/T1078/004/" + + + +[rule.threat.tactic] +id = "TA0001" +name = "Initial Access" +reference = "https://attack.mitre.org/tactics/TA0001/" + +[rule.investigation_fields] +field_names = [ + "@timestamp", + "azure.signinlogs.properties.app_id", + "azure.signinlogs.properties.app_display_name", + "azure.signinlogs.properties.service_principal_name", + "azure.signinlogs.category", + "azure.activitylogs.operation_name", + "azure.activitylogs.identity.claims.appid", + "azure.resource.id", + "source.ip", + "source.geo.country_name", + "source.geo.city_name", + "source.as.organization.name", +] + diff --git a/rules/integrations/azure/initial_access_azure_arc_cluster_credential_access_unusual_source.toml b/rules/integrations/azure/initial_access_azure_arc_cluster_credential_access_unusual_source.toml new file mode 100644 index 000000000..e394d39c9 --- /dev/null +++ b/rules/integrations/azure/initial_access_azure_arc_cluster_credential_access_unusual_source.toml @@ -0,0 +1,146 @@ +[metadata] +creation_date = "2026/03/10" +integration = ["azure"] +maturity = "production" +updated_date = "2026/03/10" + +[rule] +author = ["Elastic"] +description = """ +Detects when a service principal or user performs an Azure Arc cluster credential listing operation from a source IP not +previously associated with that identity. The `listClusterUserCredential` action retrieves credentials for the Arc +Cluster Connect proxy, enabling kubectl access through the Azure ARM API. An adversary using stolen service principal +credentials will typically call this operation from infrastructure not previously seen for that SP. By tracking the +combination of caller identity and source IP, this rule avoids false positives from backend services and CI/CD pipelines +that rotate IPs but maintain consistent identity-to-IP patterns over time. +""" +false_positives = [ + """ + A service principal used by a CI/CD pipeline may trigger this rule when the pipeline runs from a new IP range for + the first time (e.g., migrating to a new runner pool). The 7-day history window will learn the new IPs after the + first occurrence. + """, + """ + Administrators accessing Arc clusters from a new VPN endpoint or travel location. Validate the caller identity + matches an expected user and correlate with known travel or access patterns. + """, +] +from = "now-9m" +index = ["logs-azure.activitylogs-*"] +language = "kuery" +license = "Elastic License v2" +name = "Azure Arc Cluster Credential Access by Identity from Unusual Source" +note = """## Triage and analysis + +### Investigating Azure Arc Cluster Credential Access by Identity from Unusual Source + +The `listClusterUserCredential` operation on an Azure Arc-connected cluster returns credentials that allow the caller +to establish a proxy tunnel via `az connectedk8s proxy`. This proxy routes kubectl commands through the Azure ARM API, +enabling Kubernetes access without direct network connectivity to the cluster API server. + +### Possible investigation steps + +- Identify the caller service principal using `azure.activitylogs.identity.claims.appid` and cross-reference with + Azure AD to determine if this is a known application. +- Check the source IP and geolocation — is this from a country or ASN where your organization operates? +- Correlate with Azure Sign-In Logs around the same time to see the full authentication chain (SP login followed by + credential listing). +- Verify the Azure role used — the `Azure Arc Enabled Kubernetes Cluster User Role` is required for this operation. + Was this role recently assigned? +- Check if subsequent Arc-proxied operations (secret/configmap CRUD) occurred after the credential access. +- Review the service principal creation date in Azure AD — recently created SPs are more suspicious. + +### Response and remediation + +- If the source IP is from an unexpected country or the service principal is not recognized, treat as potential + credential compromise. +- Revoke the service principal credentials and remove Arc RBAC role assignments. +- Review Kubernetes audit logs for any operations performed through the Arc proxy after credential access. +- Rotate any Kubernetes secrets that may have been accessed. +""" +references = [ + "https://learn.microsoft.com/en-us/azure/azure-arc/kubernetes/cluster-connect", + "https://learn.microsoft.com/en-us/cli/azure/connectedk8s#az-connectedk8s-proxy", + "https://www.ibm.com/think/x-force/identifying-abusing-azure-arc-for-hybrid-escalation-persistence", + "https://nvd.nist.gov/vuln/detail/cve-2022-37968", +] +risk_score = 47 +rule_id = "022c37cd-5a4f-422b-8227-b136b7a23180" +severity = "medium" +tags = [ + "Domain: Cloud", + "Data Source: Azure", + "Data Source: Azure Arc", + "Data Source: Azure Activity Logs", + "Use Case: Threat Detection", + "Tactic: Initial Access", + "Tactic: Credential Access", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "new_terms" + +query = ''' +event.dataset: "azure.activitylogs" + and azure.activitylogs.operation_name: "MICROSOFT.KUBERNETES/CONNECTEDCLUSTERS/LISTCLUSTERUSERCREDENTIAL/ACTION" + and event.outcome: (Success or success) +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1078" +name = "Valid Accounts" +reference = "https://attack.mitre.org/techniques/T1078/" +[[rule.threat.technique.subtechnique]] +id = "T1078.004" +name = "Cloud Accounts" +reference = "https://attack.mitre.org/techniques/T1078/004/" + + + +[rule.threat.tactic] +id = "TA0001" +name = "Initial Access" +reference = "https://attack.mitre.org/tactics/TA0001/" +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1552" +name = "Unsecured Credentials" +reference = "https://attack.mitre.org/techniques/T1552/" +[[rule.threat.technique.subtechnique]] +id = "T1552.007" +name = "Container API" +reference = "https://attack.mitre.org/techniques/T1552/007/" + + + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" + +[rule.investigation_fields] +field_names = [ + "@timestamp", + "azure.activitylogs.operation_name", + "azure.activitylogs.identity.claims.appid", + "azure.activitylogs.identity.authorization.evidence.role", + "azure.activitylogs.identity.authorization.evidence.principalType", + "azure.resource.id", + "source.ip", + "source.geo.country_name", + "source.geo.city_name", + "source.as.organization.name", +] + +[rule.new_terms] +field = "new_terms_fields" +value = ["azure.activitylogs.identity.claims.appid", "source.ip"] +[[rule.new_terms.history_window_start]] +field = "history_window_start" +value = "now-7d" + + diff --git a/rules/integrations/azure/initial_access_azure_service_principal_signin_multiple_countries.toml b/rules/integrations/azure/initial_access_azure_service_principal_signin_multiple_countries.toml new file mode 100644 index 000000000..c844468a7 --- /dev/null +++ b/rules/integrations/azure/initial_access_azure_service_principal_signin_multiple_countries.toml @@ -0,0 +1,164 @@ +[metadata] +creation_date = "2026/03/10" +integration = ["azure"] +maturity = "production" +updated_date = "2026/03/10" + +[rule] +author = ["Elastic"] +description = """ +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. +""" +false_positives = [ + """ + Service principals used by globally distributed CI/CD systems (e.g., GitHub Actions runners in multiple regions) may + legitimately authenticate from different countries. Baseline the expected geographic distribution for each service + principal. + """, + """ + VPN or proxy usage by administrators managing service principals from different locations may produce multi-country + sign-in patterns. Correlate with the administrator's known travel or access patterns. + """, +] +from = "now-8h" +interval = "1h" +language = "esql" +license = "Elastic License v2" +name = "Azure Service Principal Authentication from Multiple Countries" +note = """## 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_id` and `app_display_name` from 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 = [ + "https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-sign-ins", + "https://learn.microsoft.com/en-us/entra/identity/conditional-access/workload-identities", + "https://www.microsoft.com/en-us/security/blog/2025/08/27/storm-0501s-evolving-techniques-lead-to-cloud-based-ransomware/", + "https://www.wiz.io/blog/lateral-movement-risks-in-the-cloud-and-how-to-prevent-them-part-3-from-compromis", +] +risk_score = 73 +rule_id = "db97a2aa-3ba5-4fa5-b8b9-bf42284edb5f" +severity = "high" +tags = [ + "Domain: Cloud", + "Domain: Identity", + "Data Source: Azure", + "Data Source: Microsoft Entra ID", + "Data Source: Microsoft Entra ID Sign-In Logs", + "Use Case: Identity and Access Audit", + "Use Case: Threat Detection", + "Tactic: Initial Access", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +FROM logs-azure.signinlogs-* metadata _id, _index +| WHERE event.dataset == "azure.signinlogs" + AND azure.signinlogs.category == "ServicePrincipalSignInLogs" + AND azure.signinlogs.properties.status.error_code == 0 + AND source.geo.country_iso_code IS NOT NULL + AND azure.signinlogs.properties.service_principal_id IS NOT NULL + AND NOT azure.signinlogs.properties.app_owner_tenant_id IN ( + "f8cdef31-a31e-4b4a-93e4-5f571e91255a", + "72f988bf-86f1-41af-91ab-2d7cd011db47" + ) + +| EVAL + Esql.source_ip_string = TO_STRING(source.ip), + Esql.source_ip_country_pair = CONCAT(Esql.source_ip_string, " - ", source.geo.country_name) + +| STATS + Esql.source_geo_country_iso_code_count_distinct = COUNT_DISTINCT(source.geo.country_iso_code), + Esql.source_geo_country_name_values = VALUES(source.geo.country_name), + Esql.source_geo_city_name_values = VALUES(source.geo.city_name), + Esql.source_ip_values = VALUES(source.ip), + Esql.source_ip_country_pair_values = VALUES(Esql.source_ip_country_pair), + Esql.source_network_org_name_values = VALUES(`source.as.organization.name`), + Esql.resource_display_name_values = VALUES(azure.signinlogs.properties.resource_display_name), + Esql.app_id_values = VALUES(azure.signinlogs.properties.app_id), + Esql.app_owner_tenant_id_values = VALUES(azure.signinlogs.properties.app_owner_tenant_id), + Esql.source_ip_count_distinct = COUNT_DISTINCT(source.ip), + Esql.source_geo_city_name_count_distinct = COUNT_DISTINCT(source.geo.city_name), + Esql.source_network_org_name_count_distinct = COUNT_DISTINCT(`source.as.organization.name`), + Esql.timestamp_first_seen = MIN(@timestamp), + Esql.timestamp_last_seen = MAX(@timestamp), + Esql.event_count = COUNT(*) + BY azure.signinlogs.properties.service_principal_id, azure.signinlogs.properties.app_display_name + +| WHERE Esql.source_geo_country_iso_code_count_distinct >= 2 +| KEEP * +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1078" +name = "Valid Accounts" +reference = "https://attack.mitre.org/techniques/T1078/" +[[rule.threat.technique.subtechnique]] +id = "T1078.004" +name = "Cloud Accounts" +reference = "https://attack.mitre.org/techniques/T1078/004/" + + + +[rule.threat.tactic] +id = "TA0001" +name = "Initial Access" +reference = "https://attack.mitre.org/tactics/TA0001/" + +[rule.investigation_fields] +field_names = [ + "azure.signinlogs.properties.service_principal_id", + "azure.signinlogs.properties.app_display_name", + "Esql.source_geo_country_iso_code_count_distinct", + "Esql.source_geo_country_name_values", + "Esql.source_geo_city_name_values", + "Esql.source_ip_values", + "Esql.source_ip_country_pair_values", + "Esql.source_network_org_name_values", + "Esql.resource_display_name_values", + "Esql.app_id_values", + "Esql.app_owner_tenant_id_values", + "Esql.source_ip_count_distinct", + "Esql.source_geo_city_name_count_distinct", + "Esql.source_network_org_name_count_distinct", + "Esql.timestamp_first_seen", + "Esql.timestamp_last_seen", + "Esql.event_count", +] + diff --git a/rules/integrations/kubernetes/credential_access_azure_arc_proxy_secret_configmap_access.toml b/rules/integrations/kubernetes/credential_access_azure_arc_proxy_secret_configmap_access.toml new file mode 100644 index 000000000..0a6f1a129 --- /dev/null +++ b/rules/integrations/kubernetes/credential_access_azure_arc_proxy_secret_configmap_access.toml @@ -0,0 +1,141 @@ +[metadata] +creation_date = "2026/03/10" +integration = ["kubernetes"] +maturity = "production" +updated_date = "2026/03/10" + +[rule] +author = ["Elastic"] +description = """ +Detects when secrets or configmaps are accessed, created, modified, or deleted in a Kubernetes cluster by the Azure Arc +AAD proxy service account. When operations are routed through the Azure Arc Cluster Connect proxy, the Kubernetes audit +log records the acting user as `system:serviceaccount:azure-arc:azure-arc-kube-aad-proxy-sa` with the actual caller +identity in the `impersonatedUser` field. This pattern indicates that someone is accessing the cluster through the Azure +ARM API rather than directly via kubectl against the API server. While legitimate for Arc-managed workflows, adversaries +with stolen service principal credentials can abuse Arc Cluster Connect to read, exfiltrate, or modify secrets and +configmaps while appearing as the Arc proxy service account in K8s audit logs. +""" +false_positives = [ + """ + Azure Arc system components may create or update secrets and configmaps in the azure-arc and azure-arc-release + namespaces during normal cluster management. Filter by namespace to exclude these. + """, + """ + Helm operations managed through Arc may create release secrets (prefixed with sh.helm.release.v1). These are normal + Arc lifecycle operations. + """, +] +from = "now-5d" +interval = "9m" +language = "esql" +license = "Elastic License v2" +name = "Kubernetes Secret or ConfigMap Access via Azure Arc Proxy" +note = """## Triage and analysis + +### Investigating Kubernetes Secret or ConfigMap Access via Azure Arc Proxy + +When Kubernetes operations are performed through Azure Arc Cluster Connect, the K8s audit log shows the Arc AAD proxy +service account as the authenticated user, with the actual Azure AD identity in the `impersonatedUser` field. This +rule detects non-system secret and configmap access — including reads, writes, and deletions — routed through this +proxy path. Read operations (`get`, `list`) are particularly important to detect as they represent the most common +adversary action: exfiltrating secrets without leaving obvious modification traces. + +### Possible investigation steps + +- Check the `kubernetes.audit.impersonatedUser.username` field — this contains the Azure AD object ID of the actual + caller. Cross-reference with Azure AD to identify the service principal or user. +- Review the `kubernetes.audit.impersonatedUser.extra.oid` field for the Azure AD object ID. +- Examine the namespace — operations in `default` or application namespaces are more suspicious than `azure-arc` or + `kube-system`. +- Check the `kubernetes.audit.objectRef.name` — look for suspicious secret/configmap names that don't match known + application resources. +- Correlate with Azure Activity Logs for the same time window to find the `LISTCLUSTERUSERCREDENTIAL` operation that + initiated the Arc proxy session. +- Review Azure Sign-In Logs for the impersonated identity's authentication source IP and geolocation. + +### Response and remediation + +- If the impersonated identity is not recognized, revoke its Azure AD credentials immediately. +- Remove the ClusterRoleBinding or RoleBinding that grants the identity access to secrets/configmaps. +- Rotate any Kubernetes secrets that may have been read or exfiltrated. +- Review the Arc connection and consider disconnecting it if compromised. +""" +references = [ + "https://learn.microsoft.com/en-us/azure/azure-arc/kubernetes/cluster-connect", + "https://microsoft.github.io/Threat-Matrix-for-Kubernetes/", + "https://www.ibm.com/think/x-force/identifying-abusing-azure-arc-for-hybrid-escalation-persistence", + "https://cloud.google.com/blog/topics/threat-intelligence/escalating-privileges-azure-kubernetes-services", + "https://www.wiz.io/blog/lateral-movement-risks-in-the-cloud-and-how-to-prevent-them-part-3-from-compromis", +] +risk_score = 47 +rule_id = "220d92c6-479d-4a49-9cc0-3a29756dad0c" +severity = "medium" +tags = [ + "Data Source: Kubernetes", + "Domain: Kubernetes", + "Domain: Cloud", + "Use Case: Threat Detection", + "Tactic: Credential Access", + "Tactic: Collection", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +FROM logs-kubernetes.audit_logs-* metadata _id, _version, _index +| WHERE STARTS_WITH(kubernetes.audit.user.username, "system:serviceaccount:azure-arc:") + AND kubernetes.audit.objectRef.resource IN ("secrets", "configmaps") + AND kubernetes.audit.verb IN ("get", "list", "create", "update", "patch", "delete") + AND kubernetes.audit.objectRef.namespace NOT IN ("azure-arc", "azure-arc-release", "kube-system") + AND NOT STARTS_WITH(kubernetes.audit.objectRef.name, "sh.helm.release.v1") + +| STATS + Esql.verb_values = VALUES(kubernetes.audit.verb), + Esql.resource_type_values = VALUES(kubernetes.audit.objectRef.resource), + Esql.resource_name_values = VALUES(kubernetes.audit.objectRef.name), + Esql.namespace_values = VALUES(kubernetes.audit.objectRef.namespace), + Esql.acting_user_values = VALUES(kubernetes.audit.user.username), + Esql.user_agent_values = VALUES(kubernetes.audit.userAgent), + Esql.source_ips_values = VALUES(kubernetes.audit.sourceIPs), + Esql.response_code_values = VALUES(kubernetes.audit.responseStatus.code), + Esql.timestamp_first_seen = MIN(@timestamp), + Esql.timestamp_last_seen = MAX(@timestamp), + Esql.event_count = COUNT(*) + BY kubernetes.audit.impersonatedUser.username + +| WHERE Esql.timestamp_first_seen >= NOW() - 9 minutes +| KEEP * +''' + + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1552" +name = "Unsecured Credentials" +reference = "https://attack.mitre.org/techniques/T1552/" +[[rule.threat.technique.subtechnique]] +id = "T1552.007" +name = "Container API" +reference = "https://attack.mitre.org/techniques/T1552/007/" + + + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1530" +name = "Data from Cloud Storage" +reference = "https://attack.mitre.org/techniques/T1530/" + + +[rule.threat.tactic] +id = "TA0009" +name = "Collection" +reference = "https://attack.mitre.org/tactics/TA0009/" +