Linux应急响应 - 21 Rootkit检测

Rootkit 检测 — 用户态与内核态全面排查

Rootkit 是攻击者用来维持访问权限并隐藏入侵痕迹的工具集合。从简单的命令替换到深入内核的 LKM 模块,Rootkit 的隐蔽性依次递增。本章系统性介绍各类 Rootkit 的检测方法,从手动排查到自动化工具,建立完整的 Rootkit 检测能力。

相关章节:19-LD_PRELOAD劫持 | 14.7-供应链攻击检测

配套实验:labs/18-rootkit/

一、Rootkit 类型分类

1.1 总体分类

类型 层级 隐蔽性 检测难度 典型代表
应用层 Rootkit Ring 3(用户态) 替换 ls/ps/netstat
库级 Rootkit Ring 3(用户态) LD_PRELOAD 劫持
内核模块 Rootkit (LKM) Ring 0(内核态) Diamorphine, Reptile
内核内存 Rootkit Ring 0(内核态) 极高 极高 直接修改 /dev/kmem
Bootkit Ring -1 以下 极高 极高 修改 GRUB/MBR/UEFI
固件 Rootkit 固件层 极高 极高 BIOS/BMC 级别

1.2 用户态 Rootkit

应用层 Rootkit(命令替换)

原理:直接替换 lspsnetstatfindtop 等系统命令为恶意版本

恶意版本在输出中过滤攻击者的进程、文件、网络连接

最古老的 Rootkit 形式,现已较少单独使用

库级 Rootkit(LD_PRELOAD)

原理:通过 LD_PRELOAD 机制注入恶意共享库,Hook libc 函数

不修改系统命令本身,而是在运行时劫持函数调用

详见 19-LD_PRELOAD劫持

1.3 内核态 Rootkit (LKM)

LKM (Loadable Kernel Module) Rootkit

原理:以内核模块(.ko 文件)的形式加载到内核中

可以 Hook 系统调用表(sys_call_table)、VFS 操作、网络栈

在内核层面隐藏进程、文件、网络连接、内核模块自身

普通用户态工具完全无法检测到

典型 LKM Rootkit

名称 特点 功能
Diamorphine 轻量级,流行 隐藏进程/文件/模块,提权
Reptile 功能全面 隐藏、后门、端口碰撞
Adore-Ng 经典 隐藏进程/文件/网络连接
Suterusu 现代化 支持 x86_64、ARM
Kovid 较新 隐藏、持久化、后门

LKM Rootkit 的典型功能

隐藏指定进程(修改 /proc 的遍历结果)

