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 发行版上,还通过 libsystemd → liblzma 的链路成为 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.xz 和 good-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.c 中 openssh_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 软件包,该补丁引入了 libsystemd → liblzma 依赖关系。该二进制文件同样以 -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)在 TERM、LD_DEBUG、LD_PROFILE 未设置、LANG 已设置、终止开关变量 yolAbejyiejuvnup 显式不存在的环境下启动 sshd,argv[0] 精确为 /usr/sbin/sshd,同时满足全部五个 IFUNC 运行时门控条件。
sshd 配置文件(env/config/sshd_config)设置了 PubkeyAuthentication yes 和 PermitRootLogin prohibit-password,没有任何加固覆盖会阻断预认证 RSA 证书路径,而那正是后门钩取的唯一代码路径。
下载完整环境文件:env.zip
单独文件:
- env/Dockerfile
- env/docker-compose.yml
- env/config/entrypoint.sh
- env/config/patch_ed448.py
- env/config/sshd_config
启动环境:
验证该环境确为存在漏洞的目标:
# 容器处于运行状态且健康检查通过
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
单独文件:
分步操作¶
第一步。 确认目标正在监听:
第二步。 选择标记文件路径和每次运行的唯一随机数(nonce)。命令 echo $NONCE > $MARKER_PATH 不得超过 64 个字符(后门的命令缓冲区限制):
第三步。 确认标记文件尚不存在(干净基线):
docker exec cve-2024-3094-sshd sh -c 'ls -l '"$MARKER_PATH"' 2>&1'
# 期望输出: "No such file or directory"
第四步。 运行利用脚本:
客户端将以十六进制形式打印构造好的 RSA 证书,然后输出:
该 EOF 是后门触发的预期信号,后门在 system() 返回后立即断开连接。脚本退出时不做成功断言;确认工作在下一步通过独立带外渠道完成。
第五步。 通过独立的特权渠道读取标记文件——与利用工具的网络连接完全分离:
成功判定依据。 通过利用工具无法控制的特权 docker exec shell,执行三项独立检查来确认成功:
- 标记文件存在于预期路径(在第三步的基线检查中尚不存在)。
- 文件内容等于第二步选择的每次唯一随机数——排除了预先植入文件的可能性。
- 文件归属于
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 |
清理环境。 完成后,停止并删除容器及其网络:
4. 安全建议¶
修复方案¶
xz-utils 从未发布修复版 5.6.x,所有受影响的发行版均发出通告要求降级。正确做法是降级至 xz-utils 5.4.6(或发行版仓库中最新的 5.4.x 版本),并重新构建任何链接了 liblzma 的软件包。对于基于 Debian 的系统,应以原始 Debian 安全通告为权威参考。
降级后,可通过以下命令验证已安装的库版本:
缓解措施与检测指标¶
可用的指标和缓解措施如下:
- 终止开关: 在
sshd的环境中设置环境变量yolAbejyiejuvnup=Evjtgvsh5okmkAvj可阻止 IFUNC 钩子完成安装。这仅为应急临时措施,并不能从库中移除恶意代码。 - IFUNC 门控条件(任意一条满足即可阻断激活):若
TERM已设置、LD_DEBUG或LD_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_verify中RSA_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_breakpoint、check_return_address、RSA_public_decryptPLT 修补逻辑。