Skip to content

Commit ac2bd28

Browse files
authored
Update persistence_web_server_potential_command_injection.toml
1 parent 7962ca6 commit ac2bd28

File tree

1 file changed

+73
-44
lines changed

1 file changed

+73
-44
lines changed

rules/cross-platform/persistence_web_server_potential_command_injection.toml

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,17 @@ applications to inject and execute arbitrary commands on the server, often using
1313
PHP, or shell commands. By monitoring for these indicators in web traffic, security teams can identify and respond to
1414
potential threats early.
1515
"""
16-
from = "now-61m"
17-
interval = "1h"
16+
from = "now-9m"
17+
interval = "10m"
1818
language = "esql"
1919
license = "Elastic License v2"
2020
name = "Web Server Potential Command Injection Request"
2121
risk_score = 21
2222
rule_id = "f3ac6734-7e52-4a0d-90b7-6847bf4308f2"
2323
severity = "low"
2424
tags = [
25-
"Domain Scope: Single",
2625
"Domain: Web",
2726
"Domain: Network",
28-
"OS: Linux",
29-
"OS: macOS",
30-
"OS: Windows",
3127
"Use Case: Threat Detection",
3228
"Tactic: Reconnaissance",
3329
"Tactic: Credential Access",
@@ -48,53 +44,86 @@ from
4844
logs-apache_tomcat.access-*,
4945
logs-iis.access-*
5046
| where
51-
@timestamp > now() - 1d and
52-
(url.original is not null or url.full is not null)
53-
54-
| eval Esql_url_text = case(url.original is not null, url.original, url.full)
55-
| eval Esql_url_lower = to_lower(Esql_url_text)
56-
57-
| eval 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)
58-
| eval 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)
59-
| eval 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)
60-
| eval contains_devtcp = case(Esql_url_lower like "*/dev/tcp/*" or Esql_url_lower like "*/dev/udp/*", 1, 0)
61-
| eval contains_helpers = case(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)
62-
| eval 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)
63-
| eval contains_privileges = case(Esql_url_lower like "*chmod*" or Esql_url_lower like "*chown*", 1, 0)
64-
| eval contains_downloader = case(Esql_url_lower like "*curl *" or Esql_url_lower like "*wget *" , 1, 0)
65-
| eval 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)
66-
| eval contains_base64_cmd = case(Esql_url_lower like "*base64*-d*" or Esql_url_lower like "*xxd*" or Esql_url_lower like "*echo*|*base64*", 1, 0)
67-
| eval 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)
68-
69-
| eval any_payload_keyword = case(
70-
contains_interpreter == 1 or contains_shell == 1 or contains_nc == 1 or contains_devtcp == 1 or
71-
contains_helpers == 1 or contains_sus_cli == 1 or contains_privileges == 1 or contains_downloader == 1 or
72-
contains_file_read_keywords == 1 or contains_base64_cmd == 1 or contains_suspicious_path == 1, 1, 0)
47+
(url.original is not null or url.full is not null) and
48+
// Limit to 200 response code to reduce noise
49+
http.response.status_code == 200
50+
51+
| eval Esql.url_lower = case(url.original is not null, url.original, url.full)
52+
| eval Esql.url_lower = to_lower(Esql.url_lower)
53+
54+
| 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)
55+
| 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)
56+
| 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)
57+
| eval Esql.contains_devtcp = case(Esql.url_lower like "*/dev/tcp/*" or Esql.url_lower like "*/dev/udp/*", 1, 0)
58+
| 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)
59+
| 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)
60+
| eval Esql.contains_privileges = case(Esql.url_lower like "*chmod*+x", 1, 0)
61+
| eval Esql.contains_downloader = case(Esql.url_lower like "*curl *" or Esql.url_lower like "*wget *" , 1, 0)
62+
| 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)
63+
| eval Esql.contains_base64_cmd = case(Esql.url_lower like "*base64*-d*" or Esql.url_lower like "*echo*|*base64*", 1, 0)
64+
| 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)
65+
66+
| eval Esql.any_payload_keyword = case(
67+
Esql.contains_interpreter == 1 or Esql.contains_shell == 1 or Esql.contains_nc == 1 or Esql.contains_devtcp == 1 or
68+
Esql.contains_helpers == 1 or Esql.contains_sus_cli == 1 or Esql.contains_privileges == 1 or Esql.contains_downloader == 1 or
69+
Esql.contains_file_read_keywords == 1 or Esql.contains_base64_cmd == 1 or Esql.contains_suspicious_path == 1, 1, 0)
7370
7471
| keep
7572
@timestamp,
76-
Esql_url_lower,
77-
any_payload_keyword,
78-
contains_interpreter,
79-
contains_shell,
80-
contains_nc,
81-
contains_devtcp,
82-
contains_helpers,
83-
contains_sus_cli,
84-
contains_privileges,
85-
contains_downloader,
86-
contains_file_read_keywords,
87-
contains_base64_cmd,
88-
contains_suspicious_path,
73+
Esql.url_lower,
74+
Esql.any_payload_keyword,
75+
Esql.contains_interpreter,
76+
Esql.contains_shell,
77+
Esql.contains_nc,
78+
Esql.contains_devtcp,
79+
Esql.contains_helpers,
80+
Esql.contains_sus_cli,
81+
Esql.contains_privileges,
82+
Esql.contains_downloader,
83+
Esql.contains_file_read_keywords,
84+
Esql.contains_base64_cmd,
85+
Esql.contains_suspicious_path,
8986
source.ip,
9087
destination.ip,
9188
agent.id,
9289
http.request.method,
9390
http.response.status_code,
94-
user_agent.original
91+
user_agent.original,
92+
host.name,
93+
event.dataset
94+
95+
| stats
96+
Esql.event_count = count(),
97+
Esql.url_path_count_distinct = count_distinct(Esql.url_lower),
98+
99+
// General fields
100+
101+
Esql.host_name_values = values(host.name),
102+
Esql.agent_id_values = values(agent.id),
103+
Esql.url_path_values = values(Esql.url_lower),
104+
Esql.http.response.status_code_values = values(http.response.status_code),
105+
Esql.user_agent_original_values = values(user_agent.original),
106+
Esql.event_dataset_values = values(event.dataset),
107+
108+
// Rule Specific fields
109+
Esql.any_payload_keyword_max = max(Esql.any_payload_keyword),
110+
Esql.contains_interpreter_values = values(Esql.contains_interpreter),
111+
Esql.contains_shell_values = values(Esql.contains_shell),
112+
Esql.contains_nc_values = values(Esql.contains_nc),
113+
Esql.contains_devtcp_values = values(Esql.contains_devtcp),
114+
Esql.contains_helpers_values = values(Esql.contains_helpers),
115+
Esql.contains_sus_cli_values = values(Esql.contains_sus_cli),
116+
Esql.contains_privileges_values = values(Esql.contains_privileges),
117+
Esql.contains_downloader_values = values(Esql.contains_downloader),
118+
Esql.contains_file_read_keywords_values = values(Esql.contains_file_read_keywords),
119+
Esql.contains_base64_cmd_values = values(Esql.contains_base64_cmd),
120+
Esql.contains_suspicious_path_values = values(Esql.contains_suspicious_path)
121+
122+
by source.ip, agent.id
95123
96-
| where any_payload_keyword == 1
97-
| limit 100
124+
| where
125+
// Filter for potential command injection attempts with low event counts to reduce false positives
126+
Esql.any_payload_keyword_max == 1 and Esql.event_count < 5
98127
'''
99128

100129
[[rule.threat]]

0 commit comments

Comments
 (0)