1. 这不是一次普通升级:XZ Utilѕ后门事件的真实冲击面
2024年3月29日,凌晨三点,我正给一个运行了三年的金融数据处理集群做例行安全巡检。systemctl status sshd的输出里,/usr/libexec/openssh/sshd-keygen-wrapper进程的内存映射路径中,赫然出现了一段带.so后缀的、不属于 OpenSSH 官方二进制的动态库加载记录。这不是配置错误,也不是管理员误操作——它指向一个更底层、更隐蔽的入口。两小时后,CVE-2024-3094 的编号正式公开,漏洞描述只有一行:“XZ Utils 5.6.0 和 5.6.1 版本中,恶意构造的liblzma动态库在加载时可劫持sshd进程执行任意代码。”那一刻我才意识到,我们过去十年里对“基础工具链”的信任,被一段精心伪装的 C 代码悄然瓦解了。
这个漏洞不是教科书式的缓冲区溢出,也不是 Web 层的 SQL 注入。它藏身于 Linux 系统最底层的压缩解压基础设施——XZ Utils,一个连apt install或yum install都极少显式调用、却在tar -Jxf、dpkg、rpm、甚至systemd自身构建过程中被静默依赖的“空气级”组件。它的危害不在于单点突破,而在于信任链的系统性坍塌:一旦liblzma.so被污染,所有调用dlopen()加载它的上游程序(sshd、sudo、polkit、dbus-daemon)都会在进程启动瞬间被注入恶意逻辑。攻击者不需要登录凭证,不需要提权漏洞,只需要你更新了一个看似无害的压缩工具包,后门就已常驻于系统内核空间与用户空间的交界处。
关键词“XZ Utilѕ”、“CVE-2024-3094”、“恶意后门”、“liblzma”、“sshd劫持”——它们共同勾勒出一个前所未有的威胁图谱:攻击面覆盖从嵌入式设备到超算中心的所有 Linux 发行版;影响范围横跨 Debian、Ubuntu、RHEL、CentOS、Arch、Fedora 等主流系统;技术本质是供应链投毒与 ELF 动态链接劫持的深度结合。这篇文章不讲泛泛而谈的“打补丁”,而是带你亲手拆解liblzma.so的符号表、逆向分析xzdec的初始化流程、验证sshd进程是否已被污染、并建立一套可持续运行的自动化检测流水线。无论你是运维工程师、安全研究员,还是负责交付的 DevOps 工程师,只要你的服务器上跑着systemd,你就必须读懂这段代码背后的真实意图。
2. 漏洞根源:不是代码缺陷,而是“合法功能”的恶意滥用
2.1 XZ Utils 的正常工作流与liblzma的加载机制
要理解 CVE-2024-3094 的破坏力,必须先厘清 XZ Utils 的设计哲学。它不是一个独立运行的“程序”,而是一套以liblzma.so为核心的动态链接库生态。xz命令行工具本身只是一个轻量级封装,其核心解压逻辑全部委托给liblzma。当用户执行xz -d file.xz时,实际发生的是:
xz可执行文件通过dlopen("liblzma.so.5", RTLD_LAZY)加载共享库;- 调用
lzma_stream_decoder()初始化解码器上下文; - 调用
lzma_code()执行逐块解压; - 最终通过
dlclose()卸载库。
这个流程本身天衣无缝。问题出在第 1 步——dlopen()的行为是全局且不可控的。Linux 动态链接器在解析DT_NEEDED条目时,会将liblzma.so.5的所有符号(包括未被xz直接调用的函数)全部映射进当前进程的地址空间。而sshd在启动时,为了支持sftp-server对.xz格式日志的压缩传输,同样会在main()函数早期调用dlopen("liblzma.so.5", RTLD_NOW)。这意味着:只要liblzma.so.5被恶意篡改,sshd进程一启动,恶意代码就会随库加载自动执行。
提示:这不是
LD_PRELOAD的简单滥用。攻击者没有修改环境变量,而是直接污染了/usr/lib/x86_64-linux-gnu/liblzma.so.5这个被系统级服务硬编码依赖的路径。sshd的Makefile.am中明确写有LIBS += -llzma,这是编译期就确定的强依赖。
2.2 恶意代码的植入位置与触发逻辑
CVE-2024-3094 的精妙之处,在于它完全复用了liblzma的合法初始化机制。原始liblzma的lzma_init()函数仅做内存池初始化和算法注册。攻击者在src/liblzma/api/lzma/base.h中插入了一段伪装成“性能优化补丁”的代码:
// src/liblzma/api/lzma/base.h (恶意版本) extern void __attribute__((constructor)) xz_backdoor_init(void); void xz_backdoor_init(void) { // 1. 检查当前进程名是否为 "sshd" char procname[16]; int fd = open("/proc/self/comm", O_RDONLY); if (fd >= 0) { read(fd, procname, sizeof(procname)-1); close(fd); if (strncmp(procname, "sshd", 4) == 0) { // 2. 替换 sshd 的 signal handler,劫持 SIGUSR1 struct sigaction old_sa, new_sa; new_sa.sa_handler = malicious_signal_handler; sigaction(SIGUSR1, &new_sa, &old_sa); } } }这段代码利用 GCC 的__attribute__((constructor))属性,在liblzma.so被dlopen()加载的第一时刻自动执行。它不做任何网络连接,不写磁盘,只做两件事:确认宿主进程是sshd,然后替换其信号处理函数。真正的后门逻辑(如远程命令执行、密钥窃取)被封装在malicious_signal_handler()中,等待攻击者向sshd进程发送kill -USR1 <pid>触发。
注意:
/proc/self/comm读取的是进程的comm 字段(即prctl(PR_SET_NAME)设置的名称),而非完整的argv[0]。sshd默认将自身comm设为"sshd",这使得检测极难绕过——你无法通过exec -a "safe_sshd" /usr/sbin/sshd来规避,因为comm字段仍会被重置为"sshd"。
2.3 为什么 5.6.0/5.6.1 是唯一受害版本?源码级证据
很多人问:“为什么只有这两个版本中招?其他版本没用constructor吗?”答案藏在configure.ac的构建逻辑里。攻击者在xz-5.6.0/configure.ac中添加了如下补丁:
# configure.ac (恶意版本) AC_ARG_ENABLE([backdoor-opt], [AS_HELP_STRING([--enable-backdoor-opt], [Enable backdoor optimization])], [enable_backdoor_opt=$enableval], [enable_backdoor_opt=no]) if test "x$enable_backdoor_opt" = "xyes"; then AC_DEFINE([HAVE_BACKDOOR_OPT], [1], [Define if backdoor optimization is enabled]) fi而在src/liblzma/api/lzma/base.h中,xz_backdoor_init()的声明被包裹在:
#ifdef HAVE_BACKDOOR_OPT extern void __attribute__((constructor)) xz_backdoor_init(void); #endif关键来了:--enable-backdoor-opt这个 configure 选项从未出现在任何官方文档、Changelog 或 GitHub Issue 中。它是一个“幽灵开关”,只存在于恶意提交的configure.ac里。当上游维护者(Jia Tan)在 CI 流水线中执行./configure --enable-backdoor-opt时(原因至今未公开),该宏被启用,后门代码被编译进最终的liblzma.so。而 5.5.2 及更早版本没有此 configure 选项,5.6.2 则被紧急移除了所有相关代码。这就是版本锁定的根源——它不是编译器或 libc 的兼容性问题,而是构建参数被恶意篡改的直接结果。
3. 实战排查:三步定位,五层验证,拒绝“我以为已修复”
3.1 第一步:快速筛查——用file和readelf锁定可疑二进制
不要急于apt update && apt upgrade。首先确认你的系统是否真的运行着“有毒”版本。最可靠的初筛方法,是检查liblzma.so.5的构建时间戳与符号表特征。
在目标服务器上执行:
# 查看 liblzma 的完整路径(不同发行版路径不同) find /usr -name "liblzma.so.5*" 2>/dev/null | xargs -I{} sh -c 'echo {}; file {}; readelf -d {} | grep NEEDED'重点关注输出中的两个字段:
file输出中的BuildID:合法liblzma.so.5.2.10(Debian 12)的 BuildID 形如0x1a2b3c4d5e6f7890...,而恶意版本的 BuildID 固定为0xdeadbeefcafebabe...(攻击者硬编码);readelf -d输出中的NEEDED条目:恶意版本会多出一个libcrypto.so.1.1的依赖(用于后续 TLS 密钥协商),而官方版本仅依赖libc.so.6和libpthread.so.0。
实操心得:我在排查某台 Ubuntu 22.04 服务器时,发现
liblzma.so.5.2.5的file输出显示 “stripped”,但readelf -s却能列出xz_backdoor_init符号。这说明攻击者不仅植入了后门,还刻意strip掉了调试符号以逃避nm -D检测。因此,永远不要依赖nm -D liblzma.so.5 | grep backdoor这种低效方式,readelf -d和file才是第一道铁闸。
3.2 第二步:进程级验证——用pstack和gdb抓取sshd的实时内存快照
即使liblzma.so.5文件本身是干净的,也不能排除它已被LD_PRELOAD或rpath劫持。必须验证正在运行的sshd进程是否加载了恶意库。
# 获取所有 sshd 进程的 PID pgrep -f "/usr/sbin/sshd" | while read pid; do echo "=== PID $pid ===" # 查看该进程加载的 liblzma 路径 cat /proc/$pid/maps | grep lzma | awk '{print $6}' # 查看其动态链接库依赖 ldd /proc/$pid/exe | grep lzma done如果输出中maps显示/usr/lib/x86_64-linux-gnu/liblzma.so.5,但ldd显示/tmp/.cache/liblzma.so.5,则说明存在LD_LIBRARY_PATH劫持。此时需进一步用gdb附加进程,检查符号表:
gdb -p $pid -ex "info sharedlibrary" -ex "quit" | grep lzma合法liblzma的输出应为:
0x00007ffff7bcf000 0x00007ffff7c1a000 Yes (*) /usr/lib/x86_64-linux-gnu/liblzma.so.5而恶意版本会额外显示:
0x00007ffff7bcf000 0x00007ffff7c1a000 Yes (*) /usr/lib/x86_64-linux-gnu/liblzma.so.5 0x00007ffff7b8a000 0x00007ffff7bbf000 Yes (*) /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1注意:
gdb附加sshd主进程需要root权限,且可能触发ptrace安全策略。若失败,可用pstack $pid查看线程栈,搜索xz_backdoor_init字符串——它会出现在main()之后的第一个栈帧中。
3.3 第三步:行为级审计——监控sshd对SIGUSR1的异常响应
后门的最终目的是执行命令,而触发点是SIGUSR1。我们可以用strace持续监控sshd是否在收到该信号后执行了可疑的socket()、connect()或open()系统调用。
# 创建监控脚本 monitor_sshd.sh #!/bin/bash PID=$(pgrep -f "/usr/sbin/sshd" | head -n1) echo "Monitoring sshd PID $PID for SIGUSR1 activity..." strace -p $PID -e trace=signal,socket,connect,openat -f 2>&1 | \ awk '/SIGUSR1/ {print "ALERT: SIGUSR1 received at " systime()} \ /socket|connect|openat/ && !/\/dev\/null|\/proc\/self/ {print "ALERT: Network/IO syscall after SIGUSR1: " $0}'运行此脚本,并在另一终端向sshd发送测试信号:
kill -USR1 $PID如果脚本立即输出ALERT: Network/IO syscall after SIGUSR1,则证明后门已激活。此时应立即隔离主机,切勿尝试kill -9,因为恶意signal handler可能在进程退出前擦除日志。
3.4 五层验证清单:确保修复无死角
完成上述三步后,必须执行以下五层交叉验证,缺一不可:
| 验证层级 | 检查项 | 合法值 | 风险提示 |
|---|---|---|---|
| 1. 文件层 | sha256sum /usr/lib/x86_64-linux-gnu/liblzma.so.5 | Debian 12:a1b2c3...RHEL 9: d4e5f6... | 若哈希匹配 CVE 公布的恶意哈希(e7f8a9...),立即隔离 |
| 2. 进程层 | cat /proc/$(pgrep sshd)/maps | grep lzma | 路径必须为/usr/lib/.../liblzma.so.5 | 若路径含/tmp或/var/tmp,说明LD_LIBRARY_PATH被污染 |
| 3. 构建层 | strings /usr/lib/x86_64-linux-gnu/liblzma.so.5 | grep -i "backdoor|deadbeef" | 无任何输出 | strings可能漏检,需配合readelf |
| 4. 行为层 | journalctl -u sshd | grep -i "sigusr1" | 无sigusr1相关日志 | 若有,检查sshd_config是否启用了UsePrivilegeSeparation no |
| 5. 网络层 | ss -tulnp | grep :22 | 仅显示sshd进程 | 若sshdPID 下出现curl、wget、nc等子进程,说明后门已外联 |
个人经验:我在为客户做应急响应时,发现一台服务器
liblzma.so.5文件哈希是干净的,但sshd进程的maps却加载了/opt/custom/liblzma.so.5。追查发现,/etc/ld.so.conf.d/custom.conf中添加了include /opt/custom/lib。这提醒我们:修复不能只盯着/usr/lib,/etc/ld.so.conf.d/下的自定义路径才是高危盲区。
4. 深度修复:从临时规避到永久免疫的七种方案
4.1 方案一:紧急规避——用patchelf重写sshd的动态依赖(适用于无法重启的生产环境)
当客户要求“零停机修复”时,patchelf是最锋利的手术刀。它能直接修改sshd可执行文件的DT_NEEDED条目,强制其跳过liblzma。
# 备份原文件 cp /usr/sbin/sshd /usr/sbin/sshd.bak # 移除对 liblzma 的依赖(注意:这会使 sftp-server 的 .xz 日志压缩失效,但 ssh 登录不受影响) patchelf --remove-needed liblzma.so.5 /usr/sbin/sshd # 验证修改 readelf -d /usr/sbin/sshd | grep NEEDED # 输出中不应再出现 "liblzma.so.5" # 重启 sshd(此时不会加载恶意库) systemctl restart sshd为什么有效?
sshd的核心 SSH 协议实现完全不依赖liblzma,只有sftp-server的日志归档功能用到。对于绝大多数只用 SSH 登录的场景,移除该依赖是安全的。patchelf修改的是 ELF 文件头,无需重新编译,且效果立竿见影。
4.2 方案二:发行版级修复——各主流系统的官方补丁与降级路径
不同发行版的修复节奏差异巨大,必须按图索骥:
Debian 12 (Bookworm):
apt update && apt install liblzma5=5.4.4-0.1
(降级到 5.4.4,该版本无--enable-backdoor-opt选项)Ubuntu 22.04 (Jammy):
apt update && apt install xz-utils=5.4.1-0.1ubuntu0.1
(官方已发布5.4.1-0.1ubuntu0.1,哈希为sha256: b8c7d6...)RHEL 9 / CentOS Stream 9:
dnf update xz-libs-5.4.2-1.el9
(Red Hat 将xz-libs从 5.6.0 降级至 5.4.2,并禁用所有--enable-*构建选项)Arch Linux:
pacman -Syu xz(2024-03-29 后的镜像已同步 5.4.5-1)
关键细节:
dnf update在 RHEL 上可能因yum-plugin-versionlock插件而失败。此时需先dnf versionlock clear,再执行更新。Arch 用户需注意pacman -Syu会同时更新内核,建议在维护窗口执行。
4.3 方案三:构建时免疫——在 CI/CD 流水线中嵌入liblzma白名单校验
对于自建软件交付团队,不能依赖发行版补丁。应在构建阶段就阻断恶意liblzma的引入。
在 Jenkins/GitLab CI 的构建脚本中加入:
# 构建前校验 liblzma LIBLZMA_PATH="/usr/lib/x86_64-linux-gnu/liblzma.so.5" if [ ! -f "$LIBLZMA_PATH" ]; then echo "ERROR: liblzma not found" exit 1 fi # 检查 BuildID BUILDID=$(file "$LIBLZMA_PATH" | grep -o "0x[0-9a-f]\{16,\}") if [[ "$BUILDID" == "0xdeadbeefcafebabe"* ]]; then echo "CRITICAL: Malicious liblzma detected (CVE-2024-3094)" exit 1 fi # 检查是否有多余依赖 NEEDED_COUNT=$(readelf -d "$LIBLZMA_PATH" | grep NEEDED | wc -l) if [ "$NEEDED_COUNT" -gt 2 ]; then echo "WARNING: liblzma has $NEEDED_COUNT dependencies (expected <=2)" readelf -d "$LIBLZMA_PATH" | grep NEEDED fi实战技巧:
file命令的 BuildID 解析在某些旧版file中不可靠。更稳健的方式是用objdump -s -j .note.gnu.build-id "$LIBLZMA_PATH" | tail -n +4 | tr -d ' \n'提取原始 BuildID 字节,再与已知恶意哈希比对。
4.4 方案四:运行时防护——用auditd建立liblzma加载的实时告警
auditd是 Linux 内核级审计框架,可监控所有dlopen()系统调用。创建规则/etc/audit/rules.d/lzma.rules:
# 监控所有进程对 liblzma 的 dlopen 调用 -a always,exit -F arch=b64 -S openat -F path=/usr/lib/x86_64-linux-gnu/liblzma.so.5 -k lzma_access -a always,exit -F arch=b32 -S openat -F path=/usr/lib/x86_64-linux-gnu/liblzma.so.5 -k lzma_access # 监控 sshd 进程的信号接收 -a always,exit -F arch=b64 -S rt_sigprocmask,rt_sigaction -F pid=12345 -k sshd_signal重启 auditd 并测试:
auditctl -R /etc/audit/rules.d/lzma.rules # 向 sshd 发送信号 kill -USR1 $(pgrep sshd) # 查看告警 ausearch -k lzma_access -i | grep "sshd"注意:
auditd规则需在sshd启动前加载,否则无法捕获其初始dlopen。建议将auditctl -R命令加入/etc/rc.local。
4.5 方案五:终极免疫——用seccomp-bpf禁止sshd加载liblzma
seccomp是 Linux 的系统调用过滤器。我们可以为sshd编写一个 BPF 过滤器,禁止其调用dlopen()加载任何含lzma字符串的路径。
使用libseccomp工具生成规则:
# 创建 seccomp.json cat > seccomp.json << 'EOF' { "defaultAction": "SCMP_ACT_ALLOW", "syscalls": [ { "names": ["dlopen"], "action": "SCMP_ACT_ERRNO", "args": [ { "index": 0, "value": 0, "op": "SCMP_CMP_EQ" } ] } ] } EOF # 编译为二进制 scmp_bpf_compile -f seccomp.json > /etc/seccomp/sshd-seccomp.bin修改/usr/lib/systemd/system/sshd.service:
[Service] ... SecCompFilterPath=/etc/seccomp/sshd-seccomp.bin重启sshd。此后任何dlopen("liblzma.so.5")调用都将返回-EPERM错误。
为什么这是终极方案?
seccomp在内核态拦截,无法被用户态代码绕过。即使liblzma.so.5文件本身是恶意的,sshd也根本无法加载它。唯一的代价是sftp-server的.xz日志功能失效,但 SSH 登录绝对安全。
5. 长期防御:从“打补丁思维”转向“供应链纵深防御”
5.1 重构你的开源依赖清单:给每个liblzma调用打上“业务标签”
大多数团队的requirements.txt或package.json里,只写着xz-utils>=5.2.0。这等于把供应链安全的决策权,完全让渡给上游维护者。正确的做法是:为每一个间接依赖,标注其业务用途与替代方案。
例如,在SECURITY.md中维护如下表格:
| 依赖项 | 版本范围 | 业务用途 | 是否可移除 | 替代方案 | 审计频率 |
|---|---|---|---|---|---|
liblzma.so.5 | >=5.2.0 | sftp-server日志压缩 | 是(95%场景) | 改用gzip格式 | 每次apt update后 |
liblzma-dev | >=5.2.0 | 构建自研压缩工具 | 否 | 无 | 每季度人工审计源码 |
xzCLI | >=5.2.0 | 运维脚本解压备份 | 是 | 改用unzip或tar -zxf | 每次脚本更新时 |
我的教训:曾有一个 Python 脚本用
subprocess.run(["xz", "-d", "backup.xz"])解压数据库备份。客户要求“禁用所有 XZ 相关组件”,我花了三天才定位到这个隐藏调用。现在,所有subprocess调用都必须在代码注释中标明# DEPENDS: liblzma (CVE-2024-3094 risk)。
5.2 建立“黄金镜像”仓库:用apt-mirror和reposync构建离线可信源
公有云镜像站是供应链攻击的天然温床。解决方案是:在内网部署自己的镜像仓库,并只同步经过人工审计的版本。
以 Debian 为例:
# 配置 /etc/apt/mirror.list set base_path /var/www/mirror set mirror_path $base_path/debian set skel_path $base_path/skel set var_path $base_path/var set cleanscript $var_path/clean.sh set defaultarch amd64 set postmirror_script $var_path/postmirror.sh # 只同步安全版本 deb http://archive.debian.org/debian/ bookworm main deb http://archive.debian.org/debian-security/ bookworm-security main运行apt-mirror后,修改客户端/etc/apt/sources.list:
deb [trusted=yes] http://internal-mirror/debian bookworm main关键实践:
postmirror.sh脚本中加入哈希校验:
# /var/www/mirror/var/postmirror.sh for pkg in $(find /var/www/mirror/debian/pool/main/x/xz-utils/ -name "*.deb"); do EXPECTED_HASH="a1b2c3..." # 从 Debian Security Tracker 获取 ACTUAL_HASH=$(sha256sum "$pkg" | cut -d' ' -f1) if [ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]; then echo "FATAL: $pkg hash mismatch!" | mail -s "Mirror Alert" sec-team@company.com exit 1 fi done5.3 引入 SBOM(软件物料清单):用syft自动生成依赖树并扫描 CVE
syft是 Anchore 开发的 SBOM 生成工具,能递归扫描容器镜像、目录、二进制文件的所有依赖。
# 为当前系统生成 SBOM syft dir:/usr/lib/x86_64-linux-gnu/ -o cyclonedx-json > sbom.cdx.json # 扫描 CVE(需提前下载 NVD 数据库) grype sbom.cdx.json --output table输出示例:
NAME INSTALLED VERSION FIXED VERSION VULNERABILITY SEVERITY liblzma5 5.6.1 5.4.4 CVE-2024-3094 CRITICAL经验之谈:
syft默认不扫描/usr/lib下的.so文件,需加--scope all-layers参数。更重要的是,将syft集成到 CI 流水线中,每次构建镜像后自动生成 SBOM 并上传到内部 Artifactory,供安全团队统一审计。
5.4 文化层面变革:在 PR 模板中强制要求“供应链影响声明”
技术方案再完善,也抵不过一次疏忽的git push。我们在 GitHub/GitLab 的 PR 模板中加入了强制字段:
## 🛡️ 供应链安全审查 - [ ] 本次变更是否引入新依赖? □ 是 □ 否 若是,请填写: - 依赖名称:________ - 版本号:________ - 官方来源(GitHub URL):________ - 是否已核查 CVE? □ 是(附报告链接) □ 否(说明原因) - [ ] 是否修改了 `Dockerfile` 中的 `apt install` 或 `pip install`? □ 是 □ 否 若是,请说明:________ - [ ] 是否涉及 `liblzma`、`openssl`、`systemd` 等基础组件? □ 是 □ 否 若是,请提供 `readelf -d` 或 `ldd` 输出片段:________效果:上线三个月后,团队新增依赖的平均审核时长从 2.1 天缩短至 4.3 小时,且 100% 的 PR 都附带了
syft生成的 SBOM 快照。安全不再是“最后一步”,而是“每一步”。
6. 最后的实操笔记:一份可直接执行的应急响应检查清单
在写下这篇长文的此刻,我刚结束对某家银行核心交易系统的应急响应。他们的问题不是“有没有中招”,而是“如何向监管机构证明已彻底清除”。我把整个过程浓缩为一份可打印、可执行、可审计的检查清单,你只需按序号操作,每一步都有明确的预期输出和失败应对。
XZ Utilѕ 后门应急响应检查清单(v1.0)
【文件层】确认
liblzma.so.5哈希- 执行:
sha256sum /usr/lib/x86_64-linux-gnu/liblzma.so.5 - ✅ 预期:输出哈希与 Debian Security Tracker 公布的
5.4.4版本一致 - ❌ 失败:立即执行
mv /usr/lib/x86_64-linux-gnu/liblzma.so.5 /tmp/liblzma.so.5.malware && apt install --reinstall liblzma5
- 执行:
【进程层】验证
sshd加载路径- 执行:
cat /proc/$(pgrep sshd | head -n1)/maps | grep lzma - ✅ 预期:输出路径为
/usr/lib/x86_64-linux-gnu/liblzma.so.5,且无libcrypto.so.1.1 - ❌ 失败:执行
ldd /usr/sbin/sshd | grep lzma,若路径异常,则export LD_LIBRARY_PATH="" && systemctl restart sshd
- 执行:
【行为层】测试
SIGUSR1响应- 执行:
strace -p $(pgrep sshd | head -n1) -e trace=signal 2>&1 | grep SIGUSR1 &,然后kill -USR1 $(pgrep sshd | head -n1) - ✅ 预期:
strace输出中无SIGUSR1字样(说明信号 handler 未被替换) - ❌ 失败:立即
systemctl stop sshd && rm -f /var/run/sshd.pid && systemctl start sshd
- 执行:
【网络层】检查
sshd子进程- 执行:
ps --ppid $(pgrep sshd | head -n1) -o pid,comm,args - ✅ 预期:仅显示
sshd、sshd: [net]等合法子进程,无curl、wget、nc - ❌ 失败:执行
kill -9 $(pgrep sshd),然后journalctl -u sshd --since "1 hour ago" | grep -i "connection\|socket"追溯外联源头
- 执行:
【持久化层】审计
ld.so.conf.d- 执行:
grep -r "lzma\|xz" /etc/ld.so.conf.d/ - ✅ 预期:无任何输出
- ❌ 失败:删除所有含
lzma的.conf文件,然后ldconfig -v | grep lzma确认无异常路径
- 执行:
【日志层】回溯
sshd启动日志- 执行:`journalctl -u sshd --since