From 9798f9d71a6036d29d102ec88bbd4a24940c6dfd Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:51:46 +0100 Subject: [PATCH 1/2] [New Hunt] Persistence via Web Shells --- hunting/index.md | 1 + hunting/index.yml | 5 ++ .../linux/docs/persistence_via_web_shell.md | 61 +++++++++++++++++++ .../queries/persistence_via_web_shell.toml | 49 +++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 hunting/linux/docs/persistence_via_web_shell.md create mode 100644 hunting/linux/queries/persistence_via_web_shell.toml diff --git a/hunting/index.md b/hunting/index.md index 7b014415d8b..fc06b26441c 100644 --- a/hunting/index.md +++ b/hunting/index.md @@ -46,6 +46,7 @@ Here are the queries currently available: - [Persistence via System V Init](./linux/docs/persistence_via_sysv_init.md) (ES|QL) - [Persistence via Systemd (Timers)](./linux/docs/persistence_via_systemd_timers.md) (ES|QL) - [Persistence via Udev](./linux/docs/persistence_via_udev.md) (ES|QL) +- [Persistence via Web Shell](./linux/docs/persistence_via_web_shell.md) (ES|QL) - [Persistence via rc.local/rc.common](./linux/docs/persistence_via_rc_local.md) (ES|QL) - [Potential Defense Evasion via Multi-Dot Process Execution](./linux/docs/defense_evasion_via_multi_dot_process_execution.md) (ES|QL) - [Privilege Escalation Identification via Existing Sudoers File](./linux/docs/privilege_escalation_via_existing_sudoers.md) (ES|QL) diff --git a/hunting/index.yml b/hunting/index.yml index ba1152c6d7e..f111d4476cf 100644 --- a/hunting/index.yml +++ b/hunting/index.yml @@ -220,6 +220,11 @@ linux: mitre: - T1037.004 - T1546.003 + e2e4a1ad-5e03-4968-927c-9ef13c49a3b8: + name: Persistence via Web Shell + path: ./linux/queries/persistence_via_web_shell.toml + mitre: + - T1505.003 okta: 0b936024-71d9-11ef-a9be-f661ea17fbcc: name: Failed OAuth Access Token Retrieval via Public Client App diff --git a/hunting/linux/docs/persistence_via_web_shell.md b/hunting/linux/docs/persistence_via_web_shell.md new file mode 100644 index 00000000000..944673cbc86 --- /dev/null +++ b/hunting/linux/docs/persistence_via_web_shell.md @@ -0,0 +1,61 @@ +# Persistence via Web Shell + +--- + +## Metadata + +- **Author:** Elastic +- **Description:** This hunt identifies potential persistence mechanisms leveraging web shells on Linux systems. Web shells are malicious scripts or executables that attackers deploy to provide remote access, execute arbitrary commands, or maintain persistence on compromised systems. This hunt focuses on detecting suspicious file creation events and anomalous network activity associated with known web shell behaviors. + +- **UUID:** `e2e4a1ad-5e03-4968-927c-9ef13c49a3b8` +- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint) +- **Language:** `[ES|QL]` +- **Source File:** [Persistence via Web Shell](../queries/persistence_via_web_shell.toml) + +## Query + +```sql +from logs-endpoint.events.file-* +| keep @timestamp, host.os.type, event.action, file.extension, process.name, agent.id, file.name, process.executable +| where @timestamp > now() - 90 day +| where host.os.type == "linux" and event.action in ("rename", "creation") and +file.extension in ("php", "py", "pl", "rb", "rs", "lua", "jsp") and not ( + // Add your noisy exclusions here + process.name in ("dnf", "dpkg", "pip3", "pip", "yum", "tar", "code", "vmtoolsd") +) +| stats cc = count(), agent_count = count_distinct(agent.id) by file.name, process.executable +| where agent_count <= 3 +| sort cc asc +| limit 100 +``` + +```sql +from logs-endpoint.events.network-* +| keep @timestamp, host.os.type, event.type, event.action, process.name, source.ip, agent.id, process.executable, process.command_line +| where @timestamp > now() - 30 days +| where host.os.type == "linux" and event.type == "end" and event.action == "disconnect_received" and +( + process.name like "ruby*" or + process.name like "perl*" or + process.name like "python*" or + process.name like "php*" +) and source.ip IS NOT null and not CIDR_MATCH(source.ip, "127.0.0.0/8", "169.254.0.0/16", "224.0.0.0/4", "::1", "172.18.0.0/16") +| stats cc = count(), agent_count = count_distinct(agent.id) by process.executable, process.command_line, source.ip +| where agent_count <= 3 +| sort cc asc +| limit 100 +``` + +## Notes + +- Monitors for the creation or renaming of files with extensions commonly associated with web shells, such as PHP, Python, Perl, Ruby, Lua, and JSP scripts. +- Analyzes network disconnect events to identify anomalous connections initiated by scripting engines, indicating potential use of web shells for remote access. +- Provides statistics and counts to detect rare or unusual activity related to file modifications or network events, helping prioritize investigation efforts. + +## MITRE ATT&CK Techniques + +- [T1505.003](https://attack.mitre.org/techniques/T1505/003) + +## License + +- `Elastic License v2` diff --git a/hunting/linux/queries/persistence_via_web_shell.toml b/hunting/linux/queries/persistence_via_web_shell.toml new file mode 100644 index 00000000000..900ed7f24d9 --- /dev/null +++ b/hunting/linux/queries/persistence_via_web_shell.toml @@ -0,0 +1,49 @@ +[hunt] +author = "Elastic" +description = """ +This hunt identifies potential persistence mechanisms leveraging web shells on Linux systems. Web shells are malicious scripts or executables that attackers deploy to provide remote access, execute arbitrary commands, or maintain persistence on compromised systems. This hunt focuses on detecting suspicious file creation events and anomalous network activity associated with known web shell behaviors. +""" +integration = ["endpoint"] +uuid = "e2e4a1ad-5e03-4968-927c-9ef13c49a3b8" +name = "Persistence via Web Shell" +language = ["ES|QL"] +license = "Elastic License v2" +notes = [ + "Monitors for the creation or renaming of files with extensions commonly associated with web shells, such as PHP, Python, Perl, Ruby, Lua, and JSP scripts.", + "Analyzes network disconnect events to identify anomalous connections initiated by scripting engines, indicating potential use of web shells for remote access.", + "Provides statistics and counts to detect rare or unusual activity related to file modifications or network events, helping prioritize investigation efforts." +] +mitre = ["T1505.003"] + +query = [ +''' +from logs-endpoint.events.file-* +| keep @timestamp, host.os.type, event.action, file.extension, process.name, agent.id, file.name, process.executable +| where @timestamp > now() - 90 day +| where host.os.type == "linux" and event.action in ("rename", "creation") and +file.extension in ("php", "py", "pl", "rb", "rs", "lua", "jsp") and not ( + // Add your noisy exclusions here + process.name in ("dnf", "dpkg", "pip3", "pip", "yum", "tar", "code", "vmtoolsd") +) +| stats cc = count(), agent_count = count_distinct(agent.id) by file.name, process.executable +| where agent_count <= 3 +| sort cc asc +| limit 100 +''', +''' +from logs-endpoint.events.network-* +| keep @timestamp, host.os.type, event.type, event.action, process.name, source.ip, agent.id, process.executable, process.command_line +| where @timestamp > now() - 30 days +| where host.os.type == "linux" and event.type == "end" and event.action == "disconnect_received" and +( + process.name like "ruby*" or + process.name like "perl*" or + process.name like "python*" or + process.name like "php*" +) and source.ip IS NOT null and not CIDR_MATCH(source.ip, "127.0.0.0/8", "169.254.0.0/16", "224.0.0.0/4", "::1", "172.18.0.0/16") +| stats cc = count(), agent_count = count_distinct(agent.id) by process.executable, process.command_line, source.ip +| where agent_count <= 3 +| sort cc asc +| limit 100 +''' +] From f3e73c6a39f137b494e5120e16a4c820f85e4218 Mon Sep 17 00:00:00 2001 From: Ruben Groenewoud <78494512+Aegrah@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:57:29 +0100 Subject: [PATCH 2/2] Update query --- hunting/linux/docs/persistence_via_web_shell.md | 2 +- hunting/linux/queries/persistence_via_web_shell.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hunting/linux/docs/persistence_via_web_shell.md b/hunting/linux/docs/persistence_via_web_shell.md index 944673cbc86..64c52eb77e2 100644 --- a/hunting/linux/docs/persistence_via_web_shell.md +++ b/hunting/linux/docs/persistence_via_web_shell.md @@ -17,7 +17,7 @@ ```sql from logs-endpoint.events.file-* | keep @timestamp, host.os.type, event.action, file.extension, process.name, agent.id, file.name, process.executable -| where @timestamp > now() - 90 day +| where @timestamp > now() - 30 days | where host.os.type == "linux" and event.action in ("rename", "creation") and file.extension in ("php", "py", "pl", "rb", "rs", "lua", "jsp") and not ( // Add your noisy exclusions here diff --git a/hunting/linux/queries/persistence_via_web_shell.toml b/hunting/linux/queries/persistence_via_web_shell.toml index 900ed7f24d9..b2f25fc5e84 100644 --- a/hunting/linux/queries/persistence_via_web_shell.toml +++ b/hunting/linux/queries/persistence_via_web_shell.toml @@ -19,7 +19,7 @@ query = [ ''' from logs-endpoint.events.file-* | keep @timestamp, host.os.type, event.action, file.extension, process.name, agent.id, file.name, process.executable -| where @timestamp > now() - 90 day +| where @timestamp > now() - 30 days | where host.os.type == "linux" and event.action in ("rename", "creation") and file.extension in ("php", "py", "pl", "rb", "rs", "lua", "jsp") and not ( // Add your noisy exclusions here