[New] Suspicious Kubernetes Pod Exec (#5978)

* [New] Kubernetes Pod Exec with Curl or Wget to HTTPS

Detects pod or attach `exec` API calls where the decoded request query implies curl or wget fetching an https URL (avoid noisy local http services).

* Create execution_kubernetes_pod_exec_potential_reverse_shell.toml

* Update execution_kubernetes_pod_exec_curl_wget_https.toml

* Update execution_kubernetes_pod_exec_potential_reverse_shell.toml

* ++

* ++

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Apply suggestion from @terrancedejesus

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>

* Update execution_kubernetes_pod_exec_curl_wget_https.toml

* Update execution_kubernetes_pod_exec_potential_reverse_shell.toml

* Update credential_access_kubernetes_pod_exec_cloud_instance_metadata.toml

* Update credential_access_kubernetes_pod_exec_sensitive_file_access.toml

* Update execution_kubernetes_pod_exec_curl_wget_https.toml

* Update credential_access_kubernetes_pod_exec_sensitive_file_access.toml

* Update credential_access_kubernetes_pod_exec_cloud_instance_metadata.toml

---------

Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com>
This commit is contained in:
Samirbous
2026-05-04 22:42:34 +01:00
committed by GitHub
parent 245956a8d6
commit e4746c3a83
4 changed files with 468 additions and 0 deletions
@@ -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/"
@@ -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/"
@@ -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/"
@@ -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/"