Repository navigation
Expand file tree
/
Copy pathprocess_behaviour_analysis.py
More file actions
171 lines (147 loc) · 7.76 KB
/
Copy pathprocess_behaviour_analysis.py
File metadata and controls
171 lines (147 loc) · 7.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import psutil
import time
import threading
import config
from common import AlertManager, is_hyperion_process
print(
"""
__ __ __ ______ _______ ______
| \\ | \\ | \\ | \\| \\ / \\
| $$ | $$ __ __ ______ ______ ______ \\$$ ______ _______ \\$$$$$$| $$$$$$$\\| $$$$$$\\
| $$__| $$| \\ | \\ / \\ / \\ / \\ | \\ / \\ | \\ | $$ | $$ | $$| $$___\\$$
| $$ $$| $$ | $$| $$$$$$\\| $$$$$$\\| $$$$$$\\| $$| $$$$$$\\| $$$$$$$\\ | $$ | $$ | $$ \\$$ \\
| $$$$$$$$| $$ | $$| $$ | $$| $$ $$| $$ \\$$| $$| $$ | $$| $$ | $$ | $$ | $$ | $$ _\\$$$$$$\\
| $$ | $$| $$__/ $$| $$__/ $$| $$$$$$$$| $$ | $$| $$__/ $$| $$ | $$ _| $$_ | $$__/ $$| \\__| $$
| $$ | $$ \\$$ $$| $$ $$ \\$$ \\| $$ | $$ \\$$ $$| $$ | $$| $$ \\| $$ $$ \\$$ $$
\\$$ \\$$ _\\$$$$$$$| $$$$$$$ \\$$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$ \\$$$$$$$ \\$$$$$$
| \\__| $$| $$
\\$$ $$| $$
\\$$$$$$ \\$$
===================================================> Process Behaviour Analysis
Made by: Harsh Raj Singhania
Github: https://github.com/HarshRajSinghania
"""
)
class PBAModule:
def __init__(self, log_file,
cpu_threshold=config.PBA_CPU_THRESHOLD,
memory_threshold=config.PBA_MEMORY_THRESHOLD,
scan_interval=config.PBA_SCAN_INTERVAL,
alert_manager=None):
self.log_file = log_file
self.cpu_threshold = cpu_threshold
self.memory_threshold = memory_threshold
self.scan_interval = scan_interval
self.alerts = alert_manager or AlertManager(
log_file=log_file, app_name="PBA System",
title="Process Behavior Analysis Alert",
)
def is_excluded_process(self, process_name):
"""Check if a process is in the exclusion list."""
for excluded in config.PBA_EXCLUDED_PROCESSES:
if process_name.startswith(excluded):
return True
return False
def _is_suspicious_root_process(self, process, process_name):
"""A process running as root is normal (most of the OS does). What
is actually worth an alert is a root process running from outside
the standard system binary directories -- e.g. something dropped
in /tmp or a user's home dir and executed as root. This replaces
the previous blanket 'UID == 0' check, which flagged literally
every root process on the box, including HyperionIDS's own
modules and each other."""
exe = process.info.get("exe")
if not exe:
return False
if any(exe.startswith(trusted) for trusted in config.PBA_TRUSTED_ROOT_DIRECTORIES):
return False
return True
def monitor_processes(self):
"""Monitor active processes for suspicious behavior."""
self.alerts.info("[INFO] Starting Process Behavior Analysis...")
# Prime psutil's internal CPU-percent tracking. Without a first,
# throwaway call, cpu_percent() has nothing to compare against and
# every process reports 0.0% on the first real reading.
for process in psutil.process_iter():
try:
process.cpu_percent(interval=None)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
while True:
for process in psutil.process_iter(
['pid', 'name', 'memory_percent', 'exe', 'ppid', 'uids']
):
try:
process_name = process.info['name']
pid = process.info['pid']
if self.is_excluded_process(process_name):
continue
# Never let HyperionIDS's own modules -- or each other
# -- get flagged as malicious.
if is_hyperion_process(process):
continue
cpu_percent = process.cpu_percent(interval=None)
if cpu_percent > self.cpu_threshold:
self.alerts.raise_alert(
f"cpu:{pid}",
f"[ALERT] High CPU usage: {process_name} (PID: {pid}), CPU: {cpu_percent}%",
)
if process.info['memory_percent'] and process.info['memory_percent'] > self.memory_threshold:
self.alerts.raise_alert(
f"mem:{pid}",
f"[ALERT] High Memory usage: {process_name} (PID: {pid}), Memory: {process.info['memory_percent']:.1f}%",
)
if process.info['exe'] and any(
restricted_file in process.info['exe'] for restricted_file in config.PBA_RESTRICTED_FILES
):
self.alerts.raise_alert(
f"restricted:{pid}",
f"[ALERT] Restricted file accessed by {process_name} (PID: {pid})",
)
parent_process = (
psutil.Process(process.info['ppid']).name()
if process.info['ppid'] and psutil.pid_exists(process.info['ppid'])
else None
)
for rule in config.PBA_SUSPICIOUS_RELATIONSHIPS:
if parent_process == rule["parent"] and process_name == rule["child"]:
self.alerts.raise_alert(
f"relationship:{pid}",
f"[ALERT] Suspicious parent-child relationship: {parent_process} -> {process_name} (PID: {pid})",
)
if process.info['exe'] and any(
process.info['exe'].startswith(d) for d in config.PBA_SUSPICIOUS_DIRECTORIES
):
self.alerts.raise_alert(
f"suspdir:{pid}",
f"[ALERT] Suspicious directory execution: {process.info['exe']} (PID: {pid})",
)
if process.info['uids'] and process.info['uids'].real == 0:
if self._is_suspicious_root_process(process, process_name):
self.alerts.raise_alert(
f"root:{pid}",
f"[ALERT] Suspicious process running as root from non-standard path: "
f"{process_name} (PID: {pid}, exe: {process.info['exe']})",
)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
time.sleep(self.scan_interval)
def run(self):
"""Run the PBA module."""
monitor_thread = threading.Thread(target=self.monitor_processes, daemon=True)
monitor_thread.start()
self.alerts.info("[INFO] PBA module is running in the background.")
# Main Execution
if __name__ == "__main__":
pba = PBAModule(
log_file=config.PBA_LOG_FILE,
cpu_threshold=config.PBA_CPU_THRESHOLD,
memory_threshold=config.PBA_MEMORY_THRESHOLD,
)
pba.run()
print("Process Behavior Analysis is running. Press Ctrl+C to stop.")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n[INFO] Monitoring stopped.")