Compare commits
10 Commits
SoulKindre
...
SoulKindre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02f6d6eba0 | ||
|
|
1988f0c021 | ||
|
|
da25db0825 | ||
|
|
1635c68ade | ||
|
|
d84a0717bc | ||
|
|
4a9af2f04c | ||
|
|
1ea740dfd3 | ||
|
|
e9d283f94e | ||
|
|
00c2b25a27 | ||
|
|
c0893c31e6 |
21
LICENSE
21
LICENSE
@@ -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.
|
|
||||||
@@ -177,4 +177,6 @@ All in all, the main functionality works as intended. Basically now would be the
|
|||||||
## Wednesday, 7, June 2023
|
## Wednesday, 7, June 2023
|
||||||
### Michel
|
### Michel
|
||||||
|
|
||||||
|
|
||||||
I have written 1 systemtap scripts, that can detect, whenever a module registers at the Keyboard-notifier. The Script can currently detect whenever a module registers. My script cant detect which kernel module registered. Here comes Sebastians idea of writing a python script, that can unload all un-known modules and loads them back in, whilst the stap-script is running. Whenever a module is loaded in, and it triggers the stap-script, we know it is tracking key-strokes. Those modules will be shown to the user and the user then has to decide whether to unload and remove them, or keep them. My script is based on a redhat-script. The redhat-script is called funcall_tracer2.stp . The idea behind both scripts is the same. My script is simplyfied for the use with python.
|
I have written 1 systemtap scripts, that can detect, whenever a module registers at the Keyboard-notifier. The Script can currently detect whenever a module registers. My script cant detect which kernel module registered. Here comes Sebastians idea of writing a python script, that can unload all un-known modules and loads them back in, whilst the stap-script is running. Whenever a module is loaded in, and it triggers the stap-script, we know it is tracking key-strokes. Those modules will be shown to the user and the user then has to decide whether to unload and remove them, or keep them. My script is based on a redhat-script. The redhat-script is called funcall_tracer2.stp . The idea behind both scripts is the same. My script is simplyfied for the use with python.
|
||||||
|
|
||||||
|
|||||||
7
src/funcall_trace.stp
Normal file
7
src/funcall_trace.stp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
probe kernel.function("register_keyboard_notifier").call
|
||||||
|
{
|
||||||
|
print("[-]")
|
||||||
|
exit()
|
||||||
|
}
|
||||||
|
|
||||||
8
src/funcall_trace1.stp
Normal file
8
src/funcall_trace1.stp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
probe kernel.function("register_keyboard_notifier").call
|
||||||
|
{
|
||||||
|
printf("%s (%d)\n", execname(),tid())
|
||||||
|
}
|
||||||
|
probe end{
|
||||||
|
printf("end\n")
|
||||||
|
}
|
||||||
58
src/funcall_trace2.stp
Normal file
58
src/funcall_trace2.stp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#! /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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
src/kernel_detector.py
Normal file
183
src/kernel_detector.py
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import multiprocessing
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from io import TextIOWrapper, BytesIO
|
||||||
|
|
||||||
|
pipe1, pipe2 = multiprocessing.Pipe()
|
||||||
|
|
||||||
|
#==============================================================================================================
|
||||||
|
#
|
||||||
|
#Functions
|
||||||
|
#
|
||||||
|
#==============================================================================================================
|
||||||
|
def list_modules(command):
|
||||||
|
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 get_whitelist(file_path):
|
||||||
|
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 compare_mods(A, B):
|
||||||
|
setA = set(A)
|
||||||
|
setB = set(B)
|
||||||
|
|
||||||
|
result = setB - setA
|
||||||
|
|
||||||
|
return list(result)
|
||||||
|
|
||||||
|
def tidy_up(entries):
|
||||||
|
cleaned_entries = []
|
||||||
|
for entry in entries:
|
||||||
|
modules = entry.split()
|
||||||
|
if modules:
|
||||||
|
first_mod = modules[0]
|
||||||
|
cleaned_entries.append(first_mod)
|
||||||
|
return cleaned_entries
|
||||||
|
|
||||||
|
def unload_mod(modules):
|
||||||
|
tmp = []
|
||||||
|
for module in modules:
|
||||||
|
result = subprocess.run(['sudo','rmmod', module],capture_output = True, text = True)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print(f"Unloaded module: {module}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to unloaded module: {module}")
|
||||||
|
tmp.append(module)
|
||||||
|
print(result.stderr)
|
||||||
|
result_out = compare_mods(tmp, modules)
|
||||||
|
print(result_out)
|
||||||
|
return result_out
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
print(module)
|
||||||
|
for i in range(2):
|
||||||
|
subprocess.Popen(['sudo','insmod', module])
|
||||||
|
time.sleep(1)
|
||||||
|
subprocess.Popen(['sudo','rmmod', module])
|
||||||
|
time.sleep(1)
|
||||||
|
subprocess.Popen(['sudo', 'insmod', module])
|
||||||
|
|
||||||
|
|
||||||
|
def find_file(filename):
|
||||||
|
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 getpath(sus_modules):
|
||||||
|
for i in range(len(sus_modules)):
|
||||||
|
sus_modules[i] = find_file(sus_modules[i] + ".ko")
|
||||||
|
return sus_modules
|
||||||
|
|
||||||
|
def detect_logger(module):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================================================
|
||||||
|
#
|
||||||
|
#Work
|
||||||
|
#
|
||||||
|
#==============================================================================================================
|
||||||
|
|
||||||
|
def run_kernel_detection:
|
||||||
|
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)
|
||||||
|
print(sus_modules)
|
||||||
|
if len(sus_modules) == 0:
|
||||||
|
print("nothing to do")
|
||||||
|
print("ALL CLEAN")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
suspects = []
|
||||||
|
for module in sus_modules:
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
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 (
|
||||||
|
check_platform,
|
||||||
check_root,
|
check_root,
|
||||||
check_packages,
|
check_packages,
|
||||||
get_keyboard_device_files,
|
get_keyboard_device_files,
|
||||||
@@ -113,8 +114,9 @@ def detect_keyloggers():
|
|||||||
# 1. Setup and initialization
|
# 1. Setup and initialization
|
||||||
############################
|
############################
|
||||||
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()
|
||||||
|
|
||||||
global auto_kill_option, verbose_option, safe_option
|
global auto_kill_option, verbose_option, safe_option
|
||||||
global CONFIG_FILE
|
|
||||||
set_input_options()
|
set_input_options()
|
||||||
if verbose_option:
|
if verbose_option:
|
||||||
print('[Verbose] Input options set')
|
print('[Verbose] Input options set')
|
||||||
|
|||||||
14
src/utils.py
14
src/utils.py
@@ -3,6 +3,20 @@ import subprocess # for executing shell commands
|
|||||||
import signal # for sending signals to processes
|
import signal # for sending signals to processes
|
||||||
import sys # for exit
|
import sys # for exit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_platform():
|
||||||
|
"""
|
||||||
|
Check if platform is Linux.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SystemExit: If the platform isn not LInux.
|
||||||
|
"""
|
||||||
|
if sys.platform != 'linux':
|
||||||
|
print("[-] This script only works on Linux.")
|
||||||
|
|
||||||
|
|
||||||
def check_root():
|
def check_root():
|
||||||
"""
|
"""
|
||||||
Check if script is run as root(sudo).
|
Check if script is run as root(sudo).
|
||||||
|
|||||||
68
src/whitelist.txt
Normal file
68
src/whitelist.txt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
Module Size Used by
|
||||||
|
uinput 20480 0
|
||||||
|
isofs 65536 1
|
||||||
|
snd_seq_dummy 16384 0
|
||||||
|
snd_hrtimer 16384 1
|
||||||
|
vboxvideo 36864 0
|
||||||
|
drm_vram_helper 24576 1 vboxvideo
|
||||||
|
nf_conntrack_netbios_ns 16384 1
|
||||||
|
nf_conntrack_broadcast 16384 1 nf_conntrack_netbios_ns
|
||||||
|
nft_fib_inet 16384 1
|
||||||
|
nft_fib_ipv4 16384 1 nft_fib_inet
|
||||||
|
nft_fib_ipv6 16384 1 nft_fib_inet
|
||||||
|
nft_fib 16384 3 nft_fib_ipv6,nft_fib_ipv4,nft_fib_inet
|
||||||
|
nft_reject_inet 16384 6
|
||||||
|
nf_reject_ipv4 16384 1 nft_reject_inet
|
||||||
|
nf_reject_ipv6 24576 1 nft_reject_inet
|
||||||
|
nft_reject 16384 1 nft_reject_inet
|
||||||
|
nft_ct 24576 16
|
||||||
|
nft_chain_nat 16384 3
|
||||||
|
nf_nat 65536 1 nft_chain_nat
|
||||||
|
nf_conntrack 192512 4 nf_nat,nft_ct,nf_conntrack_netbios_ns,nf_conntrack_broadcast
|
||||||
|
nf_defrag_ipv6 24576 1 nf_conntrack
|
||||||
|
nf_defrag_ipv4 16384 1 nf_conntrack
|
||||||
|
ip_set 65536 0
|
||||||
|
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
|
||||||
|
rfkill 40960 3
|
||||||
|
qrtr 57344 4
|
||||||
|
sunrpc 815104 1
|
||||||
|
snd_intel8x0 57344 2
|
||||||
|
snd_ac97_codec 200704 1 snd_intel8x0
|
||||||
|
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
|
||||||
|
joydev 28672 0
|
||||||
|
soundcore 16384 1 snd
|
||||||
|
pcspkr 16453 0
|
||||||
|
i2c_piix4 36864 0
|
||||||
|
vboxguest 53248 6
|
||||||
|
loop 40960 0
|
||||||
|
zram 32768 2
|
||||||
|
crct10dif_pclmul 16384 1
|
||||||
|
crc32_pclmul 16384 0
|
||||||
|
crc32c_intel 24576 3
|
||||||
|
polyval_generic 16384 0
|
||||||
|
vmwgfx 458752 2
|
||||||
|
ghash_clmulni_intel 16384 0
|
||||||
|
drm_ttm_helper 16384 3 vmwgfx,drm_vram_helper,vboxvideo
|
||||||
|
sha512_ssse3 49152 0
|
||||||
|
e1000 188416 0
|
||||||
|
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
|
||||||
|
pata_acpi 16384 0
|
||||||
|
ip6_tables 40960 0
|
||||||
|
ip_tables 40960 0
|
||||||
|
fuse 212992 5
|
||||||
|
end
|
||||||
|
|
||||||
Reference in New Issue
Block a user