Compare commits

..

46 Commits

Author SHA1 Message Date
Sebastian Lenzlinger
39a56a53f0
Update README.md 2023-06-14 12:23:29 +02:00
Sebastian Lenzlinger
000d485da0
Update README.md 2023-06-14 12:21:32 +02:00
Sebastian Lenzlinger
65733380d4
Update README.md 2023-06-14 12:14:35 +02:00
Sebastian Lenzlinger
b934ffded1
Rename keylogger_detector.py to kldetect.py 2023-06-13 16:44:22 +02:00
Sebastian Lenzlinger
efc5b26e60
Merge pull request #10 from sebaschi/fiex
Fiex
2023-06-13 14:36:30 +02:00
Sebastian Lenzlinger
e3c076b1df
Update README.md 2023-06-13 14:35:53 +02:00
Sebastian Lenzlinger
dfac235733
Update README.md
Add warning about updating whitelist.txt with current kernel modules
2023-06-13 14:34:12 +02:00
Sebastian Lenzlinger
ba4bc26b2c
Merge pull request #9 from sebaschi/fiex
Fiex
2023-06-13 14:20:49 +02:00
Sebastian Lenzlinger
5680143977
Merge branch 'main' into fiex 2023-06-13 14:20:31 +02:00
Sebastian Lenzlinger
7d4bc93243
Update README.md 2023-06-13 13:10:24 +02:00
Sebastian Lenzlinger
326f5011e0
Update README.md 2023-06-13 13:07:14 +02:00
Sebastian Lenzlinger
79bad57439
Update README.md 2023-06-13 13:05:49 +02:00
Sebastian Lenzlinger
33eb3c6fb4
Update README.md 2023-06-13 13:05:00 +02:00
Sebastian Lenzlinger
b59c659553
Update README.md 2023-06-13 12:57:39 +02:00
Sebastian Lenzlinger
ec19a08e63 Upate Readme 2023-06-13 12:56:15 +02:00
SoulKindred
689508282c
Add files via upload
Made print-statements preaty and added doc
2023-06-13 12:55:11 +02:00
Sebastian Lenzlinger
6fac965976
Update README.md 2023-06-13 12:49:40 +02:00
Sebastian Lenzlinger
c8fc7dc2b7
Update README.md 2023-06-13 12:49:20 +02:00
Sebastian Lenzlinger
05f86101dd
Update README.md 2023-06-13 12:49:02 +02:00
Sebastian Lenzlinger
c30de44832 Merge branch 'main' of github.com:sebaschi/keylogger-detector 2023-06-13 12:48:02 +02:00
Sebastian Lenzlinger
496ee97d3f Update Readme 2023-06-13 12:47:45 +02:00
Sebastian Lenzlinger
1d40184e5f
Merge pull request #8 from sebaschi/sebaschi-patch-2
Update dev_journal.md
2023-06-13 12:44:48 +02:00
Sebastian Lenzlinger
50c0cef1d2
Update dev_journal.md
fix link
2023-06-13 12:44:33 +02:00
Sebastian Lenzlinger
e3b5b409f1
Merge pull request #7 from sebaschi/sebaschi-patch-1
Update keylogger_detector.py
2023-06-13 11:48:54 +02:00
Sebastian Lenzlinger
6d88d1e97d
Update keylogger_detector.py
Fix Tabs in help messages
2023-06-13 11:48:42 +02:00
Sebastian Lenzlinger
522fb0a847 Add QR Code to repository. 2023-06-13 11:39:09 +02:00
Sebastian Lenzlinger
72bbf7a575 Stop Kernel Detector from allways running and crashing the system 2023-06-13 11:38:35 +02:00
SoulKindred
efbc9bc88f
Add files via upload
Just took the essential code from my script and I put it into keylogger_detector.py
No clue why it works now
2023-06-12 17:55:12 +02:00
Sebastian Lenzlinger
92fc49ac9e FIX 2023-06-11 18:24:42 +02:00
Sebastian Lenzlinger
c8da5e4743 DEBUG 2023-06-11 18:12:26 +02:00
Sebastian Lenzlinger
0e2250816e DEBUG 2023-06-11 18:08:56 +02:00
Sebastian Lenzlinger
1ffe505c5d DEBUG 2023-06-11 17:40:06 +02:00
Sebastian Lenzlinger
0f1b6d4543 DEBUG 2023-06-11 17:39:06 +02:00
Sebastian Lenzlinger
1a0ae39e87 DEBUG 2023-06-11 17:36:17 +02:00
Sebastian Lenzlinger
8ce2a8c55c DEBUG 2023-06-11 16:46:55 +02:00
Sebastian Lenzlinger
037963bccb DEBUG 2023-06-11 16:45:57 +02:00
Sebastian Lenzlinger
d2ab6c92a8 Quick DEBUG 2023-06-11 16:41:32 +02:00
Sebastian Lenzlinger
b4005d7876 Small Bug Fix 2023-06-11 15:33:22 +02:00
Sebastian Lenzlinger
6f13ce5d89 Integrate kernel detector in to main program. Add option to run kernel detector 2023-06-11 15:31:49 +02:00
Sebastian Lenzlinger
626915fd70
Merge pull request #6 from sebaschi/sebaschi-patch-1
Update kernel_detector.py
2023-06-11 15:25:41 +02:00
Sebastian Lenzlinger
bb607848dc
Update kernel_detector.py
remove exit() from within run_kernel_detection()
2023-06-11 15:25:31 +02:00
Sebastian Lenzlinger
0198c6d0fa
Delete funcall_trace2.stp 2023-06-11 15:24:39 +02:00
Sebastian Lenzlinger
c67c88b750
Delete funcall_trace1.stp 2023-06-11 15:24:31 +02:00
Sebastian Lenzlinger
e210227b31
Merge pull request #5 from sebaschi/SoulKindred-patch-1
Kernel Detector
2023-06-11 15:23:23 +02:00
SoulKindred
02f6d6eba0
Kernel Detector
Uploading kernel detection package
2023-06-11 15:22:05 +02:00
Sebastian Lenzlinger
1988f0c021
Delete LICENSE
Unsure About the LICENCE we need and if we get to use all the software we have.
2023-06-10 13:21:19 +02:00
15 changed files with 454 additions and 155 deletions

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Sebastian Lenzlinger
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,2 +1,65 @@
# keylogger-detector # KLDetect
University project for an Operating Systems lecture. The goal is to develope a keystroke-logger-detector for a Linux environment. Developement Environment: Fedora 37 VM under Gnome on VirtualBox. A project journal can be found [here](https://github.com/sebaschi/keylogger-detector/blob/main/doc/dev_journal.md) KLDetect is a keylogger detector for the Linux Desktop.
It can detect processes reading from ```/dev/input/event*``` devices and kernel modules registered to listen to keyboard events.
# Dependencies
* [Python](https://www.python.org/downloads/)
* [SystemTap](https://sourceware.org/systemtap/wiki)
* [```fuser```](https://www.man7.org/linux/man-pages/man1/fuser.1.html)
* Utilities that come with [Fedora](https://fedoraproject.org/) like ```which```.
# Setup
Download or clone this repository:
```
git clone https://github.com/sebaschi/keylogger-detector.git
```
Navigate into the src directory:
```
cd keylogger-detector/src
```
Run a keylogger. KLDetect has been tested and shown to work on the following keylogger.
User progams:
* [simple-key-logger](https://github.com/gsingh93/simple-key-logger/tree/master)
* [logkeys](https://github.com/kernc/logkeys)
* [keylog](https://github.com/SCOTPAUL/keylog)
Kernel Module:
* [spy](https://github.com/jarun/spy)
# Usage
KLDetect **must** be run as root (sudo).
If KLDetect is not run as root the user is reminded to try again with root permissions.
Running without options just runs userspace detection:
```
./kldetect.py
```
To get a list of command line options:
```
./kldetect.py -h
```
To run with kernel module detection:
```
./kldetect.py -k
```
To run just kernel module detection
```
./kernel_detector.py
```
# Warning
Running any part if this program in a lightheaded manner may break your system.
Killing processes and unloading modules should be done with caution. We suggest testing it an a VM.
If one runs the KLDetect with the kernel module keylogger detection option set, make sure to update the [whitelist.txt](https://github.com/sebaschi/keylogger-detector/blob/main/src/whitelist.txt) with the safe kernel modules that you know you have on your system. In particular we highly suggest running ```lsmod > <path-to-kldetect>/whitelist.txt```, before inserting a kernel keylogger. This writes the modules currently inserted in the kernel to the whitelist. This way 'normal' modules that you already have installed on the 'clean' kernel will not accidentally be unloaded. Altough KLDetect should not unload any kernel modules currently used, better safe than sorry.
# Developers
Copyright © 2023[Michel Romancuk](https://github.com/SoulKindred), [Sebastian Lenzlinger](https://github.com/sebaschi)
This project is Part of a Univeristy project at the [Operating Systems](https://dmi.unibas.ch/de/studium/computer-science-informatik/lehrangebot-fs23/vorlesung-operating-systems-1/) lecture at the University of Basel, Switzerland.
A project journal can be found [here](https://github.com/sebaschi/keylogger-detector/blob/main/doc/dev_journal.md).

View File

@ -166,7 +166,7 @@ This is after extensivly refactoring because I was starting to loose oversight o
## Wednesday, 7. June 2023, day ## Wednesday, 7. June 2023, day
### Sebastian ### Sebastian
VirtualBox stopped working so after much pain I decided to switch to Boxes. There the install of Fedora 37 went smoothly. VirtualBox stopped working so after much pain I decided to switch to Boxes. There the install of Fedora 37 went smoothly.
Then Started testing the userland detector on [simple-key-logger](https://github.com/gsingh93/simple-key-logger/tree/maste), [logkeys](https://github.com/kernc/logkeys). Then Started testing the userland detector on [simple-key-logger](https://github.com/gsingh93/simple-key-logger/tree/master), [logkeys](https://github.com/kernc/logkeys).
[pykeylogger](https://github.com/amoffat/pykeylogger) produced a segmentation fault, after I finaly got it to run. Trying to run [py-keylogger](https://github.com/hiamandeep/py-keylogger), turns out it only runs on X11 it seem (so we'd not catch it anyway). [pykeylogger](https://github.com/amoffat/pykeylogger) produced a segmentation fault, after I finaly got it to run. Trying to run [py-keylogger](https://github.com/hiamandeep/py-keylogger), turns out it only runs on X11 it seem (so we'd not catch it anyway).
[keylog](https://github.com/SCOTPAUL/keylog) was succesfully detected and removed. [keylog](https://github.com/SCOTPAUL/keylog) was succesfully detected and removed.
All in all, the main functionality works as intended. Basically now would be the refinement phase to add more options or to have a way to configure the config.json file more easily. All in all, the main functionality works as intended. Basically now would be the refinement phase to add more options or to have a way to configure the config.json file more easily.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,17 @@
@inproceedings{10.1145/2390317.2390326,
author = {Howard, Adam and Hu, Yi},
title = {An Approach for Detecting Malicious Keyloggers},
year = {2012},
isbn = {9781450315388},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/2390317.2390326},
doi = {10.1145/2390317.2390326},
abstract = {Keyloggers are applications that are installed onto computers with the intent of monitoring and storing keystrokes that are input by a user. These keystrokes can either be stored on a physical hard disk or transmitted via a network connection to a remote location. Because of their functions, keyloggers have a potential of being used for malicious purposes. In order to protect privacy, it is important to realize the threat that a keylogger application might pose and identify appropriate methods for detecting it. The method presented in this research provides a standardized approach to detect unknown keylogging software from a computer. We also conducted experiments on a variety of keyloggers to verify the effectiveness of the proposed approach.},
booktitle = {Proceedings of the 2012 Information Security Curriculum Development Conference},
pages = {5356},
numpages = {4},
keywords = {rootkit, privacy, system hook, keylogger, malicious software},
location = {Kennesaw, Georgia},
series = {InfoSecCD '12}
}

View File

@ -0,0 +1,17 @@
@inproceedings{10.1145/2905055.2905362,
author = {Amin, Maitri},
title = {A Survey of Financial Losses Due to Malware},
year = {2016},
isbn = {9781450339629},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/2905055.2905362},
doi = {10.1145/2905055.2905362},
abstract = {General survey stat that the main damage malware can cause is to slow down their PCs and perhaps crash some websites which is quite wrong, The Russian antivirus software developer teamed up with B2B International for a study worldwide recently, shown 36\% of users lose money online as a result of a malware attack. Currently malware can't be detected by traditional way based anti-malware tools due to their polymorphic and/or metamorphic nature. Here we have improvised a current detection technique of malware based on mining Application Programming Interface (API) calls and developed the first public dataset to promote malware research.• In survey of cyber-attacks 6.2\% financial attacks are due to malware which increase to 1.3 \% in 2013 compared to 2012.• Financial data theft causes 27.6\% to reach 28,400,000. Victims abused by this targeting malware countered 3,800,000, which is 18.6\% greater than previous year.• Finance-committed malware, associated with Bitcoin has demonstrated the most dynamic development. Where's, Zeus is still top listed for playing important roles to steal banking credentials.Solutionary study stats that companies are spending a staggering amount of money in the aftermath of damaging attack: DDoS attacks recover $6,500 per hour from malware and more than $3,000 each time for up to 30 days to moderate and improve from malware attacks. [1]},
booktitle = {Proceedings of the Second International Conference on Information and Communication Technology for Competitive Strategies},
articleno = {145},
numpages = {4},
keywords = {Malware, API, financial losses, Survey},
location = {Udaipur, India},
series = {ICTCS '16}
}

View File

@ -0,0 +1,17 @@
@inproceedings{10.1145/3133956.3134067,
author = {Thomas, Kurt and Li, Frank and Zand, Ali and Barrett, Jacob and Ranieri, Juri and Invernizzi, Luca and Markov, Yarik and Comanescu, Oxana and Eranti, Vijay and Moscicki, Angelika and Margolis, Daniel and Paxson, Vern and Bursztein, Elie},
title = {Data Breaches, Phishing, or Malware? Understanding the Risks of Stolen Credentials},
year = {2017},
isbn = {9781450349468},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3133956.3134067},
doi = {10.1145/3133956.3134067},
abstract = {In this paper, we present the first longitudinal measurement study of the underground ecosystem fueling credential theft and assess the risk it poses to millions of users. Over the course of March, 2016--March, 2017, we identify 788,000 potential victims of off-the-shelf keyloggers; 12.4 million potential victims of phishing kits; and 1.9 billion usernames and passwords exposed via data breaches and traded on blackmarket forums. Using this dataset, we explore to what degree the stolen passwords---which originate from thousands of online services---enable an attacker to obtain a victim's valid email credentials---and thus complete control of their online identity due to transitive trust. Drawing upon Google as a case study, we find 7--25\% of exposed passwords match a victim's Google account. For these accounts, we show how hardening authentication mechanisms to include additional risk signals such as a user's historical geolocations and device profiles helps to mitigate the risk of hijacking. Beyond these risk metrics, we delve into the global reach of the miscreants involved in credential theft and the blackhat tools they rely on. We observe a remarkable lack of external pressure on bad actors, with phishing kit playbooks and keylogger capabilities remaining largely unchanged since the mid-2000s.},
booktitle = {Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security},
pages = {14211434},
numpages = {14},
keywords = {keylogger, phishing, risk analysis, data breach, password reuse, authentication, phishing kit, password},
location = {Dallas, Texas, USA},
series = {CCS '17}
}

View File

@ -0,0 +1,10 @@
@article{article,
author = {Creutzburg, Reiner},
year = {2017},
month = {01},
pages = {139-148},
title = {The strange world of keyloggers - an overview, Part I},
volume = {2017},
journal = {Electronic Imaging},
doi = {10.2352/ISSN.2470-1173.2017.6.MOBMU-313}
}

View File

@ -1,8 +1,8 @@
{ {
"white_listed_programs": [ "white_listed_programs": [
"systemd_logind", "systemd_logind",
"gnome-shell", "systemd",
"systemd" "gnome-shell"
], ],
"auto_kill_programs": [], "auto_kill_programs": [],
"kbd_names": [ "kbd_names": [

View File

@ -1,6 +1,7 @@
probe kernel.function("register_keyboard_notifier").call probe kernel.function("register_keyboard_notifier").call
{ {
printf("triggered\n") print("[-]")
exit()
} }

View File

@ -1,8 +0,0 @@
probe kernel.function("register_keyboard_notifier").call
{
printf("%s (%d)\n", execname(),tid())
}
probe end{
printf("end\n")
}

View File

@ -1,58 +0,0 @@
#! /usr/bin/env stap
#
# Copyright (C) 2010-2015 Red Hat, Inc.
# Written by William Cohen <wcohen@redhat.com>
#
# The linetimes.stp script takes two arguments: where to find the function
# and the function name. linetimes.stp will instrument each line in the
# function. It will print out the number of times that the function is
# called, a table with the average and maximum time each line takes,
# and control flow information when the script exits.
#
# For example all the lines of the do_unlinkat function:
#
# stap linetimes.stp kernel do_unlinkat
global calls, times, last_pp, region, cfg
probe $1.function(@2).call { calls <<< 1 }
probe $1.function(@2).return {
t = gettimeofday_us()
s = times[tid()]
if (s) {
e = t - s
region[last_pp[tid()]] <<< e
cfg[last_pp[tid()], pp()] <<< 1
}
delete times[tid()]
delete last_pp[tid()]
}
probe $1.statement(@2 "@*:*") {
t = gettimeofday_us()
s = times[tid()]
if (s) {
e = t - s
region[last_pp[tid()]] <<< e
cfg[last_pp[tid()], pp()] <<< 1
}
times[tid()] = t
last_pp[tid()] = pp()
}
probe end {
printf("\n%s %s call count: %d\n", @1, @2, @count(calls));
printf("\n%-58s %10s %10s\n", "region", "avg(us)", "max(us)");
foreach (p+ in region) {
printf("%-58s %10d %10d\n", p, @avg(region[p]), @max(region[p]));
}
printf("\n\ncontrol flow graph information\n")
printf("from\n\tto\n=======================\n")
foreach ([src+] in region) {
printf("%-s\n", src)
foreach ([s,dest+] in cfg[src,*]) { # slice for all dest's
printf("\t%-s %d\n", dest, @count(cfg[src,dest]));
}
}
}

View File

@ -1,9 +1,16 @@
#!/usr/bin/env python3
import subprocess import subprocess
import time import time
import multiprocessing import multiprocessing
import os
global Smell import threading
import os
import sys
from io import TextIOWrapper, BytesIO
pipe1, pipe2 = multiprocessing.Pipe()
#============================================================================================================== #==============================================================================================================
# #
@ -59,34 +66,24 @@ def unload_mod(modules):
return result_out return result_out
#TODO Get Return-value from start_stap()
def start_stap():
print("Starting Sniffer")
output = subprocess.Popen(['stap','funcall_trace.stp'],universal_newlines=True)
if output.stdout != "":
output.terminate()
print("fishy")
Smell = "fishy"
else:
output.terminate()
print("nothing fishy")
Smell = "not fishy"
print(Smell + " smell")
def stap_start():
print("starting sniffing")
process = subprocess.Popen(['stap','funcall_trace.stp', '-T', '15'], flush = True)
process.wait()
print("ended sniffing")
def load_mod(module): def load_mod(module):
result = subprocess.run(['sudo','insmod', module],capture_output = True, text = True) print(module)
if result.returncode == 0: for i in range(2):
print(f"Loaded module: {module}") subprocess.Popen(['sudo','insmod', module])
time.sleep(5) time.sleep(1)
else: subprocess.Popen(['sudo','rmmod', module])
print(f"Failed to Loaded module: {module}") time.sleep(1)
print(result.stderr) subprocess.Popen(['sudo', 'insmod', module])
def find_file(filename): def find_file(filename):
@ -95,7 +92,7 @@ def find_file(filename):
if filename in files: if filename in files:
file_path = os.path.join(root, filename) file_path = os.path.join(root, filename)
result.append(file_path) result.append(file_path)
result_out = result[0] result_out = result
result_out = ''.join(result_out) result_out = ''.join(result_out)
return result_out return result_out
@ -105,15 +102,34 @@ def getpath(sus_modules):
return sus_modules return sus_modules
def detect_logger(module): def detect_logger(module):
p1 = multiprocessing.Process(target=start_stap)
p1.start()
p2 = multiprocessing.Process(target=load_mod(module))
p2.start()
p1.join()
p2.join()
print("starting sniffing")
process = subprocess.Popen(['stap','funcall_trace.stp', '-T', '10'], stdout=subprocess.PIPE, text=True)
for i in range(2):
subprocess.Popen(['sudo','insmod', module])
time.sleep(1)
print("-")
subprocess.Popen(['sudo','rmmod', module])
time.sleep(1)
subprocess.Popen(['sudo','insmod', module])
print("-")
out = process.communicate()[0]
print("ended sniffing")
print(out)
if out == "[-]":
return module
print("FAILED")
return 0
#============================================================================================================== #==============================================================================================================
@ -129,21 +145,39 @@ lsmod_output = list_modules("lsmod");
sus_modules = compare_mods(whitelist, lsmod_output) sus_modules = compare_mods(whitelist, lsmod_output)
sus_modules = tidy_up(sus_modules) sus_modules = tidy_up(sus_modules)
print(sus_modules)
sus_modules = unload_mod(sus_modules) sus_modules = unload_mod(sus_modules)
time.sleep(1) time.sleep(1)
print("waited")
sus_modules = getpath(sus_modules) sus_modules = getpath(sus_modules)
print(sus_modules) print(sus_modules)
if len(sus_modules) == 0: if len(sus_modules) == 0:
exit() print("nothing to do")
print("ALL CLEAN")
suspects = [] suspects = []
for module in range(len(sus_modules)): for module in sus_modules:
suspects.append(detect_logger(sus_modules[module])) suspects.append(detect_logger(module))
time.sleep(1)
print("Following modules are logging your keystrokes: ")
for i in range(len(suspects)):
print( f"[{i}] {suspects[i]}")
print("Enter the number of the module you want to remove: ")
user_input = input().split()
for j in user_input:
to_remove = suspects[int(j)]
subprocess.Popen(['sudo','rmmod', to_remove])
print(f"Removed {to_remove}")
print("Finished")
print(suspects)

View File

@ -1,5 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import subprocess
import time
import os
import sys import sys
from config import CONFIG_FILE, load_config, save_config from config import CONFIG_FILE, load_config, save_config
from utils import ( from utils import (
@ -22,6 +24,8 @@ safe_option = False
add_white_list_option = False add_white_list_option = False
add_black_list_option = False add_black_list_option = False
debug_option = False debug_option = False
kernel_detection_option = False
# Functions # Functions
def debug(option, to_print): def debug(option, to_print):
@ -35,9 +39,10 @@ def print_help():
print(' -v, --verbose\t\t\tVerbose mode. Informative information will be displayed duting execution') print(' -v, --verbose\t\t\tVerbose mode. Informative information will be displayed duting execution')
print(' -a, --auto-kill\t\tAutomatically kill blacklisted processes') print(' -a, --auto-kill\t\tAutomatically kill blacklisted processes')
print(' -s, --safe\t\t\tSafe mode. Asked to confirm before killing a process') print(' -s, --safe\t\t\tSafe mode. Asked to confirm before killing a process')
print(' -w, --add-white-list\t\t\tActivate prompt to add program names to the whitelist') #For some reason this line gets messed up in display print(' -w, --add-white-list\t\tActivate prompt to add program names to the whitelist') #For some reason this line gets messed up in display
print(' -b, --add-black-list\t\t\tAutomatically add program names chosen to kill to the blacklist') print(' -b, --add-black-list\t\tAutomatically add program names chosen to kill to the blacklist')
print(' -d, --debug\t\t\tDebug mode. Print debug statements') print(' -d, --debug\t\t\tDebug mode. Print debug statements')
print(' -k, --kernel-detection\t\tRun the kernel keylogger detector, too. CURRENTLY NOT IMPLEMENTED TO DIRECTLY RUN KERNEL DETECTOR.')
def set_input_options(): def set_input_options():
""" """
@ -50,7 +55,7 @@ def set_input_options():
""" """
global auto_kill_option, verbose_option, safe_option, add_white_list_option global auto_kill_option, verbose_option, safe_option, add_white_list_option
global debug_option, add_black_list_option global debug_option, add_black_list_option, kernel_detection_option
if len(sys.argv) > 1: if len(sys.argv) > 1:
for arg in sys.argv[1:]: for arg in sys.argv[1:]:
print(arg) print(arg)
@ -69,6 +74,8 @@ def set_input_options():
add_black_list_option = True add_black_list_option = True
elif arg == '-d' or arg == '--debug': elif arg == '-d' or arg == '--debug':
debug_option = True debug_option = True
elif arg == '-k' or arg == '--kernel-detection':
kernel_detection_option = True
def confirm_kill_procces(process_name, times=0): def confirm_kill_procces(process_name, times=0):
@ -101,6 +108,176 @@ def confirm_kill_procces(process_name, times=0):
else: else:
return confirm_kill_procces(process_name, times+1) return confirm_kill_procces(process_name, times+1)
def detect_kernel(module):
"""
Start the systemtap-script.
load and unload modules twice.
load module when finished.
Args:
module(str): Path + name of the module being tested
Returns:
String: Path + name of the module that is logging keystrokes
"""
if verbose_option:
print('[Verbose] Started kernel keylogger detection')
process = subprocess.Popen(['stap','funcall_trace.stp', '-T', '10'], stdout=subprocess.PIPE, text=True)
for i in range(2):
subprocess.Popen(['sudo','insmod', module])
time.sleep(1)
print(".", end="")
subprocess.Popen(['sudo','rmmod', module])
time.sleep(1)
subprocess.Popen(['sudo','insmod', module])
print(".")
out = process.communicate()[0]
if verbose_option:
print('[Verbose] Started kernel keylogger detection')
print(out)
if out == "[-]":
return module
print("FAILED")
return 0
def getpath(sus_modules):
"""
Gets the path of a list of modules being tested
calls "find_file()" function
Args:
List[module(str)] List of all modules being tested
Returns:
List[modules(str)]List of the Path of all modules being tested
"""
for i in range(len(sus_modules)):
sus_modules[i] = find_file(sus_modules[i] + ".ko")
return sus_modules
def find_file(filename):
"""
Searches for a file begining at root
Args:
filename(str) The filename one is looking for
Returns:
result_out(str) 'The Path_to_Module/Module_name'
"""
result = []
for root, dirs, files in os.walk("/"):
if filename in files:
file_path = os.path.join(root, filename)
result.append(file_path)
result_out = result
result_out = ''.join(result_out)
return result_out
def unload_mod(modules):
"""
Unloads modules.
Args:
module(str) the module that needs to be unloaded. Has to be Path_to_Module/Module_name
"""
tmp = []
for module in modules:
result = subprocess.run(['sudo','rmmod', module],capture_output = True, text = True)
if result.returncode == 0:
if verbose_option:
print(f"[Verbose] Unloaded module: {module}")
else:
if verbose_option:
print(f"[Verbose] Failed to unloaded module: {module}")
print("[Verbose] " + result.stderr)
tmp.append(module)
result_out = compare_mods(tmp, modules)
if verbose_option:
print("[Verbose] ", end="")
print(result_out)
return result_out
def tidy_up(entries):
"""
Takes a txt file and removes everything except the first word of a line
Args:
File(.txt) in this usecase a whitelist.txt
Returns:
clean_entries(List[str]) List of only the first wrod from each line
"""
cleaned_entries = []
for entry in entries:
modules = entry.split()
if modules:
first_mod = modules[0]
cleaned_entries.append(first_mod)
return cleaned_entries
def compare_mods(A, B):
"""
Does set-suptraction to.
Args:
A(list[str]) List of elements one wants to ignore
B(list[str]) List of elements that one wants without all elements in A
Returns:
result(list[str] List of elements that are in B but not in A
"""
setA = set(A)
setB = set(B)
result = setB - setA
return list(result)
def get_whitelist(file_path):
"""
reads a text-file
Args:
file_path(str) Path to file one wants to read
Returns:
lines(list[str]) List of each line from a file
"""
try:
with open(file_path, 'r') as file:
lines = file.read().splitlines()
return lines
except IOError:
print(f'Error: Failed to load whitelist{file_path}')
def list_modules(command):
"""
Calls a command in terminal
Args:
command(str) the command one wants to execute
Returns:
result(list[std]) List of each line the command has as an output.
"""
result = subprocess.run(command, shell = True, capture_output=True, text=True)
if result.returncode == 0:
return result.stdout.strip().split('\n')
else:
print(f"Failed with error:{result.stderr}")
return[]
def detect_keyloggers(): def detect_keyloggers():
""" """
Detect (userland) keylogger processes based on which processes have a keyboard file open (/dev/input/event*) Detect (userland) keylogger processes based on which processes have a keyboard file open (/dev/input/event*)
@ -116,7 +293,7 @@ def detect_keyloggers():
debug(True, str(sys.argv)) # Set manually to debug if args are being read debug(True, str(sys.argv)) # Set manually to debug if args are being read
check_platform() check_platform()
global auto_kill_option, verbose_option, safe_option global auto_kill_option, verbose_option, safe_option, add_white_list_option, kernel_detection_option, debug_option
set_input_options() set_input_options()
if verbose_option: if verbose_option:
print('[Verbose] Input options set') print('[Verbose] Input options set')
@ -205,7 +382,8 @@ def detect_keyloggers():
############################ ############################
if len(suspicious_processes) == 0: if len(suspicious_processes) == 0:
print("[+] No suspicious processes found") print("[+] No suspicious processes found")
sys.exit(0) if not kernel_detection_option:
exit(0)
############################ ############################
# 7. Prompt user to chose which processes (not covered by auto kill if set) to kill # 7. Prompt user to chose which processes (not covered by auto kill if set) to kill
@ -282,11 +460,60 @@ def detect_keyloggers():
if verbose_option: if verbose_option:
print('[Verbose] Config file saved') print('[Verbose] Config file saved')
print('[+] Program completed. Exiting.')
debug(debug_option, 'Kernel detection option: ' + str(kernel_detection_option))
###########################
# 10. If kernel_detection_option is set, run kernel detection
###########################
if kernel_detection_option:
whitelist = get_whitelist("whitelist.txt")
lsmod_output = list_modules("lsmod")
sus_modules = compare_mods(whitelist, lsmod_output)
sus_modules = tidy_up(sus_modules)
sus_modules = unload_mod(sus_modules)
time.sleep(1)
sus_modules = getpath(sus_modules)
suspects = []
if verbose_option:
print("[Verbose] ", end="")
print(sus_modules)
if len(sus_modules) == 0 and verbose_option:
print("[Verbose] Nothing to do")
for module in sus_modules:
if module == '': #if modules have an empty path, they are in root
break
suspects.append(detect_kernel(module))
time.sleep(1)
print("Following modules are logging your keystrokes: ")
for i in range(len(suspects)):
print( f"[{i}] {suspects[i]}")
print("Enter the number of the module you want to remove: ")
user_input = input().split()
to_remove = []
for j in user_input:
to_remove = suspects[int(j)]
subprocess.Popen(['sudo','rmmod', to_remove])
if len(to_remove) < 1:
print(f"Removed {to_remove}")
print('[+] Program completed. Exiting.')
if __name__ == '__main__': if __name__ == '__main__':
detect_keyloggers() detect_keyloggers()

View File

@ -1,5 +1,4 @@
Module Size Used by Module Size Used by
tls 147456 0
uinput 20480 0 uinput 20480 0
isofs 65536 1 isofs 65536 1
snd_seq_dummy 16384 0 snd_seq_dummy 16384 0
@ -23,26 +22,26 @@ nf_conntrack 192512 4 nf_nat,nft_ct,nf_conntrack_netbios_ns,nf_conntra
nf_defrag_ipv6 24576 1 nf_conntrack nf_defrag_ipv6 24576 1 nf_conntrack
nf_defrag_ipv4 16384 1 nf_conntrack nf_defrag_ipv4 16384 1 nf_conntrack
ip_set 65536 0 ip_set 65536 0
rfkill 40960 3
nf_tables 352256 237 nft_ct,nft_reject_inet,nft_fib_ipv6,nft_fib_ipv4,nft_chain_nat,nft_reject,nft_fib,nft_fib_inet nf_tables 352256 237 nft_ct,nft_reject_inet,nft_fib_ipv6,nft_fib_ipv4,nft_chain_nat,nft_reject,nft_fib,nft_fib_inet
nfnetlink 20480 3 nf_tables,ip_set nfnetlink 20480 3 nf_tables,ip_set
rfkill 40960 3
qrtr 57344 4 qrtr 57344 4
sunrpc 815104 1
snd_intel8x0 57344 2 snd_intel8x0 57344 2
snd_ac97_codec 200704 1 snd_intel8x0 snd_ac97_codec 200704 1 snd_intel8x0
ac97_bus 16384 1 snd_ac97_codec
snd_seq 106496 7 snd_seq_dummy
intel_rapl_msr 20480 0
snd_seq_device 16384 1 snd_seq
intel_rapl_common 36864 1 intel_rapl_msr
snd_pcm 184320 2 snd_intel8x0,snd_ac97_codec
snd_timer 53248 3 snd_seq,snd_hrtimer,snd_pcm
sunrpc 815104 1
rapl 24576 0
binfmt_misc 28672 1 binfmt_misc 28672 1
intel_rapl_msr 20480 0
ac97_bus 16384 1 snd_ac97_codec
intel_rapl_common 36864 1 intel_rapl_msr
snd_seq 106496 7 snd_seq_dummy
snd_seq_device 16384 1 snd_seq
snd_pcm 184320 2 snd_intel8x0,snd_ac97_codec
rapl 24576 0
snd_timer 53248 3 snd_seq,snd_hrtimer,snd_pcm
snd 143360 12 snd_seq,snd_seq_device,snd_intel8x0,snd_timer,snd_ac97_codec,snd_pcm snd 143360 12 snd_seq,snd_seq_device,snd_intel8x0,snd_timer,snd_ac97_codec,snd_pcm
joydev 28672 0 joydev 28672 0
pcspkr 16384 0
soundcore 16384 1 snd soundcore 16384 1 snd
pcspkr 16453 0
i2c_piix4 36864 0 i2c_piix4 36864 0
vboxguest 53248 6 vboxguest 53248 6
loop 40960 0 loop 40960 0
@ -51,18 +50,19 @@ crct10dif_pclmul 16384 1
crc32_pclmul 16384 0 crc32_pclmul 16384 0
crc32c_intel 24576 3 crc32c_intel 24576 3
polyval_generic 16384 0 polyval_generic 16384 0
video 73728 0
ghash_clmulni_intel 16384 0
vmwgfx 458752 2 vmwgfx 458752 2
ghash_clmulni_intel 16384 0
drm_ttm_helper 16384 3 vmwgfx,drm_vram_helper,vboxvideo drm_ttm_helper 16384 3 vmwgfx,drm_vram_helper,vboxvideo
wmi 45056 1 video
sha512_ssse3 49152 0 sha512_ssse3 49152 0
e1000 188416 0 e1000 188416 0
serio_raw 20480 0
ttm 102400 3 vmwgfx,drm_vram_helper,drm_ttm_helper ttm 102400 3 vmwgfx,drm_vram_helper,drm_ttm_helper
serio_raw 20480 0
video 73728 0
wmi 45056 1 video
ata_generic 16384 0 ata_generic 16384 0
pata_acpi 16384 0 pata_acpi 16384 0
ip6_tables 40960 0 ip6_tables 40960 0
ip_tables 40960 0 ip_tables 40960 0
fuse 212992 5 fuse 212992 5
end