jonbttt/rk-veil
GitHub: jonbttt/rk-veil
面向 Linux 6.x 内核的 LKM rootkit 安全研究项目,演示了 syscall 挂钩、进程/文件隐藏与权限提升等核心技术的实现原理与检测方法。
Stars: 3 | Forks: 0
# rk-veil
一个简单的 Linux 可加载内核模块 (LKM) rootkit,目标是 kernel 6.x,作为一个安全研究和作品集项目而构建。它演示了核心的 rootkit 技术,包括 syscall 表挂钩、模块自我隐藏、文件和进程隐藏以及权限提升。
## 功能
| 功能 | 机制 |
|---|---|
| 模块自我隐藏 | 从 kernel 模块列表中解除链接,移除 `/sys/module/` 条目 |
| 文件隐藏 | `getdents64` 挂钩剥离以 `rkveil_` 为前缀的条目 |
| 进程隐藏 | `execve` 挂钩根据 `argv[0]` 前缀自动隐藏进程 |
| 权限提升 | `kill` 挂钩在接收到信号 64 时触发 `commit_creds` |
## 工作原理
### syscall 表解析
在 kernel >= 5.7 上,`kallsyms_lookup_name()` 不再导出给模块。rk-veil 通过在该符号上注册 kprobe,在运行时对其进行解析。kernel 使用函数地址填充 `kp.addr`,随后将其转换为函数指针,并直接用于定位 `sys_call_table`。
### 写保护绕过
在 kernel >= 5.4 上,syscall 表页在页表条目级别被标记为只读,这使得传统的 CR0 WP-bit 方法不再有效。rk-veil 使用 `lookup_address()` 查找 syscall 表页的 PTE,并在修补前直接设置 `_PAGE_RW` 位,之后将其恢复。
### 模块自我隐藏
`THIS_MODULE` 从 kernel 的双向链表模块列表中解除链接,使其对 `lsmod` 和 `/proc/modules` 不可见。`kobject_del()` 移除 `/sys/module/` 条目。
### 文件和进程隐藏
`getdents64` 挂钩将 dirent 缓冲区复制到 kernel 空间,剥离任何名称以 `rkveil_` 开头或数字名称匹配隐藏 PID 的条目,然后将修改后的缓冲区复制回 userspace。文件和 `/proc/` 条目都通过这种方式隐藏。
### 自动进程隐藏
`execve` 挂钩在新进程映像加载之前检查 `argv[0]`。如果它以 `rkveil_` 开头,则在成功执行 exec 后将该 PID 添加到隐藏列表。`exit_group` 挂钩在进程退出时移除该 PID,以防止出现过期条目。
### 权限提升
`kill` 挂钩拦截信号 64(一个未使用的实时信号)作为后门触发器。收到信号后,`prepare_creds()` 克隆调用进程的凭证,所有 uid/gid 字段都被清零,`commit_creds()` 原子性地将它们替换进去。该信号被消耗并且永远不会被传递。
## 用法
### 前置条件
- Linux kernel 6.x (在 6.2.0-25-generic 上测试)
- `linux-headers-$(uname -r)`
- `build-essential`
### 构建
```
make
```
### 加载
```
sudo insmod rk_veil.ko
sudo dmesg | grep rk-veil
```
### 隐藏文件或目录
```
# 任何以 rkveil_ 为前缀的文件或目录都会对 ls、find 等隐藏。
touch rkveil_secret.txt
ls | grep rkveil # returns nothing
```
### 隐藏进程
```
# 使用以 rkveil_ 为前缀的 argv[0] 启动任何进程
(exec -a "rkveil_backdoor" sleep 60) &
ps aux | grep rkveil # returns nothing
```
### 权限提升
```
whoami # vagrant (or any unprivileged user)
kill -64 1
whoami # root
```
### 卸载
```
# 注意:加载后 rmmod 将会失败,因为该模块已从列表中隐藏。
# 重启 VM 以干净地卸载。
sudo reboot
```
## 测试
包含完整的行为测试套件:
```
sudo bash test.sh
```
测试涵盖模块隐藏、文件隐藏、进程自动隐藏、多个同时隐藏的进程、权限提升、syscall 表解析以及正常信号传递。
预期结果:**18/18 通过**。
## Kernel 兼容性
| Kernel | 状态 |
|---|---|
| 6.2.x | 已测试,正常工作 |
| 5.7 -- 6.1 | 应该可以工作(支持 kprobe 解析,需要 PTE 绕过) |
| < 5.7 | 不受支持(kallsyms_lookup_name 曾被导出,需要使用不同的方法) |
## 测试环境
Vagrant + VirtualBox,`bento/ubuntu-22.04` 搭配 kernel `6.2.0-25-generic`。
仓库中包含一个 `Vagrantfile`。要开始使用:
```
vagrant up
vagrant ssh
```
在 `vagrant ssh` 之后,安装特定的 kernel 版本:
```
sudo apt update
sudo apt install -y linux-image-6.2.0-25-generic linux-headers-6.2.0-25-generic
sudo reboot
```
重启后进行验证:
```
uname -r # should show 6.2.0-25-generic
```
## 检测
此 rootkit 可通过以下方式检测:
| 检测方法 | 描述 |
|---|---|
| **syscall 表完整性检查** | 将当前的 syscall 表条目与已知正常值进行比较 |
| **基于 eBPF 的监控** | 像 Falco 或 Tetragon 这样的工具可以检测到异常的 kernel 函数调用,而不管 userspace 的隐藏情况如何 |
| **直接访问 `/proc`** | 隐藏仅限于列表显示;如果 PID 已经知道,进程仍然可以通过直接路径访问 |
| **KASLR 绕过检测** | kprobe 解析技术留下了一个短暂的 kprobe 注册窗口,可以通过 `/sys/kernel/debug/kprobes/` 观察到 |
| **模块列表交叉引用** | 比较 sysfs 中的 `kobject` 条目与模块列表可以揭示隐藏的模块 |
## 参考文献
- [xcellerator 的 Linux Rootkits 系列](https://xcellerator.github.io/posts/linux_rootkits_01/)
- [Linux Kernel 模块编程指南](https://sysprog21.github.io/lkmpg/)
- [Caraxes — 学术 LKM rootkit](https://github.com/ait-aecid/caraxes)
## 作者
[jonbttt](https://github.com/jonbttt)
标签:CSV导出, Linux内核, LKM, Rootkit, Zeek, 协议分析, 子域名枚举, 客户端加密, 应用安全, 权限提升, 私有化部署, 系统安全, 防御规避