diff --git a/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_cloud_instance_metadata.toml b/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_cloud_instance_metadata.toml new file mode 100644 index 000000000..b32b225ec --- /dev/null +++ b/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_cloud_instance_metadata.toml @@ -0,0 +1,125 @@ +[metadata] +creation_date = "2026/04/23" +integration = ["kubernetes"] +maturity = "production" +updated_date = "2026/04/23" + +[rule] +author = ["Elastic"] +description = """ +Detects Kubernetes pod exec sessions whose decoded command line references cloud instance metadata endpoints or +equivalent hostnames and paths. Workloads that reach the link-local metadata IP, AWS IMDS paths, GCP computeMetadata, +Azure IMDS token routes, or encoded variants are often attempting to harvest role credentials, tokens, or instance +attributes from the underlying node or hypervisor boundary. That behavior is high risk in multi-tenant and regulated +environments because it can expose short-lived cloud credentials to code running inside a container. The rule +classifies a coarse cloud target label and whether the string looks like credential retrieval versus lighter +reconnaissance. +""" +from = "now-6m" +interval = "5m" +language = "esql" +license = "Elastic License v2" +name = "Kubernetes Pod Exec Cloud Instance Metadata Access" +note = """## Triage and analysis + +### Investigating Kubernetes Pod Exec Cloud Instance Metadata Access + +This alert fires when an audited exec requestURI, after URL decoding and command reconstruction, matches patterns +associated with instance metadata services across AWS, GCP, and Azure. Use it to catch interactive or scripted access +from inside a pod to metadata surfaces that should usually be blocked by network policy or not needed by application +code. + +### Possible investigation steps + +- Confirm the Kubernetes identity that performed exec: user name, groups, impersonation, source IP, and user agent. +- Map the pod and namespace to a workload owner, image digest, and entrypoint; determine whether the container should + ever call metadata endpoints. +- Inspect Esql.cloud_target and Esql.is_credential_theft in the alert document and expand the timeline for the same + identity for secret reads, IAM changes, or data egress. +- Correlate with cloud audit logs on the node identity or instance profile for STS or token issuance around the event + time. + +### False positive analysis + +- Break-glass debugging from platform engineers may include curl to 169.254.169.254; validate change tickets and + bastion use. +- Misconfigured agents or bootstrap scripts in bespoke images can touch metadata during startup; baseline approved + images and tune exclusions narrowly. + +### Response and remediation + +- If unauthorized, terminate the session, isolate the workload, revoke or rotate instance and workload credentials that + could have been read, and tighten RBAC on pods exec plus network policies that deny link-local metadata from pods. +""" +references = [ + "https://attack.mitre.org/techniques/T1552/005/", + "https://hardenedsecurity.io/blog/aws-imds-vulnerabilities-and-mitigations/", +] +risk_score = 73 +rule_id = "a8e7d6c5-b4a3-2918-0f9e-8d7c6b5a4032" +severity = "high" +tags = [ + "Data Source: Kubernetes", + "Domain: Kubernetes", + "Domain: Cloud", + "Use Case: Threat Detection", + "Tactic: Credential Access", + "Tactic: Execution", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" +query = ''' +FROM logs-kubernetes.audit_logs-* metadata _id, _index, _version +| WHERE kubernetes.audit.objectRef.subresource == "exec" + AND kubernetes.audit.requestURI LIKE "*command=*" +| EVAL decoded_uri = URL_DECODE(kubernetes.audit.requestURI) +| GROK decoded_uri "%{DATA}/exec\\?%{DATA:raw_commands}&(?:container|stdin|stdout|stderr)=%{GREEDYDATA}" +| EVAL command = REPLACE(raw_commands, "command=", "") +| EVAL command = REPLACE(command, "&", " ") +| EVAL Esql.executed_command = REPLACE(command, "\\+", " ") +| WHERE Esql.executed_command IS NOT NULL + AND Esql.executed_command RLIKE """.*(169\.254\.169\.254|2852039166|0xa9fea9fe|/latest/api/token|/latest/meta-data|/latest/user-data|/latest/dynamic/instance-identity|computeMetadata/v1|metadata\.google\.internal|metadata/identity/oauth2/token|metadata/instance).*""" +| EVAL Esql.cloud_target = CASE( + Esql.executed_command RLIKE """.*(169\.254\.169\.254|2852039166|0xa9fea9fe|/latest/meta-data|/latest/api/token|/latest/user-data|/latest/dynamic).*""", "AWS_IMDS", + Esql.executed_command RLIKE """.*(computeMetadata/v1|metadata\.google\.internal).*""", "GCP_METADATA", + Esql.executed_command RLIKE """.*metadata/identity/oauth2/token.*""", "AZURE_IMDS", + "UNKNOWN" + ) +| EVAL Esql.is_credential_theft = CASE( + Esql.executed_command RLIKE """.*(security-credentials|/api/token|oauth2/token|service-accounts/.*/token).*""", "yes", + "recon" + ) +| 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.005" +name = "Cloud Instance Metadata API" +reference = "https://attack.mitre.org/techniques/T1552/005/" + +[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 = "T1609" +name = "Container Administration Command" +reference = "https://attack.mitre.org/techniques/T1609/" + +[rule.threat.tactic] +id = "TA0002" +name = "Execution" +reference = "https://attack.mitre.org/tactics/TA0002/" diff --git a/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_sensitive_file_access.toml b/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_sensitive_file_access.toml new file mode 100644 index 000000000..ff46d1da6 --- /dev/null +++ b/rules/integrations/kubernetes/credential_access_kubernetes_pod_exec_sensitive_file_access.toml @@ -0,0 +1,136 @@ +[metadata] +creation_date = "2026/04/23" +integration = ["kubernetes"] +maturity = "production" +updated_date = "2026/04/23" + +[rule] +author = ["Elastic"] +description = """ +Detects Kubernetes pod exec sessions whose decoded command line references high-value host or in-cluster paths and +material types: mounted service account or platform tokens, kubelet and control-plane configuration areas, host +identity stores, root dot-directories for cloud and kubeconfig material, common private-key and keystore extensions, +process environment dumps, and configuration filenames suggestive of embedded secrets. The intent is to catch +interactive or scripted access that often precedes lateral movement, privilege escalation, or credential theft from +the node or workload boundary. A narrow exclusion ignores benign reads of resolv.conf. The query also labels an +access_type bucket to speed triage without altering the detection predicates you validated. +""" +from = "now-6m" +interval = "5m" +language = "esql" +license = "Elastic License v2" +name = "Kubernetes Pod Exec Sensitive File or Credential Path Access" +note = """## Triage and analysis + +### Investigating Kubernetes Pod Exec Sensitive File or Credential Path Access + +This alert ties Kubernetes audit exec events to reconstructed command text that matches sensitive path and filename +patterns. Use the Esql.access_type field to prioritize: IRSA token paths, default Kubernetes service account tokens, +other mounted secrets, certificates and keystores, Kubernetes static config, kubelet state, host passwd or shadow, +user home credential stores, and proc environ scraping. + +### Possible investigation steps + +- Identify the Kubernetes user, groups, impersonation, source IP, and user agent for the exec caller. +- Map objectRef namespace, pod, and container to an owning team, image digest, and change history. +- Compare Esql.executed_command against known runbooks; capture follow-on audit activity such as additional execs, + secret reads at the API layer, or RBAC changes. +- If host-level paths appear, determine whether the workload runs privileged, with hostPath mounts, or on nodes where + break-glass access is expected. + +### False positive analysis + +- Diagnostic images and vendor agents sometimes cat resolv.conf or kubeconfig-like paths; the rule excludes resolv.conf + but other matches may still be legitimate—baseline stable automation identities. +- Training containers that deliberately demonstrate passwd reads can trigger; scope exceptions to those images and + namespaces. + +### Response and remediation + +- If malicious, end the exec session, isolate the pod or node, rotate any credentials that could have been read, + review and tighten pods exec RBAC and admission controls, and inspect for persistence added after the session. +""" +references = [ + "https://attack.mitre.org/techniques/T1552/001/", + "https://attack.mitre.org/techniques/T1552/007/", +] +risk_score = 73 +rule_id = "b2c3d4e5-f6a7-4890-b1c2-d3e4f5a60789" +severity = "high" +tags = [ + "Data Source: Kubernetes", + "Domain: Kubernetes", + "Use Case: Threat Detection", + "Tactic: Credential Access", + "Tactic: Execution", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" +query = ''' +from logs-kubernetes.audit_logs-* metadata _id, _index, _version +| WHERE kubernetes.audit.objectRef.subresource == "exec" + AND kubernetes.audit.requestURI LIKE "*command=*" +| EVAL decoded_uri = URL_DECODE(kubernetes.audit.requestURI) +| GROK decoded_uri "%{DATA}/exec\\?%{DATA:raw_commands}&(?:container|stdin|stdout|stderr)=%{GREEDYDATA}" +| EVAL command = REPLACE(raw_commands, "command=", "") +| EVAL command = REPLACE(command, "&", " ") +| EVAL Esql.executed_command = REPLACE(command, "\\+", " ") +| WHERE Esql.executed_command IS NOT NULL + AND Esql.executed_command RLIKE """.*(/var/run/secrets/|/etc/kubernetes/|/var/lib/kubelet/|/etc/shadow|/etc/passwd|/etc/sudoers|(/root|/home/[^/]+)/\.(ssh|aws|azure|kube|config/gcloud)|\.p12|\.pem|\.key|\.jks|\.keystore|/etc/.*\.conf.*(password|secret|key|token|credential)|/proc/.*/environ).*""" + AND NOT Esql.executed_command RLIKE """.*/etc/resolv\.conf.*""" +| EVAL Esql.access_type = CASE( + Esql.executed_command RLIKE """.*/var/run/secrets/eks\.amazonaws\.com.*""", "AWS_IRSA_TOKEN", + Esql.executed_command RLIKE """.*/var/run/secrets/azure/tokens/.*""", "AZURE_WORKLOAD_IDENTITY_TOKEN", + Esql.executed_command RLIKE """.*/var/run/secrets/tokens/gcp-ksa/.*""", "GCP_WORKLOAD_IDENTITY_TOKEN", + Esql.executed_command RLIKE """.*/var/run/secrets/kubernetes\.io/serviceaccount/token.*""", "K8S_SA_TOKEN", + Esql.executed_command RLIKE """.*/var/run/secrets/.*""", "MOUNTED_SECRET", + Esql.executed_command RLIKE """.*\.(p12|pem|key|jks|keystore).*""", "CERTIFICATE_OR_KEY", + Esql.executed_command RLIKE """.*/etc/kubernetes/.*""", "K8S_CONFIG", + Esql.executed_command RLIKE """.*/var/lib/kubelet/.*""", "KUBELET_CONFIG", + Esql.executed_command RLIKE """.*/etc/shadow.*""", "HOST_CREDENTIALS", + Esql.executed_command RLIKE """.*/etc/passwd.*""", "USER_ENUMERATION", + Esql.executed_command RLIKE """.*/etc/sudoers.*""", "SUDOERS_ACCESS", + Esql.executed_command RLIKE """.*(/root|/home)/\.(ssh|aws|azure|kube|config/gcloud).*""", "USER_CREDENTIALS", + Esql.executed_command RLIKE """.*/proc/.*/environ.*""", "PROCESS_ENV_SECRETS", + Esql.executed_command RLIKE """.*/etc/.*\.conf.*(password|secret|key|token|credential).*""", "EMBEDDED_CONFIG_SECRET", + "OTHER_SENSITIVE" + ) +| 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.001" +name = "Credentials In Files" +reference = "https://attack.mitre.org/techniques/T1552/001/" + +[[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 = "T1609" +name = "Container Administration Command" +reference = "https://attack.mitre.org/techniques/T1609/" + +[rule.threat.tactic] +id = "TA0002" +name = "Execution" +reference = "https://attack.mitre.org/tactics/TA0002/" diff --git a/rules/integrations/kubernetes/execution_kubernetes_pod_exec_curl_wget_https.toml b/rules/integrations/kubernetes/execution_kubernetes_pod_exec_curl_wget_https.toml new file mode 100644 index 000000000..d8eb419a9 --- /dev/null +++ b/rules/integrations/kubernetes/execution_kubernetes_pod_exec_curl_wget_https.toml @@ -0,0 +1,109 @@ +[metadata] +creation_date = "2026/04/23" +integration = ["kubernetes"] +maturity = "production" +updated_date = "2026/04/23" + +[rule] +author = ["Elastic"] +description = """ +Detects pod or attach exec API calls where the decoded request query implies **curl** or wget fetching an +**https** URL. Attackers with permission to exec into workloads often run one-liners to stage tooling, pull +scripts or binaries, or exfiltrate data over HTTPS—activity that should be rare compared to shells, debuggers, or +expected health checks. The rule decodes the audit requestURI, reconstructs a readable command string from +repeated command parameters, and applies **noise filters** for common cluster health and OIDC/JWKS endpoints so +benign automation is less likely to alert. +""" +from = "now-6m" +interval = "5m" +language = "esql" +license = "Elastic License v2" +name = "Kubernetes Pod Exec with Curl or Wget to HTTPS" +note = """## Triage and analysis + +### Investigating Kubernetes Pod Exec with Curl or Wget to HTTPS + +Kubernetes audit logs record exec (and similar attach) calls on requestURI, including URL-encoded +command segments. This rule URL-decodes the URI, extracts the query portion into a single string, and +flags curl or wget combined with https, excluding several ommon health, localhost, and OIDC/JWKS patterns. + +### Possible investigation steps + +- Confirm who may exec into the target namespace: review kubernetes.audit.user.username, groups, impersonation, and + source.ip / user_agent.original (kubectl, CI, webhooks). +- Map the pod (kubernetes.audit.objectRef.name) and workload owner; retrieve the exact decoded URI from + Esql.decoded_uri and the reconstructed Esql.command in the alert. +- Search for adjacent audit events from the same identity: secret reads, additional execs, RBAC changes, or anonymous + access. +- If malicious, revoke credentials used for exec, review RoleBindings for **`pods/exec`**, and inspect the pod + filesystem or snapshot for dropped artifacts. + +### False positive analysis + +- Approved runbooks or support sessions may use kubectl exec with curl/wget to test egress or download vendor tools; + document break-glass identities and tune exclusions. +- Some cluster components use HTTPS to **kubernetes.default.svc** or **.well-known** endpoints; the rule attempts to + filter those—expand the exclusion list if your platform uses additional first-party URLs. + +### Response and remediation + +- Rotate any secrets accessible from the pod, cordon or delete the workload if compromised, and tighten RBAC so only + required principals retain **`pods/exec`** on sensitive namespaces. +""" +references = [ + "https://attack.mitre.org/techniques/T1609/", + "https://attack.mitre.org/techniques/T1105/", +] +risk_score = 73 +rule_id = "c9d4e8f1-2a3b-4c5d-8e9f-0a1b2c3d4e5f" +severity = "high" +tags = [ + "Data Source: Kubernetes", + "Domain: Kubernetes", + "Use Case: Threat Detection", + "Tactic: Execution", + "Tactic: Command and Control", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" +query = ''' +FROM logs-kubernetes.audit_logs-* metadata _id, _index, _version +| WHERE kubernetes.audit.objectRef.subresource == "exec" + AND kubernetes.audit.requestURI LIKE "*command=*" +| EVAL decoded_uri = URL_DECODE(kubernetes.audit.requestURI) +| GROK decoded_uri "%{DATA}/exec\\?%{DATA:raw_commands}&(?:container|stdin|stdout|stderr)=%{GREEDYDATA}" +| EVAL command = REPLACE(raw_commands, "command=", "") +| EVAL command = REPLACE(command, "&", " ") +| EVAL Esql.executed_command = REPLACE(command, "\\+", " ") +| WHERE Esql.executed_command IS NOT NULL + AND Esql.executed_command RLIKE """.*(curl.*https|wget.*https).*""" + AND NOT Esql.executed_command RLIKE """.*(/api/v1/health|/healthz|/readyz|/livez|127\.0\.0\.1|localhost|/openid/v1/jwks|/openid-connect/certs|/.well-known/openid-configuration|/.well-known/jwks\.json|kubernetes\.default\.svc).*""" +| KEEP * +''' + +[[rule.threat]] +framework = "MITRE ATT&CK" + +[[rule.threat.technique]] +id = "T1609" +name = "Container Administration Command" +reference = "https://attack.mitre.org/techniques/T1609/" + +[rule.threat.tactic] +id = "TA0002" +name = "Execution" +reference = "https://attack.mitre.org/tactics/TA0002/" + +[[rule.threat]] +framework = "MITRE ATT&CK" + +[[rule.threat.technique]] +id = "T1105" +name = "Ingress Tool Transfer" +reference = "https://attack.mitre.org/techniques/T1105/" + +[rule.threat.tactic] +id = "TA0011" +name = "Command and Control" +reference = "https://attack.mitre.org/tactics/TA0011/" diff --git a/rules/integrations/kubernetes/execution_kubernetes_pod_exec_potential_reverse_shell.toml b/rules/integrations/kubernetes/execution_kubernetes_pod_exec_potential_reverse_shell.toml new file mode 100644 index 000000000..dd9a8db91 --- /dev/null +++ b/rules/integrations/kubernetes/execution_kubernetes_pod_exec_potential_reverse_shell.toml @@ -0,0 +1,98 @@ +[metadata] +creation_date = "2026/04/23" +integration = ["kubernetes"] +maturity = "production" +updated_date = "2026/04/23" + +[rule] +author = ["Elastic"] +description = """ +Flags exec into a pod when the URL-decoded command payload resembles reverse-shell or bind-shell +one-liners invocation patterns. Legitimate debug sessions sometimes use similar building blocks, but together +these patterns align with post-exploitation interactive access and command-and-control. +""" +from = "now-6m" +interval = "5m" +language = "esql" +license = "Elastic License v2" +name = "Kubernetes Pod Exec Potential Reverse Shell" +note = """## Triage and analysis + +### Investigating Kubernetes Pod Exec Potential Reverse Shell + +The rule inspects Kubernetes audit exec requestURI values, URL-decodes them, parses the command query fragment, and +matches high-signal shell and socket idioms often used to obtain a allback shell from inside a container. + +### Possible investigation steps + +- Identify the actor (kubernetes.audit.user.username, groups, impersonation), source IP, and user agent + (human kubectl vs automation). +- Resolve the target namespace, pod, and container from kubernetes.audit.objectRef.* and correlate with + workload ownership and change tickets. +- Pull the raw and decoded URI from the alert document and replay the inferred command in a sandbox only if policy + allows—otherwise rely on audit and platform logs. +- Hunt nearby events from the same identity: secret reads, pods/exec to other workloads, RoleBinding + changes, or anonymous API use. + +### False positive analysis + +- Security training, CTF-style images, or vendor diagnostics may include bash redirection or /dev/tcp examples; + baseline approved images and break-glass accounts. +- Some observability or mesh sidecars use socat or sockets in ways that could overlap; validate container image and + command lineage. + +### Response and remediation + +- If malicious, terminate the exec session, isolate the workload or node, rotate credentials reachable from the + pod, and revoke pods/exec for the abused principal unless strictly required. +""" +references = [ + "https://attack.mitre.org/techniques/T1609/", + "https://attack.mitre.org/techniques/T1059/", +] +risk_score = 73 +rule_id = "f1a2b3c4-d5e6-4789-a012-3456789abc01" +severity = "high" +tags = [ + "Data Source: Kubernetes", + "Domain: Kubernetes", + "Use Case: Threat Detection", + "Tactic: Execution", + "Tactic: Command and Control", + "Resources: Investigation Guide", +] +timestamp_override = "event.ingested" +type = "esql" +query = ''' +FROM logs-kubernetes.audit_logs-* metadata _id, _index, _version +| WHERE kubernetes.audit.objectRef.subresource == "exec" + AND kubernetes.audit.requestURI LIKE "*command=*" +| EVAL decoded_uri = URL_DECODE(kubernetes.audit.requestURI) +| GROK decoded_uri "%{DATA}/exec\\?%{DATA:raw_commands}&(?:container|stdin|stdout|stderr)=%{GREEDYDATA}" +| EVAL command = REPLACE(raw_commands, "command=", "") +| EVAL command = REPLACE(command, "&", " ") +| EVAL Esql.executed_command = REPLACE(command, "\\+", " ") +| WHERE Esql.executed_command IS NOT NULL +| WHERE Esql.executed_command IS NOT NULL AND command RLIKE """.*(/dev/tcp/|/dev/udp/|zsh/net/tcp|zsh/net/udp|nc\s+-e|ncat\s+-e|netcat\s+-e|nc\s.*\s-c\s|mkfifo|socat\s.*exec|socat\s.*pty|bash\s+-i\s+>&|0>&1|>&\s*/dev/tcp|import\s+socket.*connect|import\s+pty.*spawn|socket\.socket.*connect|IO::Socket::INET|fsockopen|TCPSocket\.new|/inet/tcp/).*""" AND + // local service health check patterns + NOT command RLIKE """.*/dev/tcp/(localhost|127\.0\.0\.1)/(8080|8443|9090|3000|5000|8888|80|443).*""" +| KEEP * +''' + +[[rule.threat]] +framework = "MITRE ATT&CK" + +[[rule.threat.technique]] +id = "T1609" +name = "Container Administration Command" +reference = "https://attack.mitre.org/techniques/T1609/" + +[[rule.threat.technique]] +id = "T1059" +name = "Command and Scripting Interpreter" +reference = "https://attack.mitre.org/techniques/T1059/" + +[rule.threat.tactic] +id = "TA0002" +name = "Execution" +reference = "https://attack.mitre.org/tactics/TA0002/"