Skip to content

Commit 7795743

Browse files
committed
[New Hunt & Tuning] Persistence via LKMs
1 parent 2ff2965 commit 7795743

6 files changed

+178
-4
lines changed

hunting/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Here are the queries currently available:
4040
- [OSQuery SUID Hunting](./linux/docs/privilege_escalation_via_suid_binaries.md) (ES|QL)
4141
- [Persistence Through Reverse/Bind Shells](./linux/docs/persistence_reverse_bind_shells.md) (ES|QL)
4242
- [Persistence via Cron](./linux/docs/persistence_via_cron.md) (ES|QL)
43+
- [Persistence via Loadable Kernel Modules](./linux/docs/persistence_via_loadable_kernel_modules.md) (ES|QL)
4344
- [Persistence via Message-of-the-Day](./linux/docs/persistence_via_message_of_the_day.md) (ES|QL)
4445
- [Persistence via Package Manager](./linux/docs/persistence_via_package_manager.md) (ES|QL)
4546
- [Persistence via SSH Configurations and/or Keys](./linux/docs/persistence_via_ssh_configurations_and_keys.md) (ES|QL)

hunting/index.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ linux:
220220
mitre:
221221
- T1037.004
222222
- T1546.003
223+
d667d328-fadc-4a52-9b46-f42b1a83181c:
224+
name: Persistence via Loadable Kernel Modules
225+
path: ./linux/queries/persistence_via_loadable_kernel_modules.toml
226+
mitre:
227+
- T1547.006
223228
okta:
224229
0b936024-71d9-11ef-a9be-f661ea17fbcc:
225230
name: Failed OAuth Access Token Retrieval via Public Client App

