[Tuning] Multiple Cloud Secrets Accessed by Source Address (#5884)

* Update credential_access_multi_could_secrets_via_api.toml

* Update credential_access_multi_could_secrets_via_api.toml

* Update credential_access_multi_could_secrets_via_api.toml

* Apply suggestion from @Mikaayenson

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>

* Apply suggestion from @Mikaayenson

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>

---------

Co-authored-by: Mika Ayenson, PhD <Mikaayenson@users.noreply.github.com>
Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>
This commit is contained in:
Samirbous
2026-03-26 15:49:19 +00:00
committed by GitHub
parent a08d6b4ff7
commit 06ea087363
@@ -1,16 +1,17 @@
[metadata]
creation_date = "2025/12/01"
integration = ["aws", "gcp", "azure"]
integration = ["aws", "gcp", "azure", "kubernetes"]
maturity = "production"
updated_date = "2026/01/26"
updated_date = "2026/03/26"
[rule]
author = ["Elastic"]
description = """
This rule detects authenticated sessions accessing secret stores across multiple cloud providers from the same source
address within a short period of time. Adversaries with access to compromised credentials or session tokens may attempt
to retrieve secrets from services such as AWS Secrets Manager, Google Secret Manager, or Azure Key Vault in rapid
succession to expand their access or exfiltrate sensitive information.
This rule detects authenticated sessions accessing secret stores across multiple environments from the same source
address within a short period of time, including cloud providers (AWS, GCP, Azure) and Kubernetes clusters.
Adversaries with access to compromised credentials or session tokens may attempt to retrieve secrets from services such
as AWS Secrets Manager, Google Secret Manager, Azure Key Vault, or Kubernetes Secrets in rapid succession to expand
their access or exfiltrate sensitive information.
"""
from = "now-9m"
interval = "5m"
@@ -52,6 +53,11 @@ Unexpected cross-cloud secret retrieval is uncommon and typically indicates auto
- Assess potential compromise scope
- If compromise is suspected, enumerate other assets accessed by the same identity in the last 24 hours.
- Look for lateral movement, privilege escalation, or abnormal API usage.
- Review Kubernetes activity
- Identify the Kubernetes user, service account, or workload performing the secret access.
- Determine whether the access originated from a pod, node, or external client.
- Validate whether the identity is expected to access secrets in the affected namespaces.
- Investigate whether the activity corresponds to application behavior or manual/API access.
### False positive analysis
@@ -60,6 +66,8 @@ Unexpected cross-cloud secret retrieval is uncommon and typically indicates auto
- Confirm that the identity is authorized to access secrets across multiple cloud services.
- If activity is expected, consider adding exceptions that pair account identity, source IP, and expected user agent
to reduce noise.
- Determine whether the source IP is associated with Kubernetes nodes, controllers, or internal workloads
that legitimately retrieve secrets alongside cloud provider integrations.
### Response and remediation
@@ -72,6 +80,10 @@ Unexpected cross-cloud secret retrieval is uncommon and typically indicates auto
- Reduce permissions to least privilege.
- Review trust relationships, workload identities, and cross-cloud integrations.
- Search for persistence mechanisms** such as newly created keys, roles, or service accounts.
- If Kubernetes access is involved:
- Rotate Kubernetes secrets and service account tokens.
- Review RBAC permissions and restrict secret access to least privilege.
- Inspect affected pods or nodes for compromise.
- Improve monitoring and audit visibility** by ensuring logging is enabled across all cloud environments.
- Determine root cause** (phishing, malware, token replay, exposed credential, etc.) and close the vector to prevent recurrence.
"""
@@ -102,6 +114,7 @@ tags = [
"Data Source: Azure Activity Logs",
"Data Source: GCP",
"Data Source: Google Cloud Platform",
"Data Source: Kubernetes",
"Tactic: Credential Access",
"Resources: Investigation Guide",
]
@@ -109,31 +122,30 @@ timestamp_override = "event.ingested"
type = "esql"
query = '''
FROM logs-azure.platformlogs-*, logs-aws.cloudtrail-*, logs-gcp.audit-* METADATA _id, _version, _index
FROM logs-azure.platformlogs-*, logs-aws.cloudtrail-*, logs-gcp.audit-*, logs-kubernetes.audit_logs-* METADATA _id, _version, _index
| WHERE
(
/* AWS Secrets Manager */
(event.dataset == "aws.cloudtrail" AND event.provider == "secretsmanager.amazonaws.com" AND event.action == "GetSecretValue") OR
(event.dataset == "aws.cloudtrail" AND event.action == "GetSecretValue") OR
// Azure Key Vault (platform logs)
(event.dataset == "azure.platformlogs" AND event.action IN ("SecretGet", "KeyGet")) or
/* Google Secret Manager */
(event.dataset IN ("googlecloud.audit", "gcp.audit") AND
event.action IN ("google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion", "google.cloud.secretmanager.v1.SecretManagerService.GetSecretRequest"))
event.action IN ("google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion", "google.cloud.secretmanager.v1.SecretManagerService.GetSecretRequest")) OR
/* Kubernetes Secrets */
(event.dataset == "kubernetes.audit_logs" AND kubernetes.audit.objectRef.resource == "secrets" AND kubernetes.audit.verb IN ("get", "list"))
) AND source.ip IS NOT NULL
// Unified user identity (raw)
| EVAL Esql_priv.user_id =
COALESCE(
client.user.id,
aws.cloudtrail.user_identity.arn,
NULL
)
// Cloud vendor label based on dataset
| EVAL Esql.cloud_vendor = CASE(
event.dataset == "aws.cloudtrail", "aws",
event.dataset == "azure.platformlogs", "azure",
event.dataset IN ("googlecloud.audit","gcp.audit"), "gcp",
event.dataset == "kubernetes.audit_logs", "k8s",
"unknown"
)
// Vendor+tenant label, e.g. aws:123456789012, azure:tenant, gcp:project
@@ -141,8 +153,10 @@ FROM logs-azure.platformlogs-*, logs-aws.cloudtrail-*, logs-gcp.audit-* METADAT
Esql.cloud_vendor == "aws", CONCAT("aws:", cloud.account.id),
Esql.cloud_vendor == "azure", CONCAT("azure:", cloud.account.id),
Esql.cloud_vendor == "gcp", CONCAT("gcp:", cloud.account.id),
NULL
Esql.cloud_vendor == "k8s", CONCAT("k8s:", orchestrator.cluster.name),
"unknown"
)
| WHERE Esql.cloud_vendor != "unknown"
| STATS
// Core counts
Esql.events_count = COUNT(*),
@@ -154,18 +168,15 @@ FROM logs-azure.platformlogs-*, logs-aws.cloudtrail-*, logs-gcp.audit-* METADAT
Esql.cloud_vendor_values = VALUES(Esql.cloud_vendor),
Esql.tenant_label_values = VALUES(Esql.tenant_label),
// Hyperscaler-specific IDs
Esql.aws_account_id_values = VALUES(CASE(Esql.cloud_vendor == "aws", cloud.account.id, NULL)),
Esql.azure_tenant_id_values = VALUES(CASE(Esql.cloud_vendor == "azure", cloud.account.id, NULL)),
Esql.gcp_project_id_values = VALUES(CASE(Esql.cloud_vendor == "gcp", cloud.account.id, NULL)),
Esql.aws_account_id_values = VALUES(CASE(Esql.cloud_vendor == "aws", cloud.account.id, "unknown")),
Esql.azure_tenant_id_values = VALUES(CASE(Esql.cloud_vendor == "azure", cloud.account.id, "unknown")),
Esql.gcp_project_id_values = VALUES(CASE(Esql.cloud_vendor == "gcp", cloud.account.id, "unknown")),
// Generic cloud metadata
Esql.cloud_region_values = VALUES(cloud.region),
Esql.cloud_service_name_values = VALUES(cloud.service.name),
// Identity (privileged)
Esql_priv.user_values = VALUES(Esql_priv.user_id),
Esql_priv.client_user_id_values = VALUES(client.user.id),
Esql_priv.aws_user_identity_arn_values = VALUES(aws.cloudtrail.user_identity.arn),
Esql.k8s_namespace_values = VALUES(kubernetes.audit.objectRef.namespace),
// Namespace values
Esql.data_stream_namespace_values = VALUES(data_stream.namespace)
Esql.data_stream_namespace_values = VALUES(data_stream.namespace),
Esql_priv.user_name_values = VALUES(user.name)
BY source.ip
// Require multi-vendor cred-access from same source IP
| WHERE Esql.vendor_count_distinct >= 2