隐藏指定文件和目录(Hook VFS 的 iterate/iterate_shared

隐藏网络连接(Hook /proc/net/tcpseq_show

隐藏内核模块自身(从 lsmod/proc/modules 中消失)

提供 root shell 后门(通过信号触发)

端口碰撞(Port Knocking)触发远程访问

1.4 Bootkit

原理:修改引导加载程序(GRUB、MBR、UEFI),在操作系统启动之前获得控制权

特点

在操作系统启动之前就已运行,可以修改内核加载过程

重装操作系统可能无法清除(如果修改的是 UEFI 固件)

检测极其困难

检测线索

1
2
3
4
5
6
7
8
9
10
11
12
# 检查 MBR 是否被修改
dd if=/dev/sda bs=512 count=1 | xxd | head -20
# 与已知良好的 MBR 对比

# 检查 GRUB 配置
cat /boot/grub/grub.cfg

# 检查 UEFI 启动项
efibootmgr -v

# 检查 Secure Boot 状态
mokutil --sb-state

1.5 固件 Rootkit(简要提及)

修改 BIOS/UEFI 固件、网卡固件、硬盘固件、BMC/IPMI 固件

极难检测和清除,通常需要硬件级别的取证

超出常规应急响应的范围,需要专业安全团队介入

二、用户态 Rootkit 检测

2.1 系统命令替换检测

使用包管理器验证(最可靠)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ========== Ubuntu/Debian ==========

# 检查核心工具包完整性
debsums coreutils # ls, cat, cp, mv, rm 等
debsums procps # ps, top, free, vmstat
debsums net-tools # netstat, ifconfig
debsums iproute2 # ss, ip
debsums findutils # find, xargs
debsums login # login, su
debsums openssh-server # sshd
debsums util-linux # mount, lsblk 等

# 一次性检查所有包(耗时较长)
debsums -c 2>/dev/null # 只输出被修改的文件

# ========== CentOS/RHEL ==========

rpm -Va # 验证所有已安装包
rpm -V coreutils # 检查核心工具
rpm -V procps-ng # 检查进程工具
rpm -V net-tools # 检查网络工具
rpm -V iproute # 检查 iproute2
rpm -V findutils # 检查 find
rpm -V openssh-server # 检查 sshd

手动 hash 验证

1
2
3
4
5
6
7
# 计算关键命令的 SHA256
sha256sum /bin/ls /bin/ps /bin/netstat /usr/bin/find /usr/bin/top \
/usr/bin/ss /usr/sbin/sshd /bin/login /usr/bin/who \
/usr/bin/w /usr/bin/last /usr/bin/lsof

# 与已知良好系统的 hash 对比
# 或与同版本操作系统镜像中的 hash 对比

2.2 对比 ps 与 /proc/ 的差异

如果 ps 被替换为恶意版本,它可能会过滤特定进程。但 /proc/ 由内核维护,用户态 Rootkit 无法修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 方法一:简单对比
echo "ps 报告的进程数:"
ps -eo pid --no-headers | wc -l
echo "/proc 中的进程数:"
ls -d /proc/[0-9]* | wc -l

# 方法二:找出差异
ps_pids=$(ps -eo pid --no-headers | tr -d ' ' | sort -n)
proc_pids=$(ls -d /proc/[0-9]* 2>/dev/null | sed 's|/proc/||' | sort -n)

echo "=== /proc 中有但 ps 未显示的进程(可能被 ps 隐藏)==="
diff <(echo "$ps_pids") <(echo "$proc_pids") | grep "^>"

# 方法三:对差异进程进行详细分析
for pid in $(comm -13 <(echo "$ps_pids") <(echo "$proc_pids")); do
echo "隐藏进程 PID $pid:"
echo " 命令行: $(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')"
echo " 执行文件: $(readlink /proc/$pid/exe 2>/dev/null)"
echo " 运行用户: $(stat -c %U /proc/$pid 2>/dev/null)"
done

⚠ 注意:如果使用了 LD_PRELOAD 劫持了 readdir(),那么 ls /proc/ 也可能被欺骗。此时需要使用 busybox 或直接系统调用

2.3 对比 ls 与 find 的差异

不同工具使用不同的系统调用遍历目录,对比可以发现被隐藏的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 对比 ls 和 find 的结果
ls_result=$(ls -la /tmp/ 2>/dev/null | awk '{print $NF}' | sort)
find_result=$(find /tmp/ -maxdepth 1 2>/dev/null | sed 's|/tmp/||' | sort)

echo "=== ls 与 find 结果差异 ==="
diff <(echo "$ls_result") <(echo "$find_result")

# 使用 Python 进行独立遍历
python3 -c "
import os
for f in sorted(os.listdir('/tmp/')):
print(f)
" > /tmp/py_listing.txt

ls -a /tmp/ | sort > /tmp/ls_listing.txt

diff /tmp/ls_listing.txt /tmp/py_listing.txt

2.4 使用静态编译工具

静态编译的 busybox 是用户态 Rootkit 检测的最佳武器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 验证 busybox 是静态编译的
file /path/to/busybox
# 应显示 "statically linked"

# 使用 busybox 进行全面检查
busybox ps aux # 进程列表
busybox ls -la /tmp/ # 文件列表
busybox ls -la /proc/ # /proc 遍历
busybox netstat -tlnp # 网络连接
busybox cat /etc/ld.so.preload # LD_PRELOAD 配置
busybox cat /etc/passwd # 用户列表
busybox find / -perm -4000 # SUID 文件

# 如果 busybox 的结果与系统命令的结果不同 → 存在用户态 Rootkit

三、内核态 Rootkit (LKM) 检测

3.1 基础检查:lsmod 与 /proc/modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看已加载的内核模块
lsmod

# 查看 /proc/modules(内核直接提供的模块列表)
cat /proc/modules

# 对比两者(正常情况下应该一致)
lsmod_list=$(lsmod | tail -n +2 | awk '{print $1}' | sort)
proc_list=$(cat /proc/modules | awk '{print $1}' | sort)

echo "=== lsmod 与 /proc/modules 差异 ==="
diff <(echo "$lsmod_list") <(echo "$proc_list")

# 注意:高级 LKM Rootkit 会同时从两者中隐藏自身
# 如 Diamorphine 会将自己从 modules 链表中摘除

3.2 modinfo 检查模块信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 检查每个加载的模块信息
lsmod | tail -n +2 | awk '{print $1}' | while read mod; do
info=$(modinfo "$mod" 2>/dev/null)

# 检查模块是否有正常的描述和作者
author=$(echo "$info" | grep "^author:" | head -1)
description=$(echo "$info" | grep "^description:" | head -1)
filename=$(echo "$info" | grep "^filename:" | head -1)

# 标记可疑模块
if [ -z "$author" ] || [ -z "$description" ]; then
echo "[!] 可疑模块: $mod (缺少 author 或 description)"
echo " $filename"
fi
done

# 检查模块是否带有有效签名(如果系统启用了模块签名验证)
for mod in $(lsmod | tail -n +2 | awk '{print $1}'); do
sig=$(modinfo -F sig_id "$mod" 2>/dev/null)
if [ -z "$sig" ]; then
echo "[?] 未签名模块: $mod"
fi
done

3.3 检查 /lib/modules/ 中的异常 .ko 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 当前内核版本
KVER=$(uname -r)

# 列出所有内核模块文件
find /lib/modules/$KVER/ -name "*.ko" -o -name "*.ko.xz" -o -name "*.ko.zst" | wc -l

# 检查不属于内核包的 .ko 文件
# Ubuntu/Debian
find /lib/modules/$KVER/ -name "*.ko*" | while read f; do
if ! dpkg -S "$f" >/dev/null 2>&1; then
echo "[!] 非包管理器安装的模块: $f"
modinfo "$f" 2>/dev/null | head -5
fi
done

# CentOS/RHEL
find /lib/modules/$KVER/ -name "*.ko*" | while read f; do
if ! rpm -qf "$f" >/dev/null 2>&1; then
echo "[!] 非包管理器安装的模块: $f"
fi
done

# 检查最近修改的模块文件
find /lib/modules/$KVER/ -name "*.ko*" -mtime -7 -ls

# 在非标准位置查找 .ko 文件
find /tmp /dev/shm /var/tmp /home -name "*.ko" 2>/dev/null

3.4 内核符号表检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# /proc/kallsyms 包含内核导出的所有符号
# 可以用来检测系统调用表是否被修改

# 查看系统调用表地址
grep "sys_call_table" /proc/kallsyms
# 输出示例:ffffffff82000300 R sys_call_table

# 查看关键系统调用的地址
grep -E "^[0-9a-f]+ T (sys_read|sys_write|sys_open|sys_getdents|sys_kill)" /proc/kallsyms

# 检查系统调用是否被 Hook
# 正常情况下,系统调用函数地址应该在内核代码段范围内
# 如果某个系统调用指向了内核模块地址范围,说明被 Hook

# 获取内核代码段范围
grep "_text\|_etext" /proc/kallsyms

# 检查所有系统调用地址是否在正常范围内
python3 << 'PYEOF'
import re

# 获取内核代码段范围
text_start = None
text_end = None
with open('/proc/kallsyms') as f:
for line in f:
parts = line.strip().split()
if len(parts) >= 3:
if parts[2] == '_text':
text_start = int(parts[0], 16)
elif parts[2] == '_etext':
text_end = int(parts[0], 16)

if text_start and text_end:
print(f"内核代码段: 0x{text_start:x} - 0x{text_end:x}")

# 检查可疑的系统调用(简化版)
with open('/proc/kallsyms') as f:
for line in f:
parts = line.strip().split()
if len(parts) >= 3 and parts[2].startswith('__x64_sys_'):
addr = int(parts[0], 16)
if addr < text_start or addr > text_end:
print(f"[!] 可疑系统调用: {parts[2]} @ 0x{addr:x} (在内核代码段之外)")
PYEOF

3.5 系统调用表完整性检测

使用内核模块进行系统调用表完整性检查的思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 方法一:使用 sysdig 检测系统调用异常
# sysdig 直接在内核层面捕获事件
sysdig -cl # 列出可用的 chisel
sysdig -c spy_users # 监控用户活动

# 方法二:使用 kprobes 跟踪系统调用
# 查看可用的 kprobe 事件
cat /sys/kernel/debug/tracing/available_filter_functions | grep sys_getdents

# 方法三:对比系统调用表与 System.map
# System.map 包含编译时的符号地址
# 如果运行时的地址与 System.map 不同,说明被 Hook

if [ -f /boot/System.map-$(uname -r) ]; then
echo "=== 对比 System.map 与运行时符号 ==="
grep " T sys_" /boot/System.map-$(uname -r) | head -20
grep " T sys_" /proc/kallsyms | head -20
# 对比两者的地址
fi

3.6 /sys/module/ 检查

/sys/module/ 目录也包含已加载模块的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 列出 /sys/module/ 中的所有模块
ls /sys/module/ | sort > /tmp/sysmod_list.txt

# 与 lsmod 对比
lsmod | tail -n +2 | awk '{print $1}' | sort > /tmp/lsmod_list.txt

# /sys/module 通常包含更多条目(包括内置模块和参数模块)
# 重点关注 lsmod 中没有但 /sys/module 中有的外部模块

# 检查每个模块的详细信息
for mod in $(ls /sys/module/); do
if [ -f /sys/module/$mod/refcnt ]; then
refcnt=$(cat /sys/module/$mod/refcnt 2>/dev/null)
# 引用计数为 -1 通常表示内置模块
if [ "$refcnt" != "-1" ] 2>/dev/null; then
# 检查是否在 lsmod 中
if ! lsmod | grep -q "^$mod "; then
echo "[!] 隐藏模块: $mod (在 /sys/module 中但不在 lsmod 中)"
fi
fi
fi
done

四、检测工具

4.1 chkrootkit

chkrootkit 是一个经典的 Rootkit 检测工具,通过检查已知 Rootkit 特征来发现感染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 安装
apt install chkrootkit # Ubuntu/Debian
yum install chkrootkit # CentOS/RHEL

# 基本使用
chkrootkit

# 指定检查路径(使用可信的 busybox 命令)
chkrootkit -p /path/to/trusted/binaries/

# 安静模式(只输出感染结果)
chkrootkit -q

# 指定检查特定项
chkrootkit chkproc # 检查隐藏进程
chkrootkit chkdirs # 检查隐藏目录
chkrootkit strings # 检查命令替换

输出解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ROOTDIR is '/'
Checking 'amd'... not found
Checking 'basename'... not infected
Checking 'biff'... not found
Checking 'chfn'... not infected
Checking 'chsh'... not infected
Checking 'cron'... not infected
Checking 'crontab'... not infected
Checking 'ifpromisc'... not infected
Checking 'login'... not infected
Checking 'ls'... not infected
Checking 'lsof'... not infected
Checking 'netstat'... not infected
Checking 'ps'... not infected
Checking 'sshd'... not infected

常见误报处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# chkrootkit 常见误报:
# 1. "Checking 'bindshell'... INFECTED (PORTS: 465)"
# → 465 端口是 SMTPS,正常服务,非 bind shell

# 2. "Checking 'lkm'... You have X process hidden for readdir command"
# → 短暂存在的内核线程可能导致误报

# 3. "Checking 'sniffer'... lo: not promisc and no packet sniffer sockets
# eth0: PACKET SNIFFER"
# → 如果运行了 tcpdump 或其他抓包工具,网卡处于混杂模式是正常的

# 验证方法:对可疑结果进行手动确认
# 例如检查 bindshell 报告的端口
ss -tlnp | grep 465
# 如果是已知服务(如 postfix),则为误报

4.2 rkhunter

rkhunter (Rootkit Hunter) 是功能更全面的 Rootkit 检测工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装
apt install rkhunter # Ubuntu/Debian
yum install rkhunter # CentOS/RHEL

# 更新数据库(首次使用前必须执行)
rkhunter --update
rkhunter --propupd

# 全面扫描
rkhunter --check

# 跳过交互确认
rkhunter --check --sk

# 只报告警告
rkhunter --check --rwo

# 查看日志
cat /var/log/rkhunter.log

rkhunter 检查项

检查类别 具体内容
系统命令 检查关键命令的文件属性和 hash
Rootkit 特征 已知 Rootkit 的文件和目录特征
后门检测 检测已知后门端口和程序
恶意软件检测 检查已知恶意软件特征
网络检查 检测混杂模式、可疑端口
文件属性 检查关键文件的权限和所有者
内核检查 检查可加载模块支持、内核符号

配置文件优化/etc/rkhunter.conf):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 常用配置调整

# 允许 root SSH 登录(如果这是你的策略)
ALLOW_SSH_ROOT_USER=yes

# 白名单已知的脚本替换
SCRIPTWHITELIST=/usr/bin/egrep
SCRIPTWHITELIST=/usr/bin/fgrep
SCRIPTWHITELIST=/usr/bin/which

# 允许隐藏目录(已知的合法目录)
ALLOWHIDDENDIR=/dev/.udev
ALLOWHIDDENDIR=/dev/.static

# 允许隐藏文件
ALLOWHIDDENFILE=/dev/.blkid.tab
ALLOWHIDDENFILE=/dev/.blkid.tab.old

# 启用自动更新
UPDATE_MIRRORS=1
MIRRORS_MODE=0
WEB_CMD=""

4.3 unhide

unhide 专门用于发现隐藏进程和隐藏端口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装
apt install unhide # Ubuntu/Debian
yum install unhide # CentOS/RHEL

# ========== 隐藏进程检测 ==========

# 使用多种技术检测隐藏进程
unhide proc # 对比 /proc 与 ps
unhide sys # 使用系统调用暴力枚举
unhide brute # 暴力枚举 PID
unhide quick # 快速检测

# 全面检测(推荐)
unhide-linux all

# ========== 隐藏端口检测 ==========

# 检测隐藏的 TCP/UDP 端口
unhide-tcp

unhide 工作原理

检测方法 原理
proc 遍历 /proc 并与 ps 输出对比
procall 更彻底的 /proc 遍历
procfs 检查 /proc 中的 stat/statm/status
sys 使用 kill(pid, 0) 系统调用逐一检查
brute 暴力枚举所有可能的 PID
reverse 反向检查:寻找 ps 显示但 /proc 中没有的

输出示例

1
2
3
4
5
6
7
8
9
10
[*] Searching for Hidden processes through comparison of results
of system calls, proc, dir and ps

[*] Searching for Hidden processes through /proc scanning

Found HIDDEN PID: 31337
Cmdline: "/usr/bin/.malware"
Executable: "/usr/bin/.malware"

[*] Found 1 hidden process(es)

4.4 OSSEC 简介

OSSEC 是一个开源的主机入侵检测系统 (HIDS),提供持续的 Rootkit 监控:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# OSSEC 的 Rootkit 检测功能:
# 1. rootcheck - 周期性 Rootkit 检测
# 2. syscheck - 文件完整性监控
# 3. 实时告警

# 配置 rootcheck(/var/ossec/etc/ossec.conf)
# <rootcheck>
# <rootkit_files>/var/ossec/etc/shared/rootkit_files.txt</rootkit_files>
# <rootkit_trojans>/var/ossec/etc/shared/rootkit_trojans.txt</rootkit_trojans>
# <system_audit>/var/ossec/etc/shared/system_audit_rcl.txt</system_audit>
# </rootcheck>

# 手动触发检测
/var/ossec/bin/rootcheck_control -u

# 查看检测结果
/var/ossec/bin/rootcheck_control -l

五、手动检测方法

5.1 内核 dmesg 检查模块加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看内核日志中的模块加载记录
dmesg | grep -iE "module|insmod|modprobe|loading"

# 查看最近的内核日志
dmesg -T | tail -100

# 搜索可疑的内核消息
dmesg | grep -iE "tainting|tainted|unsigned|out-of-tree"
# 如果模块未签名或来自内核树外,会有 "tainted" 标记

# 检查内核 taint 标志
cat /proc/sys/kernel/tainted
# 0 = 干净
# 非0 = 内核被 taint
# 1 = 加载了非 GPL 模块
# 4096 = 加载了未签名模块
# 8192 = 加载了外部模块

# journalctl 查看更多内核日志
journalctl -k | grep -iE "module|insmod"
journalctl -k --since "1 week ago"

5.2 对比进程数检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
# hidden_process_detector.sh - 多方法对比检测隐藏进程

echo "==============================="
echo " 隐藏进程检测"
echo "==============================="
echo ""

# 方法1: ps
ps_count=$(ps -eo pid --no-headers | wc -l)
echo "[1] ps 报告进程数: $ps_count"

# 方法2: /proc 目录
proc_count=$(ls -d /proc/[0-9]* 2>/dev/null | wc -l)
echo "[2] /proc 目录进程数: $proc_count"

# 方法3: 系统调用 (kill -0) 枚举
kill_count=0
max_pid=$(cat /proc/sys/kernel/pid_max)
# 限制范围避免太慢
check_max=$((max_pid < 65536 ? max_pid : 65536))
for ((pid=1; pid<=check_max; pid++)); do
kill -0 "$pid" 2>/dev/null && ((kill_count++))
done
echo "[3] kill -0 检测到的进程数: $kill_count"

# 方法4: /proc/*/status
status_count=$(cat /proc/[0-9]*/status 2>/dev/null | grep "^Pid:" | wc -l)
echo "[4] /proc/*/status 进程数: $status_count"

echo ""
echo "=== 差异分析 ==="
if [ "$ps_count" -ne "$proc_count" ]; then
echo "[!] ps 与 /proc 数量不一致 (差异: $((proc_count - ps_count)))"
fi
if [ "$kill_count" -gt "$proc_count" ]; then
echo "[!] kill 检测到更多进程 (差异: $((kill_count - proc_count)))"
echo " 可能存在被 /proc 隐藏的进程(内核态 Rootkit)"
fi

# 找出具体隐藏的 PID
echo ""
echo "=== 详细差异 ==="
for ((pid=1; pid<=check_max; pid++)); do
if kill -0 "$pid" 2>/dev/null; then
if ! [ -d "/proc/$pid" ]; then
echo "[!] PID $pid: kill -0 成功但 /proc/$pid 不存在 → 内核级隐藏"
fi
fi
done

5.3 文件系统异常检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查找隐藏文件和目录(以 . 开头的不常见文件)
find / -name ".*" -not -path "/proc/*" -not -path "/sys/*" \
-not -path "/run/*" -not -path "/dev/*" \
-newer /etc/passwd -ls 2>/dev/null

# 查找可疑的 .so 文件
find / -name "*.so" -not -path "/usr/*" -not -path "/lib/*" \
-not -path "/proc/*" -not -path "/sys/*" -ls 2>/dev/null

# 查找可疑的 .ko 文件
find / -name "*.ko" -not -path "/lib/modules/*" \
-not -path "/proc/*" -not -path "/sys/*" -ls 2>/dev/null

# 磁盘空间异常
# 如果 du 和 df 报告的使用量差异很大,可能有文件被隐藏
df_used=$(df / | tail -1 | awk '{print $3}')
du_used=$(du -sx / 2>/dev/null | awk '{print $1}')
echo "df 报告: ${df_used}K, du 报告: ${du_used}K"

六、综合检测流程

6.1 Rootkit 检测清单

步骤 检查内容 工具/命令 检测层级
1 系统命令完整性 debsums -c / rpm -Va 用户态
2 LD_PRELOAD 检查 busybox cat /etc/ld.so.preload 用户态
3 进程对比 ps vs /proc vs kill -0 用户态+内核态
4 目录对比 ls vs find vs python 用户态
5 网络连接对比 netstat vs /proc/net/tcp 用户态
6 内核模块检查 lsmod + /proc/modules + /sys/module 内核态
7 内核符号检查 /proc/kallsyms vs System.map 内核态
8 chkrootkit chkrootkit -q 自动化
9 rkhunter rkhunter –check –rwo 自动化
10 unhide unhide-linux all + unhide-tcp 自动化
11 dmesg 审查 dmesg | grep module 内核态
12 PAM 检查 debsums libpam-modules 用户态

6.2 综合检测脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/bin/bash
# rootkit_scanner.sh - Rootkit 综合检测脚本

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo "============================================"
echo " Linux Rootkit 综合检测"
echo " $(date)"
echo "============================================"
echo ""

ALERT_COUNT=0

alert() {
echo -e "${RED}[!] ALERT: $1${NC}"
((ALERT_COUNT++))
}

ok() {
echo -e "${GREEN}[+] OK: $1${NC}"
}

info() {
echo -e "${YELLOW}[*] $1${NC}"
}

# === 1. 系统命令完整性 ===
info "1. 检查系统命令完整性 ..."
if command -v debsums >/dev/null 2>&1; then
modified=$(debsums -c 2>/dev/null)
if [ -n "$modified" ]; then
alert "以下文件被修改: $modified"
else
ok "系统命令完整性正常"
fi
elif command -v rpm >/dev/null 2>&1; then
modified=$(rpm -Va 2>/dev/null | grep "^..5")
if [ -n "$modified" ]; then
alert "以下文件 hash 被修改:\n$modified"
else
ok "系统命令完整性正常"
fi
fi
echo ""

# === 2. LD_PRELOAD 检查 ===
info "2. 检查 LD_PRELOAD ..."
preload_content=$(python3 -c "
try:
print(open('/etc/ld.so.preload').read().strip())
except:
pass
" 2>/dev/null)
if [ -n "$preload_content" ]; then
alert "/etc/ld.so.preload 包含内容: $preload_content"
else
ok "/etc/ld.so.preload 为空或不存在"
fi

env_preload=$(grep -r "LD_PRELOAD" /proc/*/environ 2>/dev/null | head -5)
if [ -n "$env_preload" ]; then
alert "发现进程环境变量中的 LD_PRELOAD:\n$env_preload"
fi
echo ""

# === 3. 隐藏进程检测 ===
info "3. 检查隐藏进程 ..."
ps_count=$(ps -eo pid --no-headers | wc -l)
proc_count=$(ls -d /proc/[0-9]* 2>/dev/null | wc -l)
diff_count=$((proc_count - ps_count))
if [ ${diff_count#-} -gt 5 ]; then
alert "进程数差异较大 (ps=$ps_count, /proc=$proc_count, 差=$diff_count)"
else
ok "进程数基本一致 (ps=$ps_count, /proc=$proc_count)"
fi
echo ""

# === 4. 内核模块检查 ===
info "4. 检查内核模块 ..."
taint=$(cat /proc/sys/kernel/tainted)
if [ "$taint" != "0" ]; then
alert "内核已 taint (值=$taint),可能加载了外部模块"
else
ok "内核未 taint"
fi

# 检查未签名模块
unsigned=$(lsmod | tail -n +2 | awk '{print $1}' | while read mod; do
if ! modinfo -F sig_id "$mod" >/dev/null 2>&1; then
echo "$mod"
fi
done)
if [ -n "$unsigned" ]; then
info "未签名模块: $unsigned"
fi
echo ""

# === 5. PAM 检查 ===
info "5. 检查 PAM ..."
pam_suspicious=$(grep -rn "sufficient.*pam_permit" /etc/pam.d/ | grep "auth" 2>/dev/null)
if [ -n "$pam_suspicious" ]; then
alert "PAM 配置可疑:\n$pam_suspicious"
else
ok "PAM 配置未发现明显异常"
fi
echo ""

# === 6. 可疑文件 ===
info "6. 检查可疑文件 ..."
suspicious_so=$(find /tmp /dev/shm /var/tmp -name "*.so" 2>/dev/null)
if [ -n "$suspicious_so" ]; then
alert "在临时目录发现 .so 文件:\n$suspicious_so"
fi

suspicious_ko=$(find / -name "*.ko" -not -path "/lib/modules/*" -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null)
if [ -n "$suspicious_ko" ]; then
alert "在非标准位置发现 .ko 文件:\n$suspicious_ko"
fi
echo ""

# === 总结 ===
echo "============================================"
if [ $ALERT_COUNT -gt 0 ]; then
echo -e "${RED}检测完成: 发现 $ALERT_COUNT 个告警!${NC}"
else
echo -e "${GREEN}检测完成: 未发现明显异常${NC}"
fi
echo "============================================"

6.3 一句话总结

Rootkit 检测的核心原则:多源交叉验证,不信任任何单一工具的输出。用不同层级的工具获取同一信息,对比差异就是 Rootkit 的痕迹


上一章 目录 下一章
20-PAM后门 Linux应急响应 22-SUID后门