hunting/linux/docs/persistence_via_driver_load_with_low_occurrence_frequency.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
```sql
1818
from logs-auditd_manager.auditd-*, logs-auditd.log-*, auditbeat-*
19+
| keep @timestamp, host.os.type, event.category, event.action, auditd.data.syscall, auditd.data.name, process.executable, process.name, agent.id
1920
| where @timestamp > now() - 30 day
2021
| where host.os.type == "linux" and event.category == "driver" and event.action == "loaded-kernel-module" and auditd.data.syscall in ("init_module", "finit_module")
21-
| stats host_count = count_distinct(host.id), total_count = count(*) by auditd.data.name, process.executable, process.name
22+
// Process name is different from executable in some cases
23+
| stats agent_count = count_distinct(agent.id), total_count = count(*) by auditd.data.name, process.executable, process.name
2224
// Alter this threshold to make sense for your environment
23-
| where host_count == 1 and total_count == 1
25+
| where agent_count == 1 and total_count <= 3
2426
| limit 100
2527
| sort auditd.data.name asc
2628
```
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Persistence via Loadable Kernel Modules
2+
3+
---
4+
5+
## Metadata
6+
7+
- **Author:** Elastic
8+
- **Description:** This hunt identifies potential persistence mechanisms leveraging Loadable Kernel Modules (LKMs) on Linux systems. LKMs enable dynamic extension of kernel functionality but can be abused by attackers to load malicious code into the kernel, granting them high privileges or persistence. This hunt monitors suspicious kernel module file creations, LKM-related process executions, and access to kernel module configuration files.
9+
10+
- **UUID:** `d667d328-fadc-4a52-9b46-f42b1a83181c`
11+
- **Integration:** [endpoint](https://docs.elastic.co/integrations/endpoint)
12+
- **Language:** `[ES|QL, SQL]`
13+
- **Source File:** [Persistence via Loadable Kernel Modules](../queries/persistence_via_loadable_kernel_modules.toml)
14+
15+
## Query
16+
17+
```sql
18+
from logs-endpoint.events.file-*
19+
| keep @timestamp, host.os.type, event.type, event.action, file.extension, file.path, process.executable, agent.id
20+
| where @timestamp > now() - 30 day
21+
| where host.os.type == "linux" and event.type == "creation" and file.extension == "ko" and not (
22+
// Add your exclusions here
23+
file.path like "/run/initramfs/*" or
24+
file.path like "/var/tmp/mkinitramfs*"
25+
)
26+
| stats cc = count(), agent_count = count_distinct(agent.id) by file.path, process.executable
27+
| where agent_count <= 3
28+
| sort cc asc
29+
| limit 100
30+
```
31+
32+
```sql
33+
from logs-endpoint.events.process-*
34+
| keep @timestamp, host.os.type, event.type, event.action, process.name, agent.id, process.args, process.args_count
35+
| where @timestamp > now() - 30 days
36+
| where host.os.type == "linux" and event.type == "start" and event.action == "exec" and process.name in ("kmod", "modprobe", "insmod", "rmmod")
37+
| stats cc = count(), agent_count = count_distinct(agent.id) by process.args, process.args_count
38+
| where cc == 1 and agent_count == 1 and process.args_count <= 3
39+
| sort cc asc
40+
| limit 100
41+
```
42+
43+
```sql
44+
SELECT
45+
f.filename,
46+
f.path,
47+
u.username AS file_owner,
48+
g.groupname AS group_owner,
49+
datetime(f.atime, 'unixepoch') AS file_last_access_time,
50+
datetime(f.mtime, 'unixepoch') AS file_last_modified_time,
51+
datetime(f.ctime, 'unixepoch') AS file_last_status_change_time
52+
datetime(f.btime, 'unixepoch') AS file_created_time,
53+
f.size AS size_bytes
54+
FROM
55+
file f
56+
LEFT JOIN
57+
users u ON f.uid = u.uid
58+
LEFT JOIN
59+
groups g ON f.gid = g.gid
60+
WHERE
61+
f.path LIKE '/etc/modprobe.d/%'
62+
OR f.path LIKE '/usr/lib/modprobe.d/%'
63+
OR f.path LIKE '/usr/lib/security/%'
64+
OR f.path LIKE '/etc/modules-load.d/%'
65+
OR f.path LIKE '/run/modules-load.d/%'
66+
OR f.path LIKE '/usr/local/lib/modules-load.d/%'
67+
OR f.path like '/usr/lib/modules-load.d/%'
68+
OR f.path = '/etc/modules'
69+
```
70+
71+
```sql
72+
SELECT * FROM kernel_modules;
73+
```
74+
75+
## Notes
76+
77+
- Tracks the creation of loadable kernel module files (.ko) in non-standard directories to identify potential malicious modules.
78+
- Monitors the execution of processes related to kernel module management, such as kmod, modprobe, insmod, and rmmod, to detect suspicious or unusual activity.
79+
- Identifies changes to critical kernel module configuration files, including /etc/modprobe.d/, /etc/modules, and related paths.
80+
- Uses OSQuery queries to gather detailed metadata on kernel modules currently loaded, supporting forensic analysis of potential persistence mechanisms.
81+
- Provides statistics and counts to help identify rare or anomalous kernel module-related events.
82+
83+
## MITRE ATT&CK Techniques
84+
85+
- [T1547.006](https://attack.mitre.org/techniques/T1547/006)
86+
87+
## License
88+
89+
- `Elastic License v2`

hunting/linux/queries/persistence_via_driver_load_with_low_occurrence_frequency.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ mitre = ["T1547.006", "T1069.002"]
1818
query = [
1919
'''
2020
from logs-auditd_manager.auditd-*, logs-auditd.log-*, auditbeat-*
21+
| keep @timestamp, host.os.type, event.category, event.action, auditd.data.syscall, auditd.data.name, process.executable, process.name, agent.id
2122
| where @timestamp > now() - 30 day
2223
| where host.os.type == "linux" and event.category == "driver" and event.action == "loaded-kernel-module" and auditd.data.syscall in ("init_module", "finit_module")
23-
| stats host_count = count_distinct(host.id), total_count = count(*) by auditd.data.name, process.executable, process.name
24+
// Process name is different from executable in some cases
25+
| stats agent_count = count_distinct(agent.id), total_count = count(*) by auditd.data.name, process.executable, process.name
2426
// Alter this threshold to make sense for your environment
25-
| where host_count == 1 and total_count == 1
27+
| where agent_count == 1 and total_count <= 3
2628
| limit 100
2729
| sort auditd.data.name asc
2830
'''
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
[hunt]
2+
author = "Elastic"
3+
description = """
4+
This hunt identifies potential persistence mechanisms leveraging Loadable Kernel Modules (LKMs) on Linux systems. LKMs enable dynamic extension of kernel functionality but can be abused by attackers to load malicious code into the kernel, granting them high privileges or persistence. This hunt monitors suspicious kernel module file creations, LKM-related process executions, and access to kernel module configuration files.
5+
"""
6+
integration = ["endpoint"]
7+
uuid = "d667d328-fadc-4a52-9b46-f42b1a83181c"
8+
name = "Persistence via Loadable Kernel Modules"
9+
language = ["ES|QL", "SQL"]
10+
license = "Elastic License v2"
11+
notes = [
12+
"Tracks the creation of loadable kernel module files (.ko) in non-standard directories to identify potential malicious modules.",
13+
"Monitors the execution of processes related to kernel module management, such as kmod, modprobe, insmod, and rmmod, to detect suspicious or unusual activity.",
14+
"Identifies changes to critical kernel module configuration files, including /etc/modprobe.d/, /etc/modules, and related paths.",
15+
"Uses OSQuery queries to gather detailed metadata on kernel modules currently loaded, supporting forensic analysis of potential persistence mechanisms.",
16+
"Provides statistics and counts to help identify rare or anomalous kernel module-related events."
17+
]
18+
mitre = ["T1547.006"]
19+
20+
query = [
21+
'''
22+
from logs-endpoint.events.file-*
23+
| keep @timestamp, host.os.type, event.type, event.action, file.extension, file.path, process.executable, agent.id
24+
| where @timestamp > now() - 30 day
25+
| where host.os.type == "linux" and event.type == "creation" and file.extension == "ko" and not (
26+
// Add your exclusions here
27+
file.path like "/run/initramfs/*" or
28+
file.path like "/var/tmp/mkinitramfs*"
29+
)
30+
| stats cc = count(), agent_count = count_distinct(agent.id) by file.path, process.executable
31+
| where agent_count <= 3
32+
| sort cc asc
33+
| limit 100
34+
''',
35+
'''
36+
from logs-endpoint.events.process-*
37+
| keep @timestamp, host.os.type, event.type, event.action, process.name, agent.id, process.args, process.args_count
38+
| where @timestamp > now() - 30 days
39+
| where host.os.type == "linux" and event.type == "start" and event.action == "exec" and process.name in ("kmod", "modprobe", "insmod", "rmmod")
40+
| stats cc = count(), agent_count = count_distinct(agent.id) by process.args, process.args_count
41+
| where cc == 1 and agent_count == 1 and process.args_count <= 3
42+
| sort cc asc
43+
| limit 100
44+
''',
45+
'''
46+
SELECT
47+
f.filename,
48+
f.path,
49+
u.username AS file_owner,
50+
g.groupname AS group_owner,
51+
datetime(f.atime, 'unixepoch') AS file_last_access_time,
52+
datetime(f.mtime, 'unixepoch') AS file_last_modified_time,
53+
datetime(f.ctime, 'unixepoch') AS file_last_status_change_time
54+
datetime(f.btime, 'unixepoch') AS file_created_time,
55+
f.size AS size_bytes
56+
FROM
57+
file f
58+
LEFT JOIN
59+
users u ON f.uid = u.uid
60+
LEFT JOIN
61+
groups g ON f.gid = g.gid
62+
WHERE
63+
f.path LIKE '/etc/modprobe.d/%'
64+
OR f.path LIKE '/usr/lib/modprobe.d/%'
65+
OR f.path LIKE '/usr/lib/security/%'
66+
OR f.path LIKE '/etc/modules-load.d/%'
67+
OR f.path LIKE '/run/modules-load.d/%'
68+
OR f.path LIKE '/usr/local/lib/modules-load.d/%'
69+
OR f.path like '/usr/lib/modules-load.d/%'
70+
OR f.path = '/etc/modules'
71+
''',
72+
'''
73+
SELECT * FROM kernel_modules;
74+
'''
75+
]

0 commit comments

Comments
 (0)