Azure AKS Pod Exec Potential Reverse Shell

Detects successful AKS pod exec sessions whose command resembles reverse-shell or bind-shell one-liner patterns, including /dev/tcp, /dev/udp and gawk /inet redirection, interactive invocation of any common shell, the netcat and ncat exec/listener forms, socat command-execution and listener addresses, mkfifo and mknod pipelines, socket idioms across Python, Perl, PHP, Ruby, Lua and Node, and tooling such as gsocket, openssl s_server and xterm. Legitimate debug sessions sometimes use similar building blocks, but together these patterns align with post-exploitation interactive access and command-and-control.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/08/04"
  3integration = ["azure"]
  4maturity = "production"
  5min_stack_comments = "field_extract (flattened field support) is available starting in 9.5.0, as a Technical Preview function; GA in 9.6.0."
  6min_stack_version = "9.5.0"
  7updated_date = "2026/09/18"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12Detects successful AKS pod exec sessions whose command resembles reverse-shell or bind-shell one-liner patterns,
 13including /dev/tcp, /dev/udp and gawk /inet redirection, interactive invocation of any common shell, the netcat and ncat
 14exec/listener forms, socat command-execution and listener addresses, mkfifo and mknod pipelines, socket idioms across
 15Python, Perl, PHP, Ruby, Lua and Node, and tooling such as gsocket, openssl s_server and xterm. Legitimate debug
 16sessions sometimes use similar building blocks, but together these patterns align with post-exploitation interactive
 17access and command-and-control.
 18"""
 19from = "now-9m"
 20language = "esql"
 21license = "Elastic License v2"
 22name = "Azure AKS Pod Exec Potential Reverse Shell"
 23note = """## Triage and analysis
 24
 25### Investigating Azure AKS Pod Exec Potential Reverse Shell
 26
 27AKS kube-audit events are carried under the flattened `azure.platformlogs.properties.log.*` subtree and share the ARM
 28operation `event.action: Microsoft.ContainerService/managedClusters/diagnosticLogs/Read`.
 29
 30The rule reconstructs the executed command from the URL-encoded `requestURI` on `pods/exec` calls and matches reverse
 31and bind shell idioms: `/dev/tcp` and `/dev/udp` redirection, interactive shell invocation, the netcat and socat
 32families, named pipes, interpreter socket one-liners, and related tooling. It alerts only on established sessions,
 33`responseStatus.code` of `101` at the `ResponseComplete` stage, so denied attempts and the duplicate `ResponseStarted`
 34audit record are excluded.
 35
 36### Possible investigation steps
 37
 38- Review `Esql.username`, `Esql.user_agent`, and `Esql.source_ips` to determine whether the session originated from a
 39  human identity (`kubectl`) or an automated principal, and correlate with nearby secret reads, RBAC changes, or
 40  workload mutations by the same identity. `Esql.source_ips` retains the full array, so check the trailing entries for
 41  the originating client behind any proxy hops.
 42- Review `Esql.executed_command` for the decoded exec payload, and `Esql.namespace`/`Esql.pod_name` for the target
 43  workload. This is the only place the command is recorded, since kube-audit does not populate `requestObject` for a
 44  streaming subresource.
 45
 46### False positive analysis
 47
 48- Interactive debugging shells may look similar; validate command intent and destination. In validated
 49  telemetry, the only identity performing `pods/exec` was a human `kubectl` session (`masterclient`); no
 50  automated/platform identity issued exec calls, and `elastic-agent status`/`inspect` health checks did
 51  not overlap with any reverse-shell indicator.
 52- Security training/CTF-style images, vendor diagnostics, or observability/mesh sidecars using raw
 53  sockets, `socat`, or named pipes (`mkfifo`) can resemble this pattern; baseline approved images and
 54  validate container image/command lineage before escalating.
 55
 56### Response and remediation
 57
 58- If unauthorized, revoke the identity's credentials/kubeconfig, remove malicious objects, and rotate
 59  any secrets or tokens that may have been accessed.
 60- Terminate the exec session, isolate the workload or node, and revoke `pods/exec` for the abused
 61  principal unless strictly required.
 62- Harden RBAC to least privilege and review admission controls.
 63"""
 64references = [
 65    "https://microsoft.github.io/Threat-Matrix-for-Kubernetes/",
 66    "https://kubernetes.io/docs/reference/access-authn-authz/authorization/",
 67    "https://cloudsecdocs.com/containers/offensive/attacks/techniques/reverse_shell/",
 68]
 69risk_score = 73
 70rule_id = "eb56a087-c726-4683-8d49-8c0253ea63cf"
 71setup = "The Azure Fleet integration collecting AKS diagnostic logs with the `kube-audit` category forwarded through Event Hub into the `azure.platformlogs` data stream is required for this rule."
 72severity = "high"
 73tags = [
 74    "Domain: Cloud",
 75    "Domain: Kubernetes",
 76    "Data Source: Azure",
 77    "Data Source: Azure Platform Logs",
 78    "Data Source: Kubernetes",
 79    "Platform: Azure",
 80    "Platform: Kubernetes",
 81    "Rule Type: ES|QL",
 82    "Use Case: Threat Detection",
 83    "Tactic: Command and Control",
 84    "Tactic: Execution",
 85    "Resources: Investigation Guide",
 86    "Domain: Containers",
 87]
 88timestamp_override = "event.ingested"
 89type = "esql"
 90
 91query = '''
 92FROM logs-azure.platformlogs-* METADATA _id, _index, _version
 93| WHERE data_stream.dataset == "azure.platformlogs"
 94    AND event.action == "Microsoft.ContainerService/managedClusters/diagnosticLogs/Read"
 95    AND azure.platformlogs.category == "kube-audit"
 96| EVAL Esql.stage = FIELD_EXTRACT(azure.platformlogs.properties, "log.stage"),
 97    Esql.resource = FIELD_EXTRACT(azure.platformlogs.properties, "log.objectRef.resource"),
 98    Esql.subresource = FIELD_EXTRACT(azure.platformlogs.properties, "log.objectRef.subresource"),
 99    Esql.verb = FIELD_EXTRACT(azure.platformlogs.properties, "log.verb"),
100    Esql.requestURI = FIELD_EXTRACT(azure.platformlogs.properties, "log.requestURI"),
101    Esql.namespace = FIELD_EXTRACT(azure.platformlogs.properties, "log.objectRef.namespace"),
102    Esql.pod_name = FIELD_EXTRACT(azure.platformlogs.properties, "log.objectRef.name"),
103    Esql.username = FIELD_EXTRACT(azure.platformlogs.properties, "log.user.username"),
104    Esql.user_agent = FIELD_EXTRACT(azure.platformlogs.properties, "log.userAgent"),
105    Esql.source_ips = FIELD_EXTRACT(azure.platformlogs.properties, "log.sourceIPs"),
106    Esql.response_code = FIELD_EXTRACT(azure.platformlogs.properties, "log.responseStatus.code")
107| WHERE Esql.stage == "ResponseComplete"
108    AND Esql.resource == "pods"
109    AND Esql.subresource == "exec"
110    AND Esql.verb IN ("create", "get")
111    AND Esql.requestURI LIKE "*command=*"
112    AND Esql.response_code == "101"
113    AND Esql.username IS NOT NULL
114    AND NOT Esql.username RLIKE "system:node:.*|system:serviceaccount:kube-system:.*"
115    AND NOT Esql.username IN ("aksService", "hcpService", "readinessChecker", "system:apiserver", "system:kube-controller-manager", "system:kube-scheduler")
116| EVAL Esql.executed_command = TRIM(REPLACE(REPLACE(REPLACE(
117    URL_DECODE(Esql.requestURI),
118    """^[^?]*\?""", ""),
119    """&?(container|stderr|stdin|stdout|tty)=[^&]*""", ""),
120    """&?command=""", " "))
121| WHERE Esql.executed_command IS NOT NULL
122    // Split across several RLIKE clauses by tool family: a single combined pattern
123    // exceeds the Lucene regex determinization limit and is rejected at parse time.
124    AND (
125        Esql.executed_command RLIKE """.*(/dev/tcp/|/dev/udp/|/inet/tcp/|/inet/udp/|zsh/net/tcp|zsh/net/udp|ztcp\s|(\s|/)(ba|da|a|z|k|c|tc|mk|fi|tcl)?sh\s+-(i|il|li)|nc\s+-e|ncat\s+-e|netcat\s+-e|\s-e\s+/(usr/)?bin/[a-z]*sh|\s-c\s+/(usr/)?bin/[a-z]*sh|(nc|ncat|netcat)[a-z.]*\s+--(sh-)?exec|busybox\s+(nc|netcat|ncat)\s|mkfifo|mknod\s+/(tmp|var/tmp|dev/shm|run)/).*"""
126        OR Esql.executed_command RLIKE """(ba|da|a|z|k|c|tc|mk|fi|tcl)?sh\s+-(i|il|li).*"""
127        OR Esql.executed_command RLIKE """.*socat[0-9]?\s.*((EXEC|Exec|exec|SYSTEM|System|system):|(PTY|Pty|pty)|(OPENSSL|Openssl|openssl)|(UDP|Udp|udp)[0-9]?[-:]|(TCP|Tcp|tcp)-?(LISTEN|Listen|listen)).*"""
128        OR Esql.executed_command RLIKE """.*(socket\.create_connection\(|pty\.spawn\(|subprocess\.call\(|os\.dup2\(|Socket::INET|sockaddr_in\(|TCPSocket\.(new|open)|TCPServer\.new|ruby[0-9.]*\s+-rsocket|fsockopen|stream_socket_client\(|socket_connect\(|socket\.tcp\(|net\.(connect|createConnection|createServer)\(|openssl\s+s_server|stty\s+raw\s+-echo|gs-netcat|gs-sftp|gs-mount|gs-full-pipe|GSOCKET_ARGS=|GS_ARGS=|GS_NOINST=).*"""
129        OR Esql.executed_command RLIKE """.*(import\s+socket.*connect|socket\.socket.*connect|import\s+pty.*spawn|nc\s.*\s-c\s).*"""
130        OR Esql.executed_command RLIKE """.*(php[0-9.]*\s+-r.*(proc_open|pcntl_exec|shell_exec|passthru)\(|lua[0-9.]*\s+-e.*(io\.popen|os\.execute)).*"""
131        OR Esql.executed_command RLIKE """.*((vim|rvim|vimdiff|view|rview)\s+-c.*socket|xterm\s+-display\s+[0-9]).*"""
132    )
133    AND NOT Esql.executed_command RLIKE """.*/dev/tcp/(localhost|127\.0\.0\.1)/(8080|8443|9090|3000|5000|8888|80|443).*"""
134    AND NOT Esql.executed_command RLIKE """.*socat.*(UNIX-CONNECT|UNIX-LISTEN).*"""
135| KEEP Esql.*, event.action, data_stream.namespace, _id, _index, _version
136'''
137
138
139[[rule.threat]]
140framework = "MITRE ATT&CK"
141[[rule.threat.technique]]
142id = "T1059"
143name = "Command and Scripting Interpreter"
144reference = "https://attack.mitre.org/techniques/T1059/"
145
146[[rule.threat.technique]]
147id = "T1609"
148name = "Container Administration Command"
149reference = "https://attack.mitre.org/techniques/T1609/"
150
151
152[rule.threat.tactic]
153id = "TA0002"
154name = "Execution"
155reference = "https://attack.mitre.org/tactics/TA0002/"
156[[rule.threat]]
157framework = "MITRE ATT&CK"
158[[rule.threat.technique]]
159id = "T1095"
160name = "Non-Application Layer Protocol"
161reference = "https://attack.mitre.org/techniques/T1095/"
162
163
164[rule.threat.tactic]
165id = "TA0011"
166name = "Command and Control"
167reference = "https://attack.mitre.org/tactics/TA0011/"

Triage and analysis

Investigating Azure AKS Pod Exec Potential Reverse Shell

AKS kube-audit events are carried under the flattened azure.platformlogs.properties.log.* subtree and share the ARM operation event.action: Microsoft.ContainerService/managedClusters/diagnosticLogs/Read.

The rule reconstructs the executed command from the URL-encoded requestURI on pods/exec calls and matches reverse and bind shell idioms: /dev/tcp and /dev/udp redirection, interactive shell invocation, the netcat and socat families, named pipes, interpreter socket one-liners, and related tooling. It alerts only on established sessions, responseStatus.code of 101 at the ResponseComplete stage, so denied attempts and the duplicate ResponseStarted audit record are excluded.

Possible investigation steps

  • Review Esql.username, Esql.user_agent, and Esql.source_ips to determine whether the session originated from a human identity (kubectl) or an automated principal, and correlate with nearby secret reads, RBAC changes, or workload mutations by the same identity. Esql.source_ips retains the full array, so check the trailing entries for the originating client behind any proxy hops.
  • Review Esql.executed_command for the decoded exec payload, and Esql.namespace/Esql.pod_name for the target workload. This is the only place the command is recorded, since kube-audit does not populate requestObject for a streaming subresource.

False positive analysis

  • Interactive debugging shells may look similar; validate command intent and destination. In validated telemetry, the only identity performing pods/exec was a human kubectl session (masterclient); no automated/platform identity issued exec calls, and elastic-agent status/inspect health checks did not overlap with any reverse-shell indicator.
  • Security training/CTF-style images, vendor diagnostics, or observability/mesh sidecars using raw sockets, socat, or named pipes (mkfifo) can resemble this pattern; baseline approved images and validate container image/command lineage before escalating.

Response and remediation

  • If unauthorized, revoke the identity's credentials/kubeconfig, remove malicious objects, and rotate any secrets or tokens that may have been accessed.
  • Terminate the exec session, isolate the workload or node, and revoke pods/exec for the abused principal unless strictly required.
  • Harden RBAC to least privilege and review admission controls.

References

Related rules

to-top