EDIT:
Solved by creating rules with auditctl and then getting the logs from an specific key with ausearch.
I'm basically creating a tool to detect Ransomware activity with honeypots spread across the file system. The program functionality is pretty simple:
- Only works on Linux;
- There are honeypots in various directories in the file system;
- Each honeypot created will have an unique hash and a absolute path store in a json file;
- If watchdog observer detects a modification in one of the honeypots, it will compare the current hash with the hash stored in the json file;
- If the hashes are different, then it's malicious activity, so, kill the process modifying the honeypot file.
I managed to do all of this, and I created a custom and simple Ransomware to test this functionality above. But there is a major problem:
To kill the malicious process, I need to get the PID of the process modifying the honeypot file, but my custom ransomware is so fast that I can't get the process PID. If I place a sleep(0.064) method in the custom ransomware encrypt function, I can detect the ransomware PID an then kill it. But if the sleep value goes bellow 0.064, I just can't get the process PID modifying the file anymore...
I don't know if watchdog observer is to slow to detect this and I should use another module that does the same thing, or a I should create one file system monitor from scratch, or if there is an way to get this malicious process PID.
The functionality of the honeypot modification detector can be see bellow (file_monitor.py):
import hashlib
import json
import os
import signal
import subprocess
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import logging
class FileMonitor:
def __init__(self, directory_list, honeypot_file_name, path_to_config_folder, json_file_name):
self.directory_list = directory_list
self.honeypot_file_name = honeypot_file_name
self.path_to_config_folder = path_to_config_folder
self.json_file_name = json_file_name
class EventHandler(FileSystemEventHandler):
def __init__(self, data):
self.directory_list = data[0]
self.honeypot_file_name = data[1]
self.path_to_config_folder = data[2]
self.json_file_name = data[3]
def on_modified(self, event):
if self.honeypot_file_name in event.src_path:
try:
pids_in_honeypot = subprocess.check_output(["fuser", "-u", event.src_path], stderr=subprocess.DEVNULL).decode().strip().replace(" ", ",").split(",")
for dict in json_file_hashes:
if event.src_path == dict['absolute_path']:
with open(event.src_path, 'rb') as honeypot_file:
file_data = honeypot_file.read()
current_hash = hashlib.md5(file_data).hexdigest()
if current_hash != dict['hash']:
logger.debug(f"Honeypot in {event.src_path} was modified!")
try:
file_monitor_pid = os.getpid()
for pid in pids_in_honeypot:
if str(pid) != str(file_monitor_pid):
logger.debug(f"The Ransomware process PID is: {pid}")
#os.kill(int(pid), signal.SIGKILL)
logger.debug(f"Ransomware with process PID {pid} was killed!")
except Exception as e:
logger.error(f'{str(e.__class__.__name__)}')
continue
except Exception as e:
logger.error("The fuser command returned nothing.")
pass
def run(self):
global observers
observers = []
observer = Observer()
event_handler = self.EventHandler([self.directory_list, self.honeypot_file_name, self.path_to_config_folder, self.json_file_name])
for directory in self.directory_list:
# Criação do observer
observer.schedule(event_handler, directory, recursive=True)
# Colocando o observer criado na lista dos observers
observers.append(observer)
observer.start()
global json_file_hashes
json_file_path = os.path.join(self.path_to_config_folder, self.json_file_name)
if os.path.exists(self.path_to_config_folder):
try:
with open(os.path.join(json_file_path)) as json_file:
json_file_hashes = json.load(json_file)
except FileNotFoundError:
logger.error(f'Could not find {self.json_file_name} in {self.path_to_config_folder}')
quit()
try:
while True:
continue
except KeyboardInterrupt:
for observer in observers:
observer.unschedule_all()
observer.stop()
observer.join()
if __name__ == "__main__":
from logger import logger
logging.getLogger("watchdog.observers.inotify_buffer").disabled = True
fm = FileMonitor(
directory_list=["/home/matheusheidemann/Documents/Github/Python-Ransomware-Detector/ransomware-samples/encrypt-test"],
honeypot_file_name=".r4n50mw4r3-d373c70r.txt",
path_to_config_folder="/home/matheusheidemann/Documents/Github/Python-Ransomware-Detector/software/config",
json_file_name="ransom-detector-hashes-list.json"
)
fm.run()
else:
from software.logger import logger
The full code can be found on this Github repo: https://github.com/its0v3r/Python-Ransomware-Detector
The important files for this process are in the software folder.
When I have sleep(0.064) on my basic Ransomware, I'm able to detect the Ransomware PID: Success to get Ransomware PID
When I don't have sleep(0.064) or sleep(0.063) or lower on my basic Ransomware, I'm not able to detect the Ransomware PID (PS: the PID being printed in the terminal at the left is the file_monitor.py PID): Not being able to get the Ransomware PID
This is what my def on_modified() function is calling and the stats (used snakeviz + cProfiler + pstats): Performance log
I would be extremely grateful if someone can bring a light to this issue, I'm really lost about what should I do now.
PS: I know there is some hardcoded stuff I'm my code, I'm only testing stuff at the moment, there are still a lot of changes to be done.