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, andEsql.source_ipsto 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_ipsretains the full array, so check the trailing entries for the originating client behind any proxy hops. - Review
Esql.executed_commandfor the decoded exec payload, andEsql.namespace/Esql.pod_namefor the target workload. This is the only place the command is recorded, since kube-audit does not populaterequestObjectfor 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/execwas a humankubectlsession (masterclient); no automated/platform identity issued exec calls, andelastic-agent status/inspecthealth 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/execfor the abused principal unless strictly required. - Harden RBAC to least privilege and review admission controls.
References
Related rules
- Azure AKS API Server Proxying Request to Kubelet
- Azure AKS Attempted User Exec into Pod
- Azure AKS Ephemeral Container Added to Pod
- Azure AKS Kubelet Proxy to Command Execution Endpoint
- Azure AKS Certificate Signing Request Created or Approved