94ff4b0e3e
* [New Rule] Web Server Potential Command Injection Request * Update variable names to use consistent casing * Add 'Domain: Network' tag to command injection rule * Update persistence_web_server_potential_command_injection.toml * adding missing tags * Update rules/cross-platform/persistence_web_server_potential_command_injection.toml Co-authored-by: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com> * Update rules/cross-platform/persistence_web_server_potential_command_injection.toml Co-authored-by: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com> --------- Co-authored-by: Terrance DeJesus <99630311+terrancedejesus@users.noreply.github.com> Co-authored-by: terrancedejesus <terrance.dejesus@elastic.co> Co-authored-by: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com>
197 lines
8.3 KiB
TOML
197 lines
8.3 KiB
TOML
[metadata]
|
|
creation_date = "2025/11/19"
|
|
integration = ["nginx", "apache", "apache_tomcat", "iis", "network_traffic"]
|
|
maturity = "production"
|
|
updated_date = "2025/11/19"
|
|
|
|
[rule]
|
|
author = ["Elastic"]
|
|
description = """
|
|
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.
|
|
"""
|
|
from = "now-9m"
|
|
interval = "10m"
|
|
language = "esql"
|
|
license = "Elastic License v2"
|
|
name = "Web Server Potential Command Injection Request"
|
|
risk_score = 21
|
|
rule_id = "f3ac6734-7e52-4a0d-90b7-6847bf4308f2"
|
|
severity = "low"
|
|
tags = [
|
|
"Domain: Web",
|
|
"Domain: Network",
|
|
"Use Case: Threat Detection",
|
|
"Tactic: Reconnaissance",
|
|
"Tactic: Persistence",
|
|
"Tactic: Execution",
|
|
"Tactic: Credential Access",
|
|
"Tactic: Command and Control",
|
|
"Data Source: Network Packet Capture",
|
|
"Data Source: Nginx",
|
|
"Data Source: Apache",
|
|
"Data Source: Apache Tomcat",
|
|
"Data Source: IIS",
|
|
]
|
|
timestamp_override = "event.ingested"
|
|
type = "esql"
|
|
query = '''
|
|
from logs-network_traffic.http-*, logs-network_traffic.tls-*, logs-nginx.access-*, logs-apache.access-*, logs-apache_tomcat.access-*, logs-iis.access-*
|
|
| where
|
|
(url.original is not null or url.full is not null) and
|
|
// Limit to 200 response code to reduce noise
|
|
http.response.status_code == 200
|
|
|
|
| eval Esql.url_lower = case(url.original is not null, url.original, url.full)
|
|
| eval Esql.url_lower = to_lower(Esql.url_lower)
|
|
|
|
| eval Esql.contains_interpreter = case(Esql.url_lower like "*python* -c*" or Esql.url_lower like "*perl* -e*" or Esql.url_lower like "*ruby* -e*" or Esql.url_lower like "*ruby* -rsocket*" or Esql.url_lower like "*lua* -e*" or Esql.url_lower like "*php* -r*" or Esql.url_lower like "*node* -e*", 1, 0)
|
|
| eval Esql.contains_shell = case(Esql.url_lower like "*/bin/bash*" or Esql.url_lower like "*bash*-c*" or Esql.url_lower like "*/bin/sh*" or Esql.url_lower rlike "*sh.{1,2}-c*", 1, 0)
|
|
| eval Esql.contains_nc = case(Esql.url_lower like "*netcat*" or Esql.url_lower like "*ncat*" or Esql.url_lower rlike """.*nc.{1,2}[0-9]{1,3}(\.[0-9]{1,3}){3}.{1,2}[0-9]{1,5}.*""" or Esql.url_lower like "*nc.openbsd*" or Esql.url_lower like "*nc.traditional*" or Esql.url_lower like "*socat*", 1, 0)
|
|
| eval Esql.contains_devtcp = case(Esql.url_lower like "*/dev/tcp/*" or Esql.url_lower like "*/dev/udp/*", 1, 0)
|
|
| eval Esql.contains_helpers = case((Esql.url_lower like "*/bin/*" or Esql.url_lower like "*/usr/bin/*") and (Esql.url_lower like "*mkfifo*" or Esql.url_lower like "*nohup*" or Esql.url_lower like "*setsid*" or Esql.url_lower like "*busybox*"), 1, 0)
|
|
| eval Esql.contains_sus_cli = case(Esql.url_lower like "*import*pty*spawn*" or Esql.url_lower like "*import*subprocess*call*" or Esql.url_lower like "*tcpsocket.new*" or Esql.url_lower like "*tcpsocket.open*" or Esql.url_lower like "*io.popen*" or Esql.url_lower like "*os.execute*" or Esql.url_lower like "*fsockopen*", 1, 0)
|
|
| eval Esql.contains_privileges = case(Esql.url_lower like "*chmod*+x", 1, 0)
|
|
| eval Esql.contains_downloader = case(Esql.url_lower like "*curl *" or Esql.url_lower like "*wget *" , 1, 0)
|
|
| eval Esql.contains_file_read_keywords = case(Esql.url_lower like "*/etc/shadow*" or Esql.url_lower like "*/etc/passwd*" or Esql.url_lower like "*/root/.ssh/*" or Esql.url_lower like "*/home/*/.ssh/*" or Esql.url_lower like "*~/.ssh/*" or Esql.url_lower like "*/proc/self/environ*", 1, 0)
|
|
| eval Esql.contains_base64_cmd = case(Esql.url_lower like "*base64*-d*" or Esql.url_lower like "*echo*|*base64*", 1, 0)
|
|
| eval Esql.contains_suspicious_path = case(Esql.url_lower like "*/tmp/*" or Esql.url_lower like "*/var/tmp/*" or Esql.url_lower like "*/dev/shm/*" or Esql.url_lower like "*/root/*" or Esql.url_lower like "*/home/*/*" or Esql.url_lower like "*/var/www/*" or Esql.url_lower like "*/etc/cron.*/*", 1, 0)
|
|
|
|
| eval Esql.any_payload_keyword = case(
|
|
Esql.contains_interpreter == 1 or Esql.contains_shell == 1 or Esql.contains_nc == 1 or Esql.contains_devtcp == 1 or
|
|
Esql.contains_helpers == 1 or Esql.contains_sus_cli == 1 or Esql.contains_privileges == 1 or Esql.contains_downloader == 1 or
|
|
Esql.contains_file_read_keywords == 1 or Esql.contains_base64_cmd == 1 or Esql.contains_suspicious_path == 1, 1, 0)
|
|
|
|
| keep
|
|
@timestamp,
|
|
Esql.url_lower,
|
|
Esql.any_payload_keyword,
|
|
Esql.contains_interpreter,
|
|
Esql.contains_shell,
|
|
Esql.contains_nc,
|
|
Esql.contains_devtcp,
|
|
Esql.contains_helpers,
|
|
Esql.contains_sus_cli,
|
|
Esql.contains_privileges,
|
|
Esql.contains_downloader,
|
|
Esql.contains_file_read_keywords,
|
|
Esql.contains_base64_cmd,
|
|
Esql.contains_suspicious_path,
|
|
source.ip,
|
|
destination.ip,
|
|
agent.id,
|
|
http.request.method,
|
|
http.response.status_code,
|
|
user_agent.original,
|
|
host.name,
|
|
event.dataset
|
|
|
|
| stats
|
|
Esql.event_count = count(),
|
|
Esql.url_path_count_distinct = count_distinct(Esql.url_lower),
|
|
|
|
// General fields
|
|
|
|
Esql.host_name_values = values(host.name),
|
|
Esql.agent_id_values = values(agent.id),
|
|
Esql.url_path_values = values(Esql.url_lower),
|
|
Esql.http.response.status_code_values = values(http.response.status_code),
|
|
Esql.user_agent_original_values = values(user_agent.original),
|
|
Esql.event_dataset_values = values(event.dataset),
|
|
|
|
// Rule Specific fields
|
|
Esql.any_payload_keyword_max = max(Esql.any_payload_keyword),
|
|
Esql.contains_interpreter_values = values(Esql.contains_interpreter),
|
|
Esql.contains_shell_values = values(Esql.contains_shell),
|
|
Esql.contains_nc_values = values(Esql.contains_nc),
|
|
Esql.contains_devtcp_values = values(Esql.contains_devtcp),
|
|
Esql.contains_helpers_values = values(Esql.contains_helpers),
|
|
Esql.contains_sus_cli_values = values(Esql.contains_sus_cli),
|
|
Esql.contains_privileges_values = values(Esql.contains_privileges),
|
|
Esql.contains_downloader_values = values(Esql.contains_downloader),
|
|
Esql.contains_file_read_keywords_values = values(Esql.contains_file_read_keywords),
|
|
Esql.contains_base64_cmd_values = values(Esql.contains_base64_cmd),
|
|
Esql.contains_suspicious_path_values = values(Esql.contains_suspicious_path)
|
|
|
|
by source.ip, agent.id
|
|
|
|
| where
|
|
// Filter for potential command injection attempts with low event counts to reduce false positives
|
|
Esql.any_payload_keyword_max == 1 and Esql.event_count < 5
|
|
'''
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
|
|
[[rule.threat.technique]]
|
|
id = "T1505"
|
|
name = "Server Software Component"
|
|
reference = "https://attack.mitre.org/techniques/T1505/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1505.003"
|
|
name = "Web Shell"
|
|
reference = "https://attack.mitre.org/techniques/T1505/003/"
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0003"
|
|
name = "Persistence"
|
|
reference = "https://attack.mitre.org/tactics/TA0003/"
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
|
|
[[rule.threat.technique]]
|
|
id = "T1059"
|
|
name = "Command and Scripting Interpreter"
|
|
reference = "https://attack.mitre.org/techniques/T1059/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1059.004"
|
|
name = "Unix Shell"
|
|
reference = "https://attack.mitre.org/techniques/T1059/004/"
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0002"
|
|
name = "Execution"
|
|
reference = "https://attack.mitre.org/tactics/TA0002/"
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
|
|
[[rule.threat.technique]]
|
|
id = "T1071"
|
|
name = "Application Layer Protocol"
|
|
reference = "https://attack.mitre.org/techniques/T1071/"
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0011"
|
|
name = "Command and Control"
|
|
reference = "https://attack.mitre.org/tactics/TA0011/"
|
|
|
|
[[rule.threat]]
|
|
framework = "MITRE ATT&CK"
|
|
|
|
[[rule.threat.technique]]
|
|
id = "T1595"
|
|
name = "Active Scanning"
|
|
reference = "https://attack.mitre.org/techniques/T1595/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1595.002"
|
|
name = "Vulnerability Scanning"
|
|
reference = "https://attack.mitre.org/techniques/T1595/002/"
|
|
|
|
[[rule.threat.technique.subtechnique]]
|
|
id = "T1595.003"
|
|
name = "Wordlist Scanning"
|
|
reference = "https://attack.mitre.org/techniques/T1595/003/"
|
|
|
|
[rule.threat.tactic]
|
|
id = "TA0043"
|
|
name = "Reconnaissance"
|
|
reference = "https://attack.mitre.org/tactics/TA0043/"